|
1 | | -use std::path::PathBuf; |
| 1 | +use std::{ |
| 2 | + ffi::OsStr, |
| 3 | + num::NonZeroUsize, |
| 4 | + path::{Path, PathBuf}, |
| 5 | +}; |
2 | 6 |
|
3 | 7 | use itertools::Itertools; |
4 | 8 | use nu_ansi_term::{Color, Style}; |
|
47 | 51 | terminal, QueueableCommand, |
48 | 52 | }, |
49 | 53 | std::{ |
| 54 | + ffi::OsString, |
50 | 55 | fs::File, |
51 | 56 | io, |
52 | 57 | io::Result, |
@@ -225,6 +230,14 @@ struct BufferEditor { |
225 | 230 | temp_file: PathBuf, |
226 | 231 | } |
227 | 232 |
|
| 233 | +/// a position in a buffer. |
| 234 | +/// indexes are one-based (hence the use of non-zero integer types) |
| 235 | +#[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 236 | +pub(crate) struct EditorPosition { |
| 237 | + pub line: NonZeroUsize, |
| 238 | + pub col: NonZeroUsize, |
| 239 | +} |
| 240 | + |
228 | 241 | impl Drop for Reedline { |
229 | 242 | fn drop(&mut self) { |
230 | 243 | if self.cursor_shapes.is_some() { |
@@ -559,10 +572,6 @@ impl Reedline { |
559 | 572 | /// ``` |
560 | 573 | #[must_use] |
561 | 574 | pub fn with_buffer_editor(mut self, editor: Command, temp_file: PathBuf) -> Self { |
562 | | - let mut editor = editor; |
563 | | - if !editor.get_args().contains(&temp_file.as_os_str()) { |
564 | | - editor.arg(&temp_file); |
565 | | - } |
566 | 575 | self.buffer_editor = Some(BufferEditor { |
567 | 576 | command: editor, |
568 | 577 | temp_file, |
@@ -1359,7 +1368,15 @@ impl Reedline { |
1359 | 1368 | } |
1360 | 1369 | Ok(EventStatus::Handled) |
1361 | 1370 | } |
1362 | | - ReedlineEvent::OpenEditor => self.open_editor().map(|_| EventStatus::Handled), |
| 1371 | + ReedlineEvent::OpenEditor => { |
| 1372 | + if let Some(buffer_editor) = &self.buffer_editor { |
| 1373 | + let new_buffer = self.open_editor(buffer_editor)?; |
| 1374 | + self.editor |
| 1375 | + .set_buffer(new_buffer, UndoBehavior::CreateUndoPoint); |
| 1376 | + } |
| 1377 | + |
| 1378 | + Ok(EventStatus::Handled) |
| 1379 | + } |
1363 | 1380 | ReedlineEvent::Resize(width, height) => { |
1364 | 1381 | self.last_render_snapshot = None; |
1365 | 1382 | self.painter.handle_resize(width, height); |
@@ -1872,30 +1889,69 @@ impl Reedline { |
1872 | 1889 | } |
1873 | 1890 | } |
1874 | 1891 |
|
1875 | | - fn open_editor(&mut self) -> Result<()> { |
1876 | | - match &mut self.buffer_editor { |
1877 | | - Some(BufferEditor { |
1878 | | - ref mut command, |
1879 | | - ref temp_file, |
1880 | | - }) => { |
1881 | | - { |
1882 | | - let mut file = File::create(temp_file)?; |
1883 | | - write!(file, "{}", self.editor.get_buffer())?; |
1884 | | - } |
1885 | | - { |
1886 | | - let mut child = command.spawn()?; |
1887 | | - child.wait()?; |
1888 | | - } |
| 1892 | + /// opens the current buffer in the editor described in [`buffer_editor`] |
| 1893 | + /// returns the new buffer, after processing the changes via the editor |
| 1894 | + fn open_editor(&self, buffer_editor: &BufferEditor) -> Result<String> { |
| 1895 | + let mut command = self.render_buffer_editor_command(buffer_editor); |
| 1896 | + |
| 1897 | + // flush buffer to temp file, so it can be read by the editor |
| 1898 | + { |
| 1899 | + let mut file = File::create(&buffer_editor.temp_file)?; |
| 1900 | + write!(file, "{}", self.editor.get_buffer())?; |
| 1901 | + } |
1889 | 1902 |
|
1890 | | - let res = std::fs::read_to_string(temp_file)?; |
1891 | | - let res = res.trim_end().to_string(); |
| 1903 | + command.spawn()?.wait()?; |
1892 | 1904 |
|
1893 | | - self.editor.set_buffer(res, UndoBehavior::CreateUndoPoint); |
| 1905 | + // fetch contents of buffer after editor is done |
| 1906 | + let mut buffer = std::fs::read_to_string(&buffer_editor.temp_file)?; |
| 1907 | + let content_len = buffer.trim_end().len(); |
| 1908 | + buffer.truncate(content_len); |
1894 | 1909 |
|
1895 | | - Ok(()) |
1896 | | - } |
1897 | | - _ => Ok(()), |
| 1910 | + Ok(buffer) |
| 1911 | + } |
| 1912 | + |
| 1913 | + /// renders the template command described in [`buffer_editor`], if any |
| 1914 | + fn render_buffer_editor_command(&self, buffer_editor: &BufferEditor) -> Command { |
| 1915 | + let mut cmd = Command::new(buffer_editor.command.get_program()); |
| 1916 | + |
| 1917 | + // TODO: there are more efficient ways to do this. |
| 1918 | + const FILE: &str = "{file}"; |
| 1919 | + const LINE: &str = "{line}"; |
| 1920 | + const COL: &str = "{col}"; |
| 1921 | + |
| 1922 | + // kind of a wonky check, but it's enough to know |
| 1923 | + // that we have somewhere to stick that temp_file path in |
| 1924 | + let is_template = buffer_editor |
| 1925 | + .command |
| 1926 | + .get_args() |
| 1927 | + .map(OsStr::to_string_lossy) |
| 1928 | + .any(|arg| arg.contains(FILE)); |
| 1929 | + |
| 1930 | + if is_template { |
| 1931 | + let pos = self.editor.editor_position(); |
| 1932 | + |
| 1933 | + // TODO: there are more efficient ways to do this. |
| 1934 | + // e.g. "format args"-style structs |
| 1935 | + |
| 1936 | + let file = buffer_editor.temp_file.to_string_lossy(); |
| 1937 | + let line = pos.line.to_string(); |
| 1938 | + let col = pos.col.to_string(); |
| 1939 | + |
| 1940 | + let actual_args = buffer_editor |
| 1941 | + .command |
| 1942 | + .get_args() |
| 1943 | + .map(OsStr::to_string_lossy) |
| 1944 | + .map(|arg| arg.replace(FILE, &file)) |
| 1945 | + .map(|arg| arg.replace(LINE, &line)) |
| 1946 | + .map(|arg| arg.replace(COL, &col)); |
| 1947 | + |
| 1948 | + cmd.args(actual_args); |
| 1949 | + } else { |
| 1950 | + cmd.args(buffer_editor.command.get_args()); |
| 1951 | + cmd.arg(&buffer_editor.temp_file); |
1898 | 1952 | } |
| 1953 | + |
| 1954 | + cmd |
1899 | 1955 | } |
1900 | 1956 |
|
1901 | 1957 | /// Repaint logic for the history reverse search |
|
0 commit comments