Skip to content

Commit

Permalink
add textobject for change
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalkuthe committed Nov 30, 2022
1 parent b36b28e commit 0af3de5
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
1 change: 1 addition & 0 deletions book/src/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ though, we climb the syntax tree and then take the previous selection. So
| `a` | Argument/parameter |
| `o` | Comment |
| `t` | Test |
| `g` | Change |

> NOTE: `f`, `c`, etc need a tree-sitter grammar active for the current
document and a special tree-sitter query file to work properly. [Only
Expand Down
25 changes: 24 additions & 1 deletion helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use helix_core::{
object, pos_at_coords, pos_at_visual_coords,
regex::{self, Regex, RegexBuilder},
search::{self, CharMatcher},
selection, shellwords, surround, textobject,
selection, shellwords, surround,
textobject,
tree_sitter::Node,
unicode::width::UnicodeWidthChar,
visual_coords_at_pos, LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection,
Expand Down Expand Up @@ -4608,6 +4609,27 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
)
};

if ch == 'g' && doc.diff_handle().is_none() {
editor.set_status("Diff is not available in current buffer");
return;
}

let textobject_change = |range: Range| -> Range {
let diff_handle = doc.diff_handle().unwrap();
let hunks = diff_handle.hunks();
let line = range.cursor_line(text);
let hunk_idx = if let Some(hunk_idx) = hunks.hunk_at(line as u32, false) {
hunk_idx
} else {
return range;
};
let hunk = hunks.nth_hunk(hunk_idx).after;

let start = text.line_to_char(hunk.start as usize);
let end = text.line_to_char(hunk.end as usize);
Range::new(start, end).with_direction(range.direction())
};

let selection = doc.selection(view.id).clone().transform(|range| {
match ch {
'w' => textobject::textobject_word(text, range, objtype, count, false),
Expand All @@ -4621,6 +4643,7 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
'm' => textobject::textobject_pair_surround_closest(
text, range, objtype, count,
),
'g' => textobject_change(range),
// TODO: cancel new ranges if inconsistent surround matches across lines
ch if !ch.is_ascii_alphanumeric() => {
textobject::textobject_pair_surround(text, range, objtype, ch, count)
Expand Down
30 changes: 30 additions & 0 deletions helix-vcs/src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,34 @@ impl FileHunks<'_> {
Err(pos) | Ok(pos) => Some(pos as u32 - 1),
}
}

pub fn hunk_at(&self, line: u32, include_removal: bool) -> Option<u32> {
let hunk_range = if self.inverted {
|hunk: &Hunk| hunk.before.clone()
} else {
|hunk: &Hunk| hunk.after.clone()
};

let res = self
.hunks
.binary_search_by_key(&line, |hunk| hunk_range(hunk).start);

match res {
// Search found a hunk that starts exactly at this line, return it
Ok(pos) => Some(pos as u32),

// No hunk starts exactly at this line, so the search returns
// the position where a hunk starting at this line should be inserted.
// The previous hunk contains this hunk if it exists and doesn't end before this line
Err(0) => None,
Err(pos) => {
let hunk = hunk_range(&self.hunks[pos - 1]);
if hunk.end > line || include_removal && hunk.start == line && hunk.is_empty() {
Some(pos as u32 - 1)
} else {
None
}
}
}
}
}

0 comments on commit 0af3de5

Please sign in to comment.