Skip to content

Commit

Permalink
feat: Add command and bindings to list changes from VCS in opened doc…
Browse files Browse the repository at this point in the history
…ument(s)
  • Loading branch information
poliorcetics committed Feb 4, 2023
1 parent 3f0b07f commit 184bc62
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 1 deletion.
4 changes: 3 additions & 1 deletion helix-term/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
pub(crate) mod dap;
pub(crate) mod lsp;
pub(crate) mod typed;
pub(crate) mod vcs;

pub use dap::*;
use helix_vcs::Hunk;
pub use lsp::*;
use tui::widgets::Row;
pub use typed::*;
pub use vcs::*;

use helix_core::{
char_idx_at_visual_offset, comment,
Expand Down Expand Up @@ -77,7 +79,6 @@ pub struct Context<'a> {
pub register: Option<char>,
pub count: Option<NonZeroUsize>,
pub editor: &'a mut Editor,

pub callback: Option<crate::compositor::Callback>,
pub on_next_key_callback: Option<Box<dyn FnOnce(&mut Context, KeyEvent)>>,
pub jobs: &'a mut Jobs,
Expand Down Expand Up @@ -286,6 +287,7 @@ impl MappableCommand {
workspace_symbol_picker, "Open workspace symbol picker",
diagnostics_picker, "Open diagnostic picker",
workspace_diagnostics_picker, "Open workspace diagnostic picker",
vcs_change_picker, "Open VCS changes picker",
last_picker, "Open last picker",
insert_at_line_start, "Insert at start of line",
insert_at_line_end, "Insert at end of line",
Expand Down
163 changes: 163 additions & 0 deletions helix-term/src/commands/vcs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::{ops::Range, path::PathBuf};

use helix_core::Selection;
use helix_lsp::Url;
use helix_view::theme::Style;
use helix_view::Document;
use tui::widgets::{Cell, Row};

use super::{align_view, push_jump, Align, Context, SourcePathFormat};
use crate::ui::{menu::Item, FilePicker};

/// Picker to list the VCS changes in the current document
pub fn vcs_change_picker(cx: &mut Context) {
let current_doc = doc!(cx.editor);

let mut changes = Vec::new();
add_changes_for_doc(&mut changes, current_doc);

let picker = vcs_picker(cx, changes, current_doc.url(), SourcePathFormat::Hide);
cx.push_layer(Box::new(picker));
}

// TODO: once both #5472 and #5645 are merged, do a workspace-changes picker

fn add_changes_for_doc(changes: &mut Vec<DiffBlock>, doc: &Document) {
let url = match doc.url() {
Some(url) => url,
None => return,
};

if let Some(diff_handle) = doc.diff_handle() {
let file_hunks = diff_handle.hunks();
if file_hunks.is_empty() {
return;
}

changes.reserve(file_hunks.len() as _);

for i in 0..file_hunks.len() {
let hunk = file_hunks.nth_hunk(i);

let ty = if hunk.is_pure_insertion() {
DiffType::PureInsertion
} else if hunk.is_pure_removal() {
DiffType::PureRemoval
} else {
DiffType::Delta
};

let range = hunk.after;
let line = doc
.text()
.get_line(range.start as usize)
.map_or_else(Default::default, |l| l.to_string());

changes.push(DiffBlock {
url: url.clone(),
ty,
line,
range,
});
}
}
}

struct DiffStyles {
insertion: Style,
removal: Style,
delta: Style,
}

struct DiffBlock {
url: Url,
ty: DiffType,
line: String,
range: Range<u32>,
}

#[derive(Debug, Copy, Clone)]
enum DiffType {
PureInsertion,
PureRemoval,
Delta,
}

impl DiffType {
fn as_str(&self) -> &'static str {
match self {
DiffType::PureInsertion => "[+]",
DiffType::PureRemoval => "[-]",
DiffType::Delta => "[~]",
}
}
}

impl Item for DiffBlock {
type Data = (DiffStyles, SourcePathFormat);

fn format(&self, (styles, source_path_format): &Self::Data) -> Row {
let path = match source_path_format {
SourcePathFormat::Hide => String::new(),
SourcePathFormat::Show => {
let path = helix_core::path::get_truncated_path(self.url.path());
format!("{}:", path.to_string_lossy())
}
};

let diff_style = match self.ty {
DiffType::PureInsertion => styles.insertion,
DiffType::PureRemoval => styles.removal,
DiffType::Delta => styles.delta,
};

Row::new([
Cell::from(path),
Cell::from(self.ty.as_str()).style(diff_style),
Cell::from(self.line.as_str()),
])
}
}

fn vcs_picker(
cx: &Context,
changes: Vec<DiffBlock>,
current_path: Option<Url>,
show_source_path: SourcePathFormat,
) -> FilePicker<DiffBlock> {
let styles = DiffStyles {
insertion: cx.editor.theme.get("diff.plus"),
removal: cx.editor.theme.get("diff.minus"),
delta: cx.editor.theme.get("diff.delta"),
};

FilePicker::new(
changes,
(styles, show_source_path),
move |cx, DiffBlock { url, range, .. }, action| {
if current_path.as_ref() == Some(url) {
let (view, doc) = current!(cx.editor);
push_jump(view, doc);
} else {
let path = url.to_file_path().unwrap();
cx.editor.open(&path, action).expect("editor.open failed");
}

let (view, doc) = current!(cx.editor);

let anchor = doc.text().line_to_char(range.start as usize);
let head = doc
.text()
.line_to_char(range.end as usize)
.saturating_sub(1);
doc.set_selection(view.id, Selection::single(anchor, head));
align_view(doc, view, Align::Center);
},
move |_editor, DiffBlock { url, range, .. }| {
Some((
PathBuf::from(url.path()).into(),
Some((range.start as usize, range.end.saturating_sub(1) as usize)),
))
},
)
}
1 change: 1 addition & 0 deletions helix-term/src/keymap/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ pub fn default() -> HashMap<Mode, Keymap> {
"S" => workspace_symbol_picker,
"d" => diagnostics_picker,
"D" => workspace_diagnostics_picker,
"v" => vcs_change_picker,
"a" => code_action,
"'" => last_picker,
"g" => { "Debug (experimental)" sticky=true
Expand Down

0 comments on commit 184bc62

Please sign in to comment.