-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add command and bindings to list changes from VCS in opened doc…
…ument(s)
- Loading branch information
1 parent
d9b8dba
commit 606606f
Showing
3 changed files
with
190 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
use std::{ops::Range, path::PathBuf}; | ||
|
||
use tui::text::{Span, Spans}; | ||
|
||
use helix_core::Selection; | ||
use helix_lsp::Url; | ||
use helix_view::theme::Style; | ||
use helix_view::Document; | ||
|
||
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)); | ||
} | ||
|
||
/// Picker to list the VCS changes in all opened documents | ||
pub fn workspace_vcs_change_picker(cx: &mut Context) { | ||
let current_doc = doc!(cx.editor); | ||
let current_url = current_doc.url(); | ||
|
||
let mut changes = Vec::new(); | ||
// First add the changes for the current document, if any | ||
add_changes_for_doc(&mut changes, current_doc); | ||
|
||
// Then the changes for the others documents | ||
for doc in cx.editor.documents() { | ||
if doc.id() == current_doc.id() { | ||
continue; | ||
} | ||
add_changes_for_doc(&mut changes, doc); | ||
} | ||
|
||
let picker = vcs_picker(cx, changes, current_url, SourcePathFormat::Show); | ||
cx.push_layer(Box::new(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 label(&self, (styles, source_path_format): &Self::Data) -> Spans { | ||
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, | ||
}; | ||
|
||
Spans::from(vec![ | ||
Span::raw(path), | ||
Span::styled(self.ty.as_str(), diff_style), | ||
Span::raw(" "), | ||
Span::raw(&self.line), | ||
]) | ||
} | ||
} | ||
|
||
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)), | ||
)) | ||
}, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters