iterm2/
lib.rs

1//!
2//! # iTerm2
3//! A Rust crate to allow easy access to the various escape codes in iTerm2.
4//!
5//! # Usage
6//!
7//! ```rust
8//! extern crate iterm2;
9//! use iterm2::*;
10//!
11//! clear_scrollback().unwrap();
12//! anchor("https://google.com", "google").unwrap();
13//! attention(AttentionType::Firework).unwrap();
14//! ```
15//!
16
17extern crate base64;
18
19use base64::encode;
20use std::io::stdout;
21use std::io::Write;
22
23pub type TerminalError = Result<(), std::io::Error>;
24
25/// The possible cusor shpaes
26#[derive(Debug, Clone, Copy)]
27pub enum CursorShape {
28    /// A solid vertical block
29    Block,
30    /// A thin vertical line
31    VerticalBar,
32    /// A thin horizonal line
33    Underline,
34}
35
36/// The possible types of attention actions
37#[derive(Debug, Clone, Copy)]
38pub enum AttentionType {
39    /// Start visual display
40    Yes,
41    /// Stop visual display
42    No,
43    /// Show fireworks
44    Firework,
45}
46
47/// Display a clickable link with custom display text
48pub fn anchor(url: &str, display_text: &str) -> TerminalError {
49    stdout().write_all(format!("\x1b]8;;{}\x07{}\x1b]8;;\x07", url, display_text).as_bytes())
50}
51
52/// Set the shape of the cursor
53pub fn set_cursor_shape(shape: CursorShape) -> TerminalError {
54    use CursorShape::*;
55    let shape_val = match shape {
56        Block => 0,
57        VerticalBar => 1,
58        Underline => 2,
59    };
60    stdout().write_all(format!("\x1b]1337;CursorShape={}\x07", shape_val).as_bytes())
61}
62
63/// Set a mark at the current line
64pub fn set_mark() -> TerminalError {
65    stdout().write_all(b"\x1b]1337;SetMark\x07")
66}
67
68/// Attempt to make iTerm the focused application
69pub fn steal_focus() -> TerminalError {
70    stdout().write_all(b"\x1b]1337;StealFocus\x07")
71}
72
73/// Clear the terminals scroll history
74pub fn clear_scrollback() -> TerminalError {
75    stdout().write_all(b"\x1b]1337;ClearScrollback\x07")
76}
77
78/// Sets the terminals current working directory
79pub fn set_current_dir(dir: &str) -> TerminalError {
80    stdout().write_all(format!("\x1b]1337;CurrentDir={}\x07", dir).as_bytes())
81}
82
83/// Send a system wide Growl notification
84pub fn send_notification(message: &str) -> TerminalError {
85    stdout().write_all(format!("\x1b]9;{}\x07", message).as_bytes())
86}
87
88/// Sets the clipboard
89// TODO: Add support for the other clipboards
90pub fn set_clipboard(text: &str) -> TerminalError {
91    stdout().write_all(b"\x1b]1337;CopyToClipboard=\x07")?;
92    stdout().write_all(text.as_bytes())?;
93    stdout().write_all(b"\n\x1b]1337;EndCopy\x07")
94}
95
96/// Sets the tab colors to a custom rgb value
97pub fn set_tab_colors(red: u8, green: u8, blue: u8) -> TerminalError {
98    stdout().write_all(format!("\x1b]6;1;bg;red;brightness;{}\x07", red).as_bytes())?;
99    stdout().write_all(format!("\x1b]6;1;bg;green;brightness;{}\x07", green).as_bytes())?;
100    stdout().write_all(format!("\x1b]6;1;bg;blue;brightness;{}\x07", blue).as_bytes())
101}
102
103/// Restore the tab colors to defaults
104pub fn restore_tab_colors() -> TerminalError {
105    stdout().write_all(b"\x1b]6;1;bg;*;default\x07")
106}
107
108/// Sets the terminal color palette
109///
110/// For details on the format, see "Change the color palette" at https://www.iterm2.com/documentation-escape-codes.html
111// TODO: Add better parameters
112pub fn set_color_palette(colors: &str) -> TerminalError {
113    stdout().write_all(format!("\x1b]1337;SetColors={}\x07", colors).as_bytes())
114}
115
116/// A builder for terminal annotations
117pub struct Annotation {
118    message: String,
119    length: Option<usize>,
120    xcoord: Option<usize>,
121    ycoord: Option<usize>,
122    hidden: bool,
123}
124
125impl Annotation {
126    /// Create a new annotation with given text
127    pub fn new(message: &str) -> Annotation {
128        Annotation {
129            message: message.to_owned(),
130            length: None,
131            xcoord: None,
132            ycoord: None,
133            hidden: false,
134        }
135    }
136
137    /// Set the length of the annotation
138    pub fn length(&mut self, length: usize) -> &mut Annotation {
139        self.length = Some(length);
140        self
141    }
142
143    /// Set the (x,y) coordinates of the annotation
144    pub fn coords(&mut self, x: usize, y: usize) -> &mut Annotation {
145        self.xcoord = Some(x);
146        self.ycoord = Some(y);
147        self
148    }
149
150    /// Set the annotation to be hidden
151    pub fn hidden(&mut self, hide: bool) -> &mut Annotation {
152        self.hidden = hide;
153        self
154    }
155
156    /// Display the annotation
157    pub fn show(&self) -> TerminalError {
158        let value = match self {
159            Annotation {
160                message: msg,
161                length: None,
162                xcoord: None,
163                ycoord: None,
164                ..
165            } => msg.to_owned(),
166            Annotation {
167                message: msg,
168                length: Some(len),
169                xcoord: None,
170                ycoord: None,
171                ..
172            } => format!("{}|{}", len, msg),
173            Annotation {
174                message: msg,
175                length: Some(len),
176                xcoord: Some(x),
177                ycoord: Some(y),
178                ..
179            } => format!("{}|{}|{}|{}", msg, len, x, y),
180            _ => panic!("Invalid parameters"), //TODO: Convert to custom error
181        };
182        let key = if self.hidden {
183            "AddHiddenAnnotation"
184        } else {
185            "AddAnnotation"
186        };
187        stdout().write_all(format!("\x1b]1337;{}={}\x07", key, value).as_bytes())
188    }
189}
190
191/// Set the visibility of the cursor guide
192pub fn cursor_guide(show: bool) -> TerminalError {
193    let value = if show { "yes" } else { "no" };
194    stdout().write_all(format!("\x1b]1337;HighlightCursorLine={}\x07", value).as_bytes())
195}
196
197/// Trigger a dock bounce notification or fireworks
198pub fn attention(kind: AttentionType) -> TerminalError {
199    use AttentionType::*;
200    let value = match kind {
201        Yes => "yes",
202        No => "no",
203        Firework => "fireworks",
204    };
205    stdout().write_all(format!("\x1b]1337;RequestAttention={}\x07", value).as_bytes())
206}
207
208/// Set the terminal background to the image at a path
209pub fn set_background_image(filename: &str) -> TerminalError {
210    let base64_filename = encode(filename.as_bytes());
211    stdout()
212        .write_all(format!("\x1b]1337;SetBackgroundImageFile={}\x07", base64_filename).as_bytes())
213}
214
215/// Gets the size of a cell in points as a floating point number
216///
217/// *Not yet implemented*
218//TODO: Implement
219#[allow(unused_variables)]
220pub fn get_cell_size(filename: &str) -> Result<(f32, f32), std::io::Error> {
221    unimplemented!()
222}
223
224/// Gets the value of a session variable
225///
226/// *Not yet implemented*
227//TODO: Implement
228#[allow(unused_variables)]
229pub fn get_terminal_variable(filename: &str) -> Result<String, std::io::Error> {
230    unimplemented!()
231}
232
233/// Download a file. Accepts raw file contents and option arguments
234///
235/// See the [iTerm2 docs](https://www.iterm2.com/documentation-images.html) for more information
236pub fn download_file(args: &[(&str, &str)], img_data: &[u8]) -> TerminalError {
237    let joined_args = args
238        .iter()
239        .map(|item| format!("{}={}", item.0, item.1))
240        .collect::<Vec<_>>()
241        .join(";");
242    stdout().write_all(format!("\x1b]1337;File={}:", joined_args).as_bytes())?;
243
244    let encoded_data = base64::encode(img_data);
245    stdout().write_all(&encoded_data.as_bytes())?;
246    stdout().write_all(b"\x07")
247}
248
249/// Configures touchbar key lables
250///
251/// Seethe [iTerm2 docs](https://www.iterm2.com/documentation-escape-codes.html) for more information
252pub fn set_touchbar_key_label(key: &str, value: &str) -> TerminalError {
253    stdout().write_all(format!("\x1b]1337;SetKeyLabel={}={}\x07", key, value).as_bytes())
254}
255
256/// Push the current key labels
257pub fn push_current_touchbar_labels() -> TerminalError {
258    stdout().write_all(b"\x1b]1337;PushKeyLabels\x07")
259}
260
261/// Pop the current key labels
262pub fn pop_current_touchbar_labels() -> TerminalError {
263    stdout().write_all(b"\x1b]1337;PopKeyLabels\x07")
264}
265
266/// Push a specific touchbar key label by name
267pub fn push_touchbar_label(label: &str) -> TerminalError {
268    stdout().write_all(format!("\x1b1337;PushKeyLabels={}\x07", label).as_bytes())
269}
270
271/// Pop a specific touchbar key label by name
272pub fn pop_touchbar_label(label: &str) -> TerminalError {
273    stdout().write_all(format!("\x1b1337;PopKeyLabels={}\x07", label).as_bytes())
274}
275
276/// Sets the terminals unicode version
277pub fn set_unicode_version(version: u8) -> TerminalError {
278    stdout().write_all(format!("\x1b1337;UnicodeVersion={}\x07", version).as_bytes())
279}