on master help sign in
Fixed primary cursor position calculation to use 1-width semantics.
b0311f4Nathan Vegdahl
helix-core/src/selection.rs+9−2
@@ -267,8 +267,15 @@ impl Selection {
267 }
267 }
268
268
269 #[must_use]
269 #[must_use]
270− pub fn cursor(&self) -> usize {
270+ pub fn cursor(&self, text: RopeSlice) -> usize {
271− self.primary().head
271+ let range = self.primary();
272+
273+ // For 1-width cursor semantics.
274+ if range.anchor < range.head {
275+ prev_grapheme_boundary(text, range.head)
276+ } else {
277+ range.head
278+ }
272 }
279 }
273
280
274 /// Ensure selection containing only the primary selection.
281 /// Ensure selection containing only the primary selection.
helix-term/src/commands.rs+35−15
@@ -121,7 +121,7 @@ enum Align {
121}
121}
122
122
123fn align_view(doc: &Document, view: &mut View, align: Align) {
123fn align_view(doc: &Document, view: &mut View, align: Align) {
124− let pos = doc.selection(view.id).cursor();
124+ let pos = doc.selection(view.id).cursor(doc.text().slice(..));
125 let line = doc.text().char_to_line(pos);
125 let line = doc.text().char_to_line(pos);
126
126
127 let relative = match align {
127 let relative = match align {
@@ -868,7 +868,10 @@ fn switch_to_lowercase(cx: &mut Context) {
868fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
868fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
869 use Direction::*;
869 use Direction::*;
870 let (view, doc) = current!(cx.editor);
870 let (view, doc) = current!(cx.editor);
871− let cursor = coords_at_pos(doc.text().slice(..), doc.selection(view.id).cursor());
871+ let cursor = coords_at_pos(
872+ doc.text().slice(..),
873+ doc.selection(view.id).cursor(doc.text().slice(..)),
874+ );
872 let doc_last_line = doc.text().len_lines() - 1;
875 let doc_last_line = doc.text().len_lines() - 1;
873
876
874 let last_line = view.last_line(doc);
877 let last_line = view.last_line(doc);
@@ -1038,7 +1041,7 @@ fn split_selection_on_newline(cx: &mut Context) {
1038fn search_impl(doc: &mut Document, view: &mut View, contents: &str, regex: &Regex, extend: bool) {
1041fn search_impl(doc: &mut Document, view: &mut View, contents: &str, regex: &Regex, extend: bool) {
1039 let text = doc.text();
1042 let text = doc.text();
1040 let selection = doc.selection(view.id);
1043 let selection = doc.selection(view.id);
1041− let start = text.char_to_byte(selection.cursor());
1044+ let start = text.char_to_byte(selection.cursor(text.slice(..)));
1042
1045
1043 // use find_at to find the next match after the cursor, loop around the end
1046 // use find_at to find the next match after the cursor, loop around the end
1044 // Careful, `Regex` uses `bytes` as offsets, not character indices!
1047 // Careful, `Regex` uses `bytes` as offsets, not character indices!
@@ -2446,7 +2449,11 @@ fn goto_definition(cx: &mut Context) {
2446
2449
2447 let offset_encoding = language_server.offset_encoding();
2450 let offset_encoding = language_server.offset_encoding();
2448
2451
2452+ let pos = pos_to_lsp_pos(
2453+ doc.text(),
2449− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
2454+ doc.selection(view.id).cursor(doc.text().slice(..)),
2455+ offset_encoding,
2456+ );
2450
2457
2451 // TODO: handle fails
2458 // TODO: handle fails
2452 let future = language_server.goto_definition(doc.identifier(), pos, None);
2459 let future = language_server.goto_definition(doc.identifier(), pos, None);
@@ -2483,7 +2490,11 @@ fn goto_type_definition(cx: &mut Context) {
2483
2490
2484 let offset_encoding = language_server.offset_encoding();
2491 let offset_encoding = language_server.offset_encoding();
2485
2492
2493+ let pos = pos_to_lsp_pos(
2494+ doc.text(),
2486− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
2495+ doc.selection(view.id).cursor(doc.text().slice(..)),
2496+ offset_encoding,
2497+ );
2487
2498
2488 // TODO: handle fails
2499 // TODO: handle fails
2489 let future = language_server.goto_type_definition(doc.identifier(), pos, None);
2500 let future = language_server.goto_type_definition(doc.identifier(), pos, None);
@@ -2520,7 +2531,11 @@ fn goto_implementation(cx: &mut Context) {
2520
2531
2521 let offset_encoding = language_server.offset_encoding();
2532 let offset_encoding = language_server.offset_encoding();
2522
2533
2534+ let pos = pos_to_lsp_pos(
2535+ doc.text(),
2523− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
2536+ doc.selection(view.id).cursor(doc.text().slice(..)),
2537+ offset_encoding,
2538+ );
2524
2539
2525 // TODO: handle fails
2540 // TODO: handle fails
2526 let future = language_server.goto_implementation(doc.identifier(), pos, None);
2541 let future = language_server.goto_implementation(doc.identifier(), pos, None);
@@ -2557,7 +2572,11 @@ fn goto_reference(cx: &mut Context) {
2557
2572
2558 let offset_encoding = language_server.offset_encoding();
2573 let offset_encoding = language_server.offset_encoding();
2559
2574
2575+ let pos = pos_to_lsp_pos(
2576+ doc.text(),
2560− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
2577+ doc.selection(view.id).cursor(doc.text().slice(..)),
2578+ offset_encoding,
2579+ );
2561
2580
2562 // TODO: handle fails
2581 // TODO: handle fails
2563 let future = language_server.goto_reference(doc.identifier(), pos, None);
2582 let future = language_server.goto_reference(doc.identifier(), pos, None);
@@ -2616,7 +2635,7 @@ fn goto_next_diag(cx: &mut Context) {
2616 let editor = &mut cx.editor;
2635 let editor = &mut cx.editor;
2617 let (view, doc) = current!(editor);
2636 let (view, doc) = current!(editor);
2618
2637
2619− let cursor_pos = doc.selection(view.id).cursor();
2638+ let cursor_pos = doc.selection(view.id).cursor(doc.text().slice(..));
2620 let diag = if let Some(diag) = doc
2639 let diag = if let Some(diag) = doc
2621 .diagnostics()
2640 .diagnostics()
2622 .iter()
2641 .iter()
@@ -2637,7 +2656,7 @@ fn goto_prev_diag(cx: &mut Context) {
2637 let editor = &mut cx.editor;
2656 let editor = &mut cx.editor;
2638 let (view, doc) = current!(editor);
2657 let (view, doc) = current!(editor);
2639
2658
2640− let cursor_pos = doc.selection(view.id).cursor();
2659+ let cursor_pos = doc.selection(view.id).cursor(doc.text().slice(..));
2641 let diag = if let Some(diag) = doc
2660 let diag = if let Some(diag) = doc
2642 .diagnostics()
2661 .diagnostics()
2643 .iter()
2662 .iter()
@@ -2665,7 +2684,7 @@ fn signature_help(cx: &mut Context) {
2665
2684
2666 let pos = pos_to_lsp_pos(
2685 let pos = pos_to_lsp_pos(
2667 doc.text(),
2686 doc.text(),
2668− doc.selection(view.id).cursor(),
2687+ doc.selection(view.id).cursor(doc.text().slice(..)),
2669 language_server.offset_encoding(),
2688 language_server.offset_encoding(),
2670 );
2689 );
2671
2690
@@ -3403,13 +3422,14 @@ fn completion(cx: &mut Context) {
3403 };
3422 };
3404
3423
3405 let offset_encoding = language_server.offset_encoding();
3424 let offset_encoding = language_server.offset_encoding();
3425+ let cursor = doc.selection(view.id).cursor(doc.text().slice(..));
3406
3426
3407− let pos = pos_to_lsp_pos(doc.text(), doc.selection(view.id).cursor(), offset_encoding);
3427+ let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
3408
3428
3409 // TODO: handle fails
3429 // TODO: handle fails
3410 let future = language_server.completion(doc.identifier(), pos, None);
3430 let future = language_server.completion(doc.identifier(), pos, None);
3411
3431
3412− let trigger_offset = doc.selection(view.id).cursor();
3432+ let trigger_offset = cursor;
3413
3433
3414 cx.callback(
3434 cx.callback(
3415 future,
3435 future,
@@ -3459,7 +3479,7 @@ fn hover(cx: &mut Context) {
3459
3479
3460 let pos = pos_to_lsp_pos(
3480 let pos = pos_to_lsp_pos(
3461 doc.text(),
3481 doc.text(),
3462− doc.selection(view.id).cursor(),
3482+ doc.selection(view.id).cursor(doc.text().slice(..)),
3463 language_server.offset_encoding(),
3483 language_server.offset_encoding(),
3464 );
3484 );
3465
3485
@@ -3525,7 +3545,7 @@ fn match_brackets(cx: &mut Context) {
3525 let (view, doc) = current!(cx.editor);
3545 let (view, doc) = current!(cx.editor);
3526
3546
3527 if let Some(syntax) = doc.syntax() {
3547 if let Some(syntax) = doc.syntax() {
3528− let pos = doc.selection(view.id).cursor();
3548+ let pos = doc.selection(view.id).cursor(doc.text().slice(..));
3529 if let Some(pos) = match_brackets::find(syntax, doc.text(), pos) {
3549 if let Some(pos) = match_brackets::find(syntax, doc.text(), pos) {
3530 let selection = Selection::point(pos);
3550 let selection = Selection::point(pos);
3531 doc.set_selection(view.id, selection);
3551 doc.set_selection(view.id, selection);
@@ -3629,7 +3649,7 @@ fn align_view_bottom(cx: &mut Context) {
3629
3649
3630fn align_view_middle(cx: &mut Context) {
3650fn align_view_middle(cx: &mut Context) {
3631 let (view, doc) = current!(cx.editor);
3651 let (view, doc) = current!(cx.editor);
3632− let pos = doc.selection(view.id).cursor();
3652+ let pos = doc.selection(view.id).cursor(doc.text().slice(..));
3633 let pos = coords_at_pos(doc.text().slice(..), pos);
3653 let pos = coords_at_pos(doc.text().slice(..), pos);
3634
3654
3635 const OFFSET: usize = 7; // gutters
3655 const OFFSET: usize = 7; // gutters
helix-term/src/ui/completion.rs+4−4
@@ -86,7 +86,7 @@ impl Completion {
86 let item = item.unwrap();
86 let item = item.unwrap();
87
87
88 // if more text was entered, remove it
88 // if more text was entered, remove it
89− let cursor = doc.selection(view.id).cursor();
89+ let cursor = doc.selection(view.id).cursor(doc.text().slice(..));
90 if trigger_offset < cursor {
90 if trigger_offset < cursor {
91 let remove = Transaction::change(
91 let remove = Transaction::change(
92 doc.text(),
92 doc.text(),
@@ -109,7 +109,7 @@ impl Completion {
109 )
109 )
110 } else {
110 } else {
111 let text = item.insert_text.as_ref().unwrap_or(&item.label);
111 let text = item.insert_text.as_ref().unwrap_or(&item.label);
112− let cursor = doc.selection(view.id).cursor();
112+ let cursor = doc.selection(view.id).cursor(doc.text().slice(..));
113 Transaction::change(
113 Transaction::change(
114 doc.text(),
114 doc.text(),
115 vec![(cursor, cursor, Some(text.as_str().into()))].into_iter(),
115 vec![(cursor, cursor, Some(text.as_str().into()))].into_iter(),
@@ -155,7 +155,7 @@ impl Completion {
155 // TODO: hooks should get processed immediately so maybe do it after select!(), before
155 // TODO: hooks should get processed immediately so maybe do it after select!(), before
156 // looping?
156 // looping?
157
157
158− let cursor = doc.selection(view.id).cursor();
158+ let cursor = doc.selection(view.id).cursor(doc.text().slice(..));
159 if self.trigger_offset <= cursor {
159 if self.trigger_offset <= cursor {
160 let fragment = doc.text().slice(self.trigger_offset..cursor);
160 let fragment = doc.text().slice(self.trigger_offset..cursor);
161 let text = Cow::from(fragment);
161 let text = Cow::from(fragment);
@@ -212,7 +212,7 @@ impl Component for Completion {
212 .language()
212 .language()
213 .and_then(|scope| scope.strip_prefix("source."))
213 .and_then(|scope| scope.strip_prefix("source."))
214 .unwrap_or("");
214 .unwrap_or("");
215− let cursor_pos = doc.selection(view.id).cursor();
215+ let cursor_pos = doc.selection(view.id).cursor(doc.text().slice(..));
216 let cursor_pos = (helix_core::coords_at_pos(doc.text().slice(..), cursor_pos).row
216 let cursor_pos = (helix_core::coords_at_pos(doc.text().slice(..), cursor_pos).row
217 - view.first_line) as u16;
217 - view.first_line) as u16;
218
218
helix-term/src/ui/editor.rs+6−3
@@ -398,7 +398,7 @@ impl EditorView {
398 // TODO: set cursor position for IME
398 // TODO: set cursor position for IME
399 if let Some(syntax) = doc.syntax() {
399 if let Some(syntax) = doc.syntax() {
400 use helix_core::match_brackets;
400 use helix_core::match_brackets;
401− let pos = doc.selection(view.id).cursor();
401+ let pos = doc.selection(view.id).cursor(doc.text().slice(..));
402 let pos = match_brackets::find(syntax, doc.text(), pos)
402 let pos = match_brackets::find(syntax, doc.text(), pos)
403 .and_then(|pos| view.screen_coords_at_pos(doc, text, pos));
403 .and_then(|pos| view.screen_coords_at_pos(doc, text, pos));
404
404
@@ -443,7 +443,7 @@ impl EditorView {
443 widgets::{Paragraph, Widget},
443 widgets::{Paragraph, Widget},
444 };
444 };
445
445
446− let cursor = doc.selection(view.id).cursor();
446+ let cursor = doc.selection(view.id).cursor(doc.text().slice(..));
447
447
448 let diagnostics = doc.diagnostics().iter().filter(|diagnostic| {
448 let diagnostics = doc.diagnostics().iter().filter(|diagnostic| {
449 diagnostic.range.start <= cursor && diagnostic.range.end >= cursor
449 diagnostic.range.start <= cursor && diagnostic.range.end >= cursor
@@ -555,7 +555,10 @@ impl EditorView {
555 // _ => "indent:ERROR",
555 // _ => "indent:ERROR",
556 // };
556 // };
557 let position_info = {
557 let position_info = {
558+ let pos = coords_at_pos(
559+ doc.text().slice(..),
558− let pos = coords_at_pos(doc.text().slice(..), doc.selection(view.id).cursor());
560+ doc.selection(view.id).cursor(doc.text().slice(..)),
561+ );
559 format!("{}:{}", pos.row + 1, pos.col + 1) // convert to 1-indexing
562 format!("{}:{}", pos.row + 1, pos.col + 1) // convert to 1-indexing
560 };
563 };
561
564
helix-view/src/editor.rs+3−4
@@ -141,12 +141,11 @@ impl Editor {
141 let (view, doc) = current!(self);
141 let (view, doc) = current!(self);
142
142
143 // initialize selection for view
143 // initialize selection for view
144− let selection = doc
145− .selections
144+ doc.selections
146 .entry(view.id)
145 .entry(view.id)
147 .or_insert_with(|| Selection::point(0));
146 .or_insert_with(|| Selection::point(0));
148 // TODO: reuse align_view
147 // TODO: reuse align_view
149− let pos = selection.cursor();
148+ let pos = doc.selection(view.id).cursor(doc.text().slice(..));
150 let line = doc.text().char_to_line(pos);
149 let line = doc.text().char_to_line(pos);
151 view.first_line = line.saturating_sub(view.area.height as usize / 2);
150 view.first_line = line.saturating_sub(view.area.height as usize / 2);
152
151
@@ -296,7 +295,7 @@ impl Editor {
296 const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
295 const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
297 let view = view!(self);
296 let view = view!(self);
298 let doc = &self.documents[view.doc];
297 let doc = &self.documents[view.doc];
299− let cursor = doc.selection(view.id).cursor();
298+ let cursor = doc.selection(view.id).cursor(doc.text().slice(..));
300 if let Some(mut pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) {
299 if let Some(mut pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) {
301 pos.col += view.area.x as usize + OFFSET as usize;
300 pos.col += view.area.x as usize + OFFSET as usize;
302 pos.row += view.area.y as usize;
301 pos.row += view.area.y as usize;
helix-view/src/view.rs+1−1
@@ -84,7 +84,7 @@ impl View {
84 }
84 }
85
85
86 pub fn ensure_cursor_in_view(&mut self, doc: &Document) {
86 pub fn ensure_cursor_in_view(&mut self, doc: &Document) {
87− let cursor = doc.selection(self.id).cursor();
87+ let cursor = doc.selection(self.id).cursor(doc.text().slice(..));
88 let pos = coords_at_pos(doc.text().slice(..), cursor);
88 let pos = coords_at_pos(doc.text().slice(..), cursor);
89 let line = pos.row;
89 let line = pos.row;
90 let col = pos.col;
90 let col = pos.col;