Skip to main content

nu_command/platform/clip/
paste.rs

1use super::clipboard::provider::{Clipboard, create_clipboard};
2use crate::{
3    convert_json_string_to_value, platform::clip::get_config::get_clip_config_with_plugin_fallback,
4};
5use nu_engine::command_prelude::*;
6use nu_protocol::Config;
7
8#[derive(Clone)]
9pub struct ClipPaste;
10
11impl Command for ClipPaste {
12    fn name(&self) -> &str {
13        "clip paste"
14    }
15
16    fn signature(&self) -> Signature {
17        Signature::build(self.name())
18            .switch(
19                "raw",
20                "Disable JSON parsing. (act inverted if default_raw config is true).",
21                Some('r'),
22            )
23            .input_output_types(vec![(Type::Nothing, Type::Any)])
24            .category(Category::System)
25    }
26
27    fn description(&self) -> &str {
28        "Output the current clipboard content.
29  By default, it tries to parse clipboard content as JSON and outputs the corresponding Nushell value.
30  This behavior can be inverted using `$env.config.clip.default_raw = true`."
31    }
32    fn run(
33        &self,
34        engine_state: &EngineState,
35        stack: &mut Stack,
36        call: &Call,
37        _input: PipelineData,
38    ) -> Result<PipelineData, ShellError> {
39        let config = stack.get_config(engine_state);
40        let text = create_clipboard(&config, engine_state, stack).get_text()?;
41        if text.trim().is_empty() {
42            return Err(ShellError::GenericError {
43                error: "Clipboard is empty.".into(),
44                msg: "No text data is currently available in the clipboard.".into(),
45                span: Some(call.head),
46                help: None,
47                inner: vec![],
48            });
49        }
50
51        let default_raw = get_default_raw(&config, engine_state, stack);
52        if default_raw != call.has_flag(engine_state, stack, "raw")? {
53            return Ok(Value::string(text, call.head).into_pipeline_data());
54        }
55
56        let trimmed = text.trim_start();
57        if trimmed.starts_with('{') || trimmed.starts_with('[') || trimmed.starts_with('"') {
58            return match convert_json_string_to_value(trimmed, call.head) {
59                Ok(value) => Ok(value.into_pipeline_data()),
60                Err(_) => Ok(Value::string(text, call.head).into_pipeline_data()),
61            };
62        }
63
64        Ok(Value::string(text, call.head).into_pipeline_data())
65    }
66
67    fn examples(&self) -> Vec<Example<'_>> {
68        vec![
69            Example {
70                example: "clip paste",
71                description: "Paste from clipboard and try to parse JSON.",
72                result: None,
73            },
74            Example {
75                example: "clip paste --raw",
76                description: "Paste raw clipboard text without JSON parsing.",
77                result: None,
78            },
79        ]
80    }
81}
82
83fn get_default_raw(config: &Config, engine_state: &EngineState, stack: &mut Stack) -> bool {
84    // new config
85    if config.clip.default_raw {
86        return true;
87    }
88    // legacy config
89    get_legacy_default_raw(get_clip_config_with_plugin_fallback(engine_state, stack).as_ref())
90}
91
92fn get_legacy_default_raw(value: Option<&Value>) -> bool {
93    match value {
94        Some(Value::Record { val, .. }) => {
95            if let Some(Value::Bool { val, .. }) = val
96                .get("DEFAULT_RAW")
97                .or_else(|| val.get("default_raw"))
98                .or_else(|| val.get("defaultRaw"))
99            {
100                return *val;
101            }
102            false
103        }
104        _ => false,
105    }
106}