Skip to content

Commit 38320e2

Browse files
committed
Remove refactor view, add typed command and document type
1 parent c9e7ce5 commit 38320e2

File tree

7 files changed

+264
-429
lines changed

7 files changed

+264
-429
lines changed

helix-term/src/commands.rs

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use helix_core::{
3030
use helix_view::{
3131
apply_transaction,
3232
clipboard::ClipboardType,
33-
document::{FormatterError, Mode, SCRATCH_BUFFER_NAME},
33+
document::{FormatterError, Mode, REFACTOR_BUFFER_NAME, SCRATCH_BUFFER_NAME},
3434
editor::{Action, Motion},
3535
info::Info,
3636
input::KeyEvent,
@@ -2039,6 +2039,8 @@ fn global_refactor(cx: &mut Context) {
20392039
None
20402040
};
20412041

2042+
let encoding = Some(doc!(cx.editor).encoding());
2043+
20422044
let completions = search_completions(cx, Some(reg));
20432045
ui::regex_prompt(
20442046
cx,
@@ -2086,7 +2088,9 @@ fn global_refactor(cx: &mut Context) {
20862088
String::from(
20872089
matched
20882090
.strip_suffix("\r\n")
2089-
.or(matched.strip_suffix("\n"))
2091+
.or_else(|| {
2092+
matched.strip_suffix('\n')
2093+
})
20902094
.unwrap_or(matched),
20912095
),
20922096
))
@@ -2146,7 +2150,7 @@ fn global_refactor(cx: &mut Context) {
21462150
String::from(
21472151
matched
21482152
.strip_suffix("\r\n")
2149-
.or(matched.strip_suffix("\n"))
2153+
.or_else(|| matched.strip_suffix('\n'))
21502154
.unwrap_or(matched),
21512155
),
21522156
))
@@ -2169,32 +2173,43 @@ fn global_refactor(cx: &mut Context) {
21692173
let show_refactor = async move {
21702174
let all_matches: Vec<(PathBuf, usize, String)> =
21712175
UnboundedReceiverStream::new(all_matches_rx).collect().await;
2172-
let call: job::Callback = Callback::EditorCompositor(Box::new(
2173-
move |editor: &mut Editor, compositor: &mut Compositor| {
2174-
if all_matches.is_empty() {
2175-
editor.set_status("No matches found");
2176-
return;
2177-
}
2178-
let mut document_data: HashMap<PathBuf, Vec<(usize, String)>> = HashMap::new();
2179-
for (path, line, text) in all_matches {
2180-
if let Some(vec) = document_data.get_mut(&path) {
2181-
vec.push((line, text));
2182-
} else {
2183-
let v = Vec::from([(line, text)]);
2184-
document_data.insert(path, v);
2185-
}
2176+
let call: job::Callback = Callback::Editor(Box::new(move |editor: &mut Editor| {
2177+
if all_matches.is_empty() {
2178+
editor.set_status("No matches found");
2179+
return;
2180+
}
2181+
let mut matches: HashMap<PathBuf, Vec<(usize, String)>> = HashMap::new();
2182+
for (path, line, text) in all_matches {
2183+
if let Some(vec) = matches.get_mut(&path) {
2184+
vec.push((line, text));
2185+
} else {
2186+
let v = Vec::from([(line, text)]);
2187+
matches.insert(path, v);
21862188
}
2189+
}
21872190

2188-
let editor_view = compositor.find::<ui::EditorView>().unwrap();
2189-
let language_id = doc!(editor)
2190-
.language_id()
2191-
.and_then(|language_id| Some(String::from(language_id)));
2191+
let language_id = doc!(editor).language_id().map(String::from);
21922192

2193-
let re_view =
2194-
ui::RefactorView::new(document_data, editor, editor_view, language_id);
2195-
compositor.push(Box::new(re_view));
2196-
},
2197-
));
2193+
let mut doc_text = Rope::new();
2194+
let mut line_map = HashMap::new();
2195+
2196+
let mut count = 0;
2197+
for (key, value) in &matches {
2198+
for (line, text) in value {
2199+
doc_text.insert(doc_text.len_chars(), text);
2200+
doc_text.insert(doc_text.len_chars(), "\n");
2201+
line_map.insert((key.clone(), *line), count);
2202+
count += 1;
2203+
}
2204+
}
2205+
doc_text.split_off(doc_text.len_chars().saturating_sub(1));
2206+
let mut doc = Document::refactor(doc_text, matches, line_map, encoding);
2207+
if let Some(language_id) = language_id {
2208+
doc.set_language_by_language_id(&language_id, editor.syn_loader.clone())
2209+
.ok();
2210+
};
2211+
editor.new_file_from_document(Action::Replace, doc);
2212+
}));
21982213
Ok(call)
21992214
};
22002215
cx.jobs.callback(show_refactor);
@@ -2488,6 +2503,7 @@ fn buffer_picker(cx: &mut Context) {
24882503
path: Option<PathBuf>,
24892504
is_modified: bool,
24902505
is_current: bool,
2506+
is_refactor: bool,
24912507
}
24922508

24932509
impl ui::menu::Item for BufferMeta {
@@ -2498,9 +2514,13 @@ fn buffer_picker(cx: &mut Context) {
24982514
.path
24992515
.as_deref()
25002516
.map(helix_core::path::get_relative_path);
2501-
let path = match path.as_deref().and_then(Path::to_str) {
2502-
Some(path) => path,
2503-
None => SCRATCH_BUFFER_NAME,
2517+
let path = if self.is_refactor {
2518+
REFACTOR_BUFFER_NAME
2519+
} else {
2520+
match path.as_deref().and_then(Path::to_str) {
2521+
Some(path) => path,
2522+
None => SCRATCH_BUFFER_NAME,
2523+
}
25042524
};
25052525

25062526
let mut flags = Vec::new();
@@ -2525,6 +2545,13 @@ fn buffer_picker(cx: &mut Context) {
25252545
path: doc.path().cloned(),
25262546
is_modified: doc.is_modified(),
25272547
is_current: doc.id() == current,
2548+
is_refactor: matches!(
2549+
&doc.document_type,
2550+
helix_view::document::DocumentType::Refactor {
2551+
matches: _,
2552+
line_map: _
2553+
}
2554+
),
25282555
};
25292556

25302557
let picker = FilePicker::new(

helix-term/src/commands/typed.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,75 @@ fn run_shell_command(
18081808
Ok(())
18091809
}
18101810

1811+
fn apply_refactor(
1812+
cx: &mut compositor::Context,
1813+
_args: &[Cow<str>],
1814+
event: PromptEvent,
1815+
) -> anyhow::Result<()> {
1816+
if event != PromptEvent::Validate {
1817+
return Ok(());
1818+
}
1819+
1820+
let document_type = doc!(cx.editor).document_type.clone();
1821+
1822+
match &document_type {
1823+
helix_view::document::DocumentType::File => {}
1824+
helix_view::document::DocumentType::Refactor { matches, line_map } => {
1825+
let refactor_id = doc!(cx.editor).id();
1826+
let replace_text = doc!(cx.editor).text().clone();
1827+
let view = view!(cx.editor).clone();
1828+
let mut documents: usize = 0;
1829+
let mut count: usize = 0;
1830+
for (key, value) in matches {
1831+
let mut changes = Vec::<(usize, usize, String)>::new();
1832+
for (line, text) in value {
1833+
if let Some(re_line) = line_map.get(&(key.clone(), *line)) {
1834+
let mut replace = replace_text
1835+
.get_line(*re_line)
1836+
.unwrap_or_else(|| "\n".into())
1837+
.to_string()
1838+
.clone();
1839+
replace = replace.strip_suffix('\n').unwrap_or(&replace).to_string();
1840+
if text != &replace {
1841+
changes.push((*line, text.chars().count(), replace));
1842+
}
1843+
}
1844+
}
1845+
if !changes.is_empty() {
1846+
if let Some(doc) = cx
1847+
.editor
1848+
.open(key, Action::Load)
1849+
.ok()
1850+
.and_then(|id| cx.editor.document_mut(id))
1851+
{
1852+
documents += 1;
1853+
let mut applychanges = Vec::<(usize, usize, Option<Tendril>)>::new();
1854+
for (line, length, text) in changes {
1855+
if doc.text().len_lines() > line {
1856+
let start = doc.text().line_to_char(line);
1857+
applychanges.push((
1858+
start,
1859+
start + length,
1860+
Some(Tendril::from(text.to_string())),
1861+
));
1862+
count += 1;
1863+
}
1864+
}
1865+
let transaction = Transaction::change(doc.text(), applychanges.into_iter());
1866+
apply_transaction(&transaction, doc, &view);
1867+
}
1868+
}
1869+
}
1870+
cx.editor.set_status(format!(
1871+
"Refactored {} documents, {} lines changed.",
1872+
documents, count
1873+
));
1874+
cx.editor.close_document(refactor_id, true).ok();
1875+
}
1876+
}
1877+
Ok(())
1878+
}
1879+
18111880
pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
18121881
TypableCommand {
18131882
name: "quit",
@@ -2323,6 +2392,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
23232392
fun: run_shell_command,
23242393
completer: Some(completers::directory),
23252394
},
2395+
TypableCommand {
2396+
name: "apply-refactoring",
2397+
aliases: &["ar"],
2398+
doc: "Applies refactoring",
2399+
fun: apply_refactor,
2400+
completer: None,
2401+
},
23262402
];
23272403

23282404
pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =

helix-term/src/ui/editor.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ use tui::buffer::Buffer as Surface;
3232
use super::lsp::SignatureHelp;
3333
use super::statusline;
3434

35+
const REFACTOR_NAME_WIDTH: u16 = 20;
36+
3537
pub struct EditorView {
3638
pub keymaps: Keymaps,
3739
on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
@@ -79,7 +81,7 @@ impl EditorView {
7981
surface: &mut Surface,
8082
is_focused: bool,
8183
) {
82-
let inner = view.inner_area(doc);
84+
let mut inner = view.inner_area(doc);
8385
let area = view.area;
8486
let theme = &editor.theme;
8587
let config = editor.config();
@@ -149,10 +151,21 @@ impl EditorView {
149151
Box::new(highlights)
150152
};
151153

152-
Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights, &config);
153-
Self::render_gutter(editor, doc, view, view.area, surface, theme, is_focused);
154-
Self::render_rulers(editor, doc, view, inner, surface, theme);
154+
match &doc.document_type {
155+
helix_view::document::DocumentType::File => {
156+
Self::render_gutter(editor, doc, view, view.area, surface, theme, is_focused);
157+
Self::render_rulers(editor, doc, view, inner, surface, theme);
158+
}
159+
helix_view::document::DocumentType::Refactor {
160+
matches,
161+
line_map: _,
162+
} => {
163+
self.render_document_names(surface, &area, view.offset, matches);
164+
inner = area.clip_left(REFACTOR_NAME_WIDTH).clip_bottom(1);
165+
}
166+
}
155167

168+
Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights, &config);
156169
if is_focused {
157170
Self::render_focused_view_elements(view, doc, inner, theme, surface);
158171
}
@@ -210,6 +223,35 @@ impl EditorView {
210223
.for_each(|area| surface.set_style(area, ruler_theme))
211224
}
212225

226+
fn render_document_names(
227+
&self,
228+
surface: &mut Surface,
229+
area: &Rect,
230+
offset: helix_core::Position,
231+
matches: &std::collections::HashMap<PathBuf, Vec<(usize, String)>>,
232+
) {
233+
let mut start = 0;
234+
let mut count = 0;
235+
for (key, value) in matches {
236+
for (line, _) in value {
237+
if start >= offset.row && area.y + count < area.height {
238+
let text = key.display().to_string() + ":" + line.to_string().as_str();
239+
surface.set_string_truncated(
240+
area.x as u16,
241+
area.y + count as u16,
242+
&text,
243+
REFACTOR_NAME_WIDTH as usize,
244+
|_| Style::default().fg(helix_view::theme::Color::Magenta),
245+
true,
246+
true,
247+
);
248+
count += 1;
249+
}
250+
start += 1;
251+
}
252+
}
253+
}
254+
213255
/// Get syntax highlights for a document in a view represented by the first line
214256
/// and column (`offset`) and the last line. This is done instead of using a view
215257
/// directly to enable rendering syntax highlighted docs anywhere (eg. picker preview)

helix-term/src/ui/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ pub mod overlay;
99
mod picker;
1010
pub mod popup;
1111
mod prompt;
12-
mod refactor;
1312
mod spinner;
1413
mod statusline;
1514
mod text;
@@ -23,7 +22,6 @@ pub use menu::Menu;
2322
pub use picker::{DynamicPicker, FileLocation, FilePicker, Picker};
2423
pub use popup::Popup;
2524
pub use prompt::{Prompt, PromptEvent};
26-
pub use refactor::RefactorView;
2725
pub use spinner::{ProgressSpinners, Spinner};
2826
pub use text::Text;
2927

0 commit comments

Comments
 (0)