Skip to content

Commit

Permalink
Remove refactor view, add typed command and document type
Browse files Browse the repository at this point in the history
  • Loading branch information
pathwave committed Dec 17, 2022
1 parent c9e7ce5 commit 38320e2
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 429 deletions.
85 changes: 56 additions & 29 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use helix_core::{
use helix_view::{
apply_transaction,
clipboard::ClipboardType,
document::{FormatterError, Mode, SCRATCH_BUFFER_NAME},
document::{FormatterError, Mode, REFACTOR_BUFFER_NAME, SCRATCH_BUFFER_NAME},
editor::{Action, Motion},
info::Info,
input::KeyEvent,
Expand Down Expand Up @@ -2039,6 +2039,8 @@ fn global_refactor(cx: &mut Context) {
None
};

let encoding = Some(doc!(cx.editor).encoding());

let completions = search_completions(cx, Some(reg));
ui::regex_prompt(
cx,
Expand Down Expand Up @@ -2086,7 +2088,9 @@ fn global_refactor(cx: &mut Context) {
String::from(
matched
.strip_suffix("\r\n")
.or(matched.strip_suffix("\n"))
.or_else(|| {
matched.strip_suffix('\n')
})
.unwrap_or(matched),
),
))
Expand Down Expand Up @@ -2146,7 +2150,7 @@ fn global_refactor(cx: &mut Context) {
String::from(
matched
.strip_suffix("\r\n")
.or(matched.strip_suffix("\n"))
.or_else(|| matched.strip_suffix('\n'))
.unwrap_or(matched),
),
))
Expand All @@ -2169,32 +2173,43 @@ fn global_refactor(cx: &mut Context) {
let show_refactor = async move {
let all_matches: Vec<(PathBuf, usize, String)> =
UnboundedReceiverStream::new(all_matches_rx).collect().await;
let call: job::Callback = Callback::EditorCompositor(Box::new(
move |editor: &mut Editor, compositor: &mut Compositor| {
if all_matches.is_empty() {
editor.set_status("No matches found");
return;
}
let mut document_data: HashMap<PathBuf, Vec<(usize, String)>> = HashMap::new();
for (path, line, text) in all_matches {
if let Some(vec) = document_data.get_mut(&path) {
vec.push((line, text));
} else {
let v = Vec::from([(line, text)]);
document_data.insert(path, v);
}
let call: job::Callback = Callback::Editor(Box::new(move |editor: &mut Editor| {
if all_matches.is_empty() {
editor.set_status("No matches found");
return;
}
let mut matches: HashMap<PathBuf, Vec<(usize, String)>> = HashMap::new();
for (path, line, text) in all_matches {
if let Some(vec) = matches.get_mut(&path) {
vec.push((line, text));
} else {
let v = Vec::from([(line, text)]);
matches.insert(path, v);
}
}

let editor_view = compositor.find::<ui::EditorView>().unwrap();
let language_id = doc!(editor)
.language_id()
.and_then(|language_id| Some(String::from(language_id)));
let language_id = doc!(editor).language_id().map(String::from);

let re_view =
ui::RefactorView::new(document_data, editor, editor_view, language_id);
compositor.push(Box::new(re_view));
},
));
let mut doc_text = Rope::new();
let mut line_map = HashMap::new();

let mut count = 0;
for (key, value) in &matches {
for (line, text) in value {
doc_text.insert(doc_text.len_chars(), text);
doc_text.insert(doc_text.len_chars(), "\n");
line_map.insert((key.clone(), *line), count);
count += 1;
}
}
doc_text.split_off(doc_text.len_chars().saturating_sub(1));
let mut doc = Document::refactor(doc_text, matches, line_map, encoding);
if let Some(language_id) = language_id {
doc.set_language_by_language_id(&language_id, editor.syn_loader.clone())
.ok();
};
editor.new_file_from_document(Action::Replace, doc);
}));
Ok(call)
};
cx.jobs.callback(show_refactor);
Expand Down Expand Up @@ -2488,6 +2503,7 @@ fn buffer_picker(cx: &mut Context) {
path: Option<PathBuf>,
is_modified: bool,
is_current: bool,
is_refactor: bool,
}

impl ui::menu::Item for BufferMeta {
Expand All @@ -2498,9 +2514,13 @@ fn buffer_picker(cx: &mut Context) {
.path
.as_deref()
.map(helix_core::path::get_relative_path);
let path = match path.as_deref().and_then(Path::to_str) {
Some(path) => path,
None => SCRATCH_BUFFER_NAME,
let path = if self.is_refactor {
REFACTOR_BUFFER_NAME
} else {
match path.as_deref().and_then(Path::to_str) {
Some(path) => path,
None => SCRATCH_BUFFER_NAME,
}
};

let mut flags = Vec::new();
Expand All @@ -2525,6 +2545,13 @@ fn buffer_picker(cx: &mut Context) {
path: doc.path().cloned(),
is_modified: doc.is_modified(),
is_current: doc.id() == current,
is_refactor: matches!(
&doc.document_type,
helix_view::document::DocumentType::Refactor {
matches: _,
line_map: _
}
),
};

let picker = FilePicker::new(
Expand Down
76 changes: 76 additions & 0 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,75 @@ fn run_shell_command(
Ok(())
}

fn apply_refactor(
cx: &mut compositor::Context,
_args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}

let document_type = doc!(cx.editor).document_type.clone();

match &document_type {
helix_view::document::DocumentType::File => {}
helix_view::document::DocumentType::Refactor { matches, line_map } => {
let refactor_id = doc!(cx.editor).id();
let replace_text = doc!(cx.editor).text().clone();
let view = view!(cx.editor).clone();
let mut documents: usize = 0;
let mut count: usize = 0;
for (key, value) in matches {
let mut changes = Vec::<(usize, usize, String)>::new();
for (line, text) in value {
if let Some(re_line) = line_map.get(&(key.clone(), *line)) {
let mut replace = replace_text
.get_line(*re_line)
.unwrap_or_else(|| "\n".into())
.to_string()
.clone();
replace = replace.strip_suffix('\n').unwrap_or(&replace).to_string();
if text != &replace {
changes.push((*line, text.chars().count(), replace));
}
}
}
if !changes.is_empty() {
if let Some(doc) = cx
.editor
.open(key, Action::Load)
.ok()
.and_then(|id| cx.editor.document_mut(id))
{
documents += 1;
let mut applychanges = Vec::<(usize, usize, Option<Tendril>)>::new();
for (line, length, text) in changes {
if doc.text().len_lines() > line {
let start = doc.text().line_to_char(line);
applychanges.push((
start,
start + length,
Some(Tendril::from(text.to_string())),
));
count += 1;
}
}
let transaction = Transaction::change(doc.text(), applychanges.into_iter());
apply_transaction(&transaction, doc, &view);
}
}
}
cx.editor.set_status(format!(
"Refactored {} documents, {} lines changed.",
documents, count
));
cx.editor.close_document(refactor_id, true).ok();
}
}
Ok(())
}

pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "quit",
Expand Down Expand Up @@ -2323,6 +2392,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
fun: run_shell_command,
completer: Some(completers::directory),
},
TypableCommand {
name: "apply-refactoring",
aliases: &["ar"],
doc: "Applies refactoring",
fun: apply_refactor,
completer: None,
},
];

pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
Expand Down
50 changes: 46 additions & 4 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ use tui::buffer::Buffer as Surface;
use super::lsp::SignatureHelp;
use super::statusline;

const REFACTOR_NAME_WIDTH: u16 = 20;

pub struct EditorView {
pub keymaps: Keymaps,
on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
Expand Down Expand Up @@ -79,7 +81,7 @@ impl EditorView {
surface: &mut Surface,
is_focused: bool,
) {
let inner = view.inner_area(doc);
let mut inner = view.inner_area(doc);
let area = view.area;
let theme = &editor.theme;
let config = editor.config();
Expand Down Expand Up @@ -149,10 +151,21 @@ impl EditorView {
Box::new(highlights)
};

Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights, &config);
Self::render_gutter(editor, doc, view, view.area, surface, theme, is_focused);
Self::render_rulers(editor, doc, view, inner, surface, theme);
match &doc.document_type {
helix_view::document::DocumentType::File => {
Self::render_gutter(editor, doc, view, view.area, surface, theme, is_focused);
Self::render_rulers(editor, doc, view, inner, surface, theme);
}
helix_view::document::DocumentType::Refactor {
matches,
line_map: _,
} => {
self.render_document_names(surface, &area, view.offset, matches);
inner = area.clip_left(REFACTOR_NAME_WIDTH).clip_bottom(1);
}
}

Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights, &config);
if is_focused {
Self::render_focused_view_elements(view, doc, inner, theme, surface);
}
Expand Down Expand Up @@ -210,6 +223,35 @@ impl EditorView {
.for_each(|area| surface.set_style(area, ruler_theme))
}

fn render_document_names(
&self,
surface: &mut Surface,
area: &Rect,
offset: helix_core::Position,
matches: &std::collections::HashMap<PathBuf, Vec<(usize, String)>>,
) {
let mut start = 0;
let mut count = 0;
for (key, value) in matches {
for (line, _) in value {
if start >= offset.row && area.y + count < area.height {
let text = key.display().to_string() + ":" + line.to_string().as_str();
surface.set_string_truncated(
area.x as u16,
area.y + count as u16,
&text,
REFACTOR_NAME_WIDTH as usize,
|_| Style::default().fg(helix_view::theme::Color::Magenta),
true,
true,
);
count += 1;
}
start += 1;
}
}
}

/// Get syntax highlights for a document in a view represented by the first line
/// and column (`offset`) and the last line. This is done instead of using a view
/// directly to enable rendering syntax highlighted docs anywhere (eg. picker preview)
Expand Down
2 changes: 0 additions & 2 deletions helix-term/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ pub mod overlay;
mod picker;
pub mod popup;
mod prompt;
mod refactor;
mod spinner;
mod statusline;
mod text;
Expand All @@ -23,7 +22,6 @@ pub use menu::Menu;
pub use picker::{DynamicPicker, FileLocation, FilePicker, Picker};
pub use popup::Popup;
pub use prompt::{Prompt, PromptEvent};
pub use refactor::RefactorView;
pub use spinner::{ProgressSpinners, Spinner};
pub use text::Text;

Expand Down
Loading

0 comments on commit 38320e2

Please sign in to comment.