Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--helix-term/src/commands.rs49
-rw-r--r--helix-view/src/document.rs35
-rw-r--r--helix-view/src/view.rs3
3 files changed, 61 insertions, 26 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 8edf5944..aafe9619 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -3942,6 +3942,7 @@ fn goto_first_diag(cx: &mut Context) {
Some(diag) => Selection::single(diag.range.start, diag.range.end),
None => return,
};
+
doc.set_selection(view.id, selection);
view.diagnostics_handler
.immediately_show_diagnostic(doc, view.id);
@@ -3953,6 +3954,7 @@ fn goto_last_diag(cx: &mut Context) {
Some(diag) => Selection::single(diag.range.start, diag.range.end),
None => return,
};
+
doc.set_selection(view.id, selection);
view.diagnostics_handler
.immediately_show_diagnostic(doc, view.id);
@@ -4009,6 +4011,7 @@ fn goto_prev_diag(cx: &mut Context) {
view.diagnostics_handler
.immediately_show_diagnostic(doc, view.id);
};
+
cx.editor.apply_motion(motion)
}
@@ -5444,6 +5447,8 @@ fn reverse_selection_contents(cx: &mut Context) {
// tree sitter node selection
+const EXPAND_KEY: &str = "expand";
+
fn expand_selection(cx: &mut Context) {
let motion = |editor: &mut Editor| {
let (view, doc) = current!(editor);
@@ -5451,42 +5456,52 @@ fn expand_selection(cx: &mut Context) {
if let Some(syntax) = doc.syntax() {
let text = doc.text().slice(..);
- let current_selection = doc.selection(view.id);
+ let current_selection = doc.selection(view.id).clone();
let selection = object::expand_selection(syntax, text, current_selection.clone());
// check if selection is different from the last one
- if *current_selection != selection {
- // save current selection so it can be restored using shrink_selection
- view.object_selections.push(current_selection.clone());
+ if current_selection != selection {
+ let prev_selections = doc
+ .view_data_mut(view.id)
+ .object_selections
+ .entry(EXPAND_KEY)
+ .or_default();
- doc.set_selection(view.id, selection);
+ // save current selection so it can be restored using shrink_selection
+ prev_selections.push(current_selection);
+ doc.set_selection_clear(view.id, selection, false);
}
}
};
+
cx.editor.apply_motion(motion);
}
fn shrink_selection(cx: &mut Context) {
let motion = |editor: &mut Editor| {
let (view, doc) = current!(editor);
- let current_selection = doc.selection(view.id);
+ let current_selection = doc.selection(view.id).clone();
+ let prev_expansions = doc
+ .view_data_mut(view.id)
+ .object_selections
+ .entry(EXPAND_KEY)
+ .or_default();
+
// try to restore previous selection
- if let Some(prev_selection) = view.object_selections.pop() {
- if current_selection.contains(&prev_selection) {
- doc.set_selection(view.id, prev_selection);
- return;
- } else {
- // clear existing selection as they can't be shrunk to anyway
- view.object_selections.clear();
- }
+ if let Some(prev_selection) = prev_expansions.pop() {
+ // allow shrinking the selection only if current selection contains the previous object selection
+ doc.set_selection_clear(view.id, prev_selection, false);
+ return;
}
+
// if not previous selection, shrink to first child
if let Some(syntax) = doc.syntax() {
let text = doc.text().slice(..);
- let selection = object::shrink_selection(syntax, text, current_selection.clone());
- doc.set_selection(view.id, selection);
+ let selection = object::shrink_selection(syntax, text, current_selection);
+ doc.set_selection_clear(view.id, selection, false);
}
};
+
cx.editor.apply_motion(motion);
}
@@ -5605,8 +5620,6 @@ fn match_brackets(cx: &mut Context) {
doc.set_selection(view.id, selection);
}
-//
-
fn jump_forward(cx: &mut Context) {
let count = cx.count();
let config = cx.editor.config();
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index 04b7703c..c0672001 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -1320,15 +1320,27 @@ impl Document {
Ok(())
}
- /// Select text within the [`Document`].
- pub fn set_selection(&mut self, view_id: ViewId, selection: Selection) {
+ /// Select text within the [`Document`], optionally clearing the previous selection state.
+ pub fn set_selection_clear(&mut self, view_id: ViewId, selection: Selection, clear_prev: bool) {
// TODO: use a transaction?
self.selections
.insert(view_id, selection.ensure_invariants(self.text().slice(..)));
+
helix_event::dispatch(SelectionDidChange {
doc: self,
view: view_id,
- })
+ });
+
+ if clear_prev {
+ self.view_data
+ .entry(view_id)
+ .and_modify(|view_data| view_data.object_selections.clear());
+ }
+ }
+
+ /// Select text within the [`Document`].
+ pub fn set_selection(&mut self, view_id: ViewId, selection: Selection) {
+ self.set_selection_clear(view_id, selection, true);
}
/// Find the origin selection of the text in a document, i.e. where
@@ -1520,6 +1532,12 @@ impl Document {
apply_inlay_hint_changes(padding_after_inlay_hints);
}
+ // clear out all associated view object selections, as they are no
+ // longer valid
+ self.view_data
+ .values_mut()
+ .for_each(|view_data| view_data.object_selections.clear());
+
helix_event::dispatch(DocumentDidChange {
doc: self,
view: view_id,
@@ -1962,13 +1980,13 @@ impl Document {
&self.selections
}
- fn view_data(&self, view_id: ViewId) -> &ViewData {
+ pub fn view_data(&self, view_id: ViewId) -> &ViewData {
self.view_data
.get(&view_id)
.expect("This should only be called after ensure_view_init")
}
- fn view_data_mut(&mut self, view_id: ViewId) -> &mut ViewData {
+ pub fn view_data_mut(&mut self, view_id: ViewId) -> &mut ViewData {
self.view_data.entry(view_id).or_default()
}
@@ -2286,9 +2304,13 @@ impl Document {
}
}
+/// Stores data needed for views that are tied to this specific Document.
#[derive(Debug, Default)]
pub struct ViewData {
view_position: ViewPosition,
+
+ /// used to store previous selections of tree-sitter objects
+ pub object_selections: HashMap<&'static str, Vec<Selection>>,
}
#[derive(Clone, Debug)]
@@ -2339,6 +2361,7 @@ mod test {
Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
);
let view = ViewId::default();
+ doc.ensure_view_init(view);
doc.set_selection(view, Selection::single(0, 0));
let transaction =
@@ -2377,7 +2400,9 @@ mod test {
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
Arc::new(ArcSwap::from_pointee(syntax::Loader::default())),
);
+
let view = ViewId::default();
+ doc.ensure_view_init(view);
doc.set_selection(view, Selection::single(5, 5));
// insert
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index aecf09a6..d275befb 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -137,8 +137,6 @@ pub struct View {
// uses two docs because we want to be able to swap between the
// two last modified docs which we need to manually keep track of
pub last_modified_docs: [Option<DocumentId>; 2],
- /// used to store previous selections of tree-sitter objects
- pub object_selections: Vec<Selection>,
/// all gutter-related configuration settings, used primarily for gutter rendering
pub gutters: GutterConfig,
/// A mapping between documents and the last history revision the view was updated at.
@@ -175,7 +173,6 @@ impl View {
jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
docs_access_history: Vec::new(),
last_modified_docs: [None, None],
- object_selections: Vec::new(),
gutters,
doc_revisions: HashMap::new(),
diagnostics_handler: DiagnosticsHandler::new(),