Skip to main content

nu_command/platform/clip/
paste.rs

1use super::clipboard::provider::{Clipboard, create_clipboard};
2use crate::{
3    platform::clip::get_config::get_clip_config_with_plugin_fallback, try_json_str_to_value,
4};
5use nu_engine::command_prelude::*;
6use nu_protocol::{Config, shell_error::generic::GenericError};
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::Generic(GenericError::new(
43                "Clipboard is empty.",
44                "No text data is currently available in the clipboard.",
45                call.head,
46            )));
47        }
48
49        let default_raw = get_default_raw(&config, engine_state, stack);
50        if default_raw != call.has_flag(engine_state, stack, "raw")? {
51            return Ok(Value::string(text, call.head).into_pipeline_data());
52        }
53
54        let trimmed = text.trim_start();
55        if trimmed.starts_with('{') || trimmed.starts_with('[') || trimmed.starts_with('"') {
56            return match try_json_str_to_value(trimmed, call.head, false) {
57                Ok(value) => Ok(value.into_pipeline_data()),
58                Err(_) => Ok(Value::string(text, call.head).into_pipeline_data()),
59            };
60        }
61
62        Ok(Value::string(text, call.head).into_pipeline_data())
63    }
64
65    fn examples(&self) -> Vec<Example<'_>> {
66        vec![
67            Example {
68                example: "clip paste",
69                description: "Paste from clipboard and try to parse JSON.",
70                result: None,
71            },
72            Example {
73                example: "clip paste --raw",
74                description: "Paste raw clipboard text without JSON parsing.",
75                result: None,
76            },
77        ]
78    }
79}
80
81fn get_default_raw(config: &Config, engine_state: &EngineState, stack: &mut Stack) -> bool {
82    // new config
83    if config.clip.default_raw {
84        return true;
85    }
86    // legacy config
87    get_legacy_default_raw(get_clip_config_with_plugin_fallback(engine_state, stack).as_ref())
88}
89
90fn get_legacy_default_raw(value: Option<&Value>) -> bool {
91    match value {
92        Some(Value::Record { val, .. }) => {
93            if let Some(Value::Bool { val, .. }) = val
94                .get("DEFAULT_RAW")
95                .or_else(|| val.get("default_raw"))
96                .or_else(|| val.get("defaultRaw"))
97            {
98                return *val;
99            }
100            false
101        }
102        _ => false,
103    }
104}