Skip to content

Commit f6e653b

Browse files
committed
take template as command, fetch pos
1 parent 0080077 commit f6e653b

2 files changed

Lines changed: 113 additions & 26 deletions

File tree

src/core_editor/editor.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use super::{edit_stack::EditStack, Clipboard, ClipboardMode, LineBuffer};
22
#[cfg(feature = "system_clipboard")]
33
use crate::core_editor::get_system_clipboard;
4+
use crate::engine::EditorPosition;
45
use crate::enums::{EditType, TextObject, TextObjectScope, TextObjectType, UndoBehavior};
56
use crate::prompt::{PromptEditMode, PromptViMode};
67
use crate::{core_editor::get_local_clipboard, EditCommand};
78
use std::cmp::{max, min};
9+
use std::num::NonZeroUsize;
810
use std::ops::{DerefMut, Range};
911

1012
/// Stateful editor executing changes to the underlying [`LineBuffer`]
@@ -1112,6 +1114,35 @@ impl Editor {
11121114
self.copy_range(around_range);
11131115
}
11141116
}
1117+
1118+
pub(crate) fn editor_position(&self) -> EditorPosition {
1119+
// TODO: this impl can probably be faster (caching newline locations?)
1120+
1121+
let buffer = self.get_buffer();
1122+
1123+
let content_len = self.insertion_point().min(buffer.len());
1124+
let content_until_cursor = &buffer[..content_len];
1125+
1126+
// impl note: see `core_editor::line_buffer::insert_newline`:
1127+
// we don't expect any other char sequence for linebreak.
1128+
let newline = '\n';
1129+
1130+
let line = {
1131+
let line_one_based = content_until_cursor.matches(newline).count() + 1;
1132+
NonZeroUsize::new(line_one_based).unwrap()
1133+
};
1134+
1135+
let col = {
1136+
let col_one_based = if let Some(newline_pos) = content_until_cursor.rfind(newline) {
1137+
content_until_cursor[newline_pos + 1..].chars().count() + 1
1138+
} else {
1139+
content_until_cursor.chars().count() + 1
1140+
};
1141+
NonZeroUsize::new(col_one_based).unwrap()
1142+
};
1143+
1144+
EditorPosition { line, col }
1145+
}
11151146
}
11161147

11171148
fn insert_clipboard_content_before(line_buffer: &mut LineBuffer, clipboard: &mut dyn Clipboard) {

src/engine.rs

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use std::path::PathBuf;
1+
use std::{
2+
ffi::OsStr,
3+
num::NonZeroUsize,
4+
path::{Path, PathBuf},
5+
};
26

37
use itertools::Itertools;
48
use nu_ansi_term::{Color, Style};
@@ -47,6 +51,7 @@ use {
4751
terminal, QueueableCommand,
4852
},
4953
std::{
54+
ffi::OsString,
5055
fs::File,
5156
io,
5257
io::Result,
@@ -225,6 +230,14 @@ struct BufferEditor {
225230
temp_file: PathBuf,
226231
}
227232

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+
228241
impl Drop for Reedline {
229242
fn drop(&mut self) {
230243
if self.cursor_shapes.is_some() {
@@ -559,10 +572,6 @@ impl Reedline {
559572
/// ```
560573
#[must_use]
561574
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-
}
566575
self.buffer_editor = Some(BufferEditor {
567576
command: editor,
568577
temp_file,
@@ -1359,7 +1368,15 @@ impl Reedline {
13591368
}
13601369
Ok(EventStatus::Handled)
13611370
}
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+
}
13631380
ReedlineEvent::Resize(width, height) => {
13641381
self.last_render_snapshot = None;
13651382
self.painter.handle_resize(width, height);
@@ -1872,30 +1889,69 @@ impl Reedline {
18721889
}
18731890
}
18741891

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+
}
18891902

1890-
let res = std::fs::read_to_string(temp_file)?;
1891-
let res = res.trim_end().to_string();
1903+
command.spawn()?.wait()?;
18921904

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);
18941909

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);
18981952
}
1953+
1954+
cmd
18991955
}
19001956

19011957
/// Repaint logic for the history reverse search

0 commit comments

Comments
 (0)