treat replace/insertmode consistently, default to insert

pull/6250/head
Pascal Kuthe 2 years ago committed by Blaž Hrastnik
parent b1f7528090
commit d63e570e0a

@ -50,6 +50,7 @@ signal to the Helix process on Unix operating systems, such as by using the comm
| `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | | `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` |
| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant | `400` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant | `400` |
| `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` |
| `completion-replace` | Set to `true` to make completions always replace the entire word and not just the part before the cursor | `false` |
| `auto-info` | Whether to display info boxes | `true` | | `auto-info` | Whether to display info boxes | `true` |
| `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative | `false` | | `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative | `false` |
| `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file | `[]` | | `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file | `[]` |

@ -250,18 +250,27 @@ pub mod util {
/// If the LS did not provide a range for the completion or the range of the /// If the LS did not provide a range for the completion or the range of the
/// primary cursor can not be used for the secondary cursor, this function /// primary cursor can not be used for the secondary cursor, this function
/// can be used to find the completion range for a cursor /// can be used to find the completion range for a cursor
fn find_completion_range(text: RopeSlice, cursor: usize) -> (usize, usize) { fn find_completion_range(text: RopeSlice, replace_mode: bool, cursor: usize) -> (usize, usize) {
let start = cursor let start = cursor
- text - text
.chars_at(cursor) .chars_at(cursor)
.reversed() .reversed()
.take_while(|ch| chars::char_is_word(*ch)) .take_while(|ch| chars::char_is_word(*ch))
.count(); .count();
(start, cursor) let mut end = cursor;
if replace_mode {
end += text
.chars_at(cursor)
.skip(1)
.take_while(|ch| chars::char_is_word(*ch))
.count();
}
(start, end)
} }
fn completion_range( fn completion_range(
text: RopeSlice, text: RopeSlice,
edit_offset: Option<(i128, i128)>, edit_offset: Option<(i128, i128)>,
replace_mode: bool,
cursor: usize, cursor: usize,
) -> Option<(usize, usize)> { ) -> Option<(usize, usize)> {
let res = match edit_offset { let res = match edit_offset {
@ -276,7 +285,7 @@ pub mod util {
} }
(start_offset as usize, end_offset as usize) (start_offset as usize, end_offset as usize)
} }
None => find_completion_range(text, cursor), None => find_completion_range(text, replace_mode, cursor),
}; };
Some(res) Some(res)
} }
@ -287,6 +296,7 @@ pub mod util {
doc: &Rope, doc: &Rope,
selection: &Selection, selection: &Selection,
edit_offset: Option<(i128, i128)>, edit_offset: Option<(i128, i128)>,
replace_mode: bool,
new_text: String, new_text: String,
) -> Transaction { ) -> Transaction {
let replacement: Option<Tendril> = if new_text.is_empty() { let replacement: Option<Tendril> = if new_text.is_empty() {
@ -296,8 +306,12 @@ pub mod util {
}; };
let text = doc.slice(..); let text = doc.slice(..);
let (removed_start, removed_end) = let (removed_start, removed_end) = completion_range(
completion_range(text, edit_offset, selection.primary().cursor(text)) text,
edit_offset,
replace_mode,
selection.primary().cursor(text),
)
.expect("transaction must be valid for primary selection"); .expect("transaction must be valid for primary selection");
let removed_text = text.slice(removed_start..removed_end); let removed_text = text.slice(removed_start..removed_end);
@ -306,9 +320,9 @@ pub mod util {
selection, selection,
|range| { |range| {
let cursor = range.cursor(text); let cursor = range.cursor(text);
completion_range(text, edit_offset, cursor) completion_range(text, edit_offset, replace_mode, cursor)
.filter(|(start, end)| text.slice(start..end) == removed_text) .filter(|(start, end)| text.slice(start..end) == removed_text)
.unwrap_or_else(|| find_completion_range(text, cursor)) .unwrap_or_else(|| find_completion_range(text, replace_mode, cursor))
}, },
|_, _| replacement.clone(), |_, _| replacement.clone(),
); );
@ -326,6 +340,7 @@ pub mod util {
doc: &Rope, doc: &Rope,
selection: &Selection, selection: &Selection,
edit_offset: Option<(i128, i128)>, edit_offset: Option<(i128, i128)>,
replace_mode: bool,
snippet: snippet::Snippet, snippet: snippet::Snippet,
line_ending: &str, line_ending: &str,
include_placeholder: bool, include_placeholder: bool,
@ -336,8 +351,12 @@ pub mod util {
let mut off = 0i128; let mut off = 0i128;
let mut mapped_doc = doc.clone(); let mut mapped_doc = doc.clone();
let mut selection_tabstops: SmallVec<[_; 1]> = SmallVec::new(); let mut selection_tabstops: SmallVec<[_; 1]> = SmallVec::new();
let (removed_start, removed_end) = let (removed_start, removed_end) = completion_range(
completion_range(text, edit_offset, selection.primary().cursor(text)) text,
edit_offset,
replace_mode,
selection.primary().cursor(text),
)
.expect("transaction must be valid for primary selection"); .expect("transaction must be valid for primary selection");
let removed_text = text.slice(removed_start..removed_end); let removed_text = text.slice(removed_start..removed_end);
@ -346,9 +365,9 @@ pub mod util {
selection, selection,
|range| { |range| {
let cursor = range.cursor(text); let cursor = range.cursor(text);
completion_range(text, edit_offset, cursor) completion_range(text, edit_offset, replace_mode, cursor)
.filter(|(start, end)| text.slice(start..end) == removed_text) .filter(|(start, end)| text.slice(start..end) == removed_text)
.unwrap_or_else(|| find_completion_range(text, cursor)) .unwrap_or_else(|| find_completion_range(text, replace_mode, cursor))
}, },
|replacement_start, replacement_end| { |replacement_start, replacement_end| {
let mapped_replacement_start = (replacement_start as i128 + off) as usize; let mapped_replacement_start = (replacement_start as i128 + off) as usize;

@ -108,6 +108,7 @@ impl Completion {
start_offset: usize, start_offset: usize,
trigger_offset: usize, trigger_offset: usize,
) -> Self { ) -> Self {
let replace_mode = editor.config().completion_replace;
// Sort completion items according to their preselect status (given by the LSP server) // Sort completion items according to their preselect status (given by the LSP server)
items.sort_by_key(|item| !item.preselect.unwrap_or(false)); items.sort_by_key(|item| !item.preselect.unwrap_or(false));
@ -120,18 +121,23 @@ impl Completion {
offset_encoding: helix_lsp::OffsetEncoding, offset_encoding: helix_lsp::OffsetEncoding,
trigger_offset: usize, trigger_offset: usize,
include_placeholder: bool, include_placeholder: bool,
replace_mode: bool,
) -> Transaction { ) -> Transaction {
use helix_lsp::snippet; use helix_lsp::snippet;
let selection = doc.selection(view_id); let selection = doc.selection(view_id);
let text = doc.text().slice(..); let text = doc.text().slice(..);
let primary_cursor = selection.primary().cursor(text); let primary_cursor = selection.primary().cursor(text);
let (start_offset, end_offset, new_text) = if let Some(edit) = &item.text_edit { let (edit_offset, new_text) = if let Some(edit) = &item.text_edit {
let edit = match edit { let edit = match edit {
lsp::CompletionTextEdit::Edit(edit) => edit.clone(), lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
lsp::CompletionTextEdit::InsertAndReplace(item) => { lsp::CompletionTextEdit::InsertAndReplace(item) => {
// TODO: support using "insert" instead of "replace" via user config let range = if replace_mode {
lsp::TextEdit::new(item.replace, item.new_text.clone()) item.replace
} else {
item.insert
};
lsp::TextEdit::new(range, item.new_text.clone())
} }
}; };
@ -157,7 +163,7 @@ impl Completion {
// document changed (and not just the selection) then we will // document changed (and not just the selection) then we will
// likely delete the wrong text (same if we applied an edit sent by the LS) // likely delete the wrong text (same if we applied an edit sent by the LS)
debug_assert!(primary_cursor == trigger_offset); debug_assert!(primary_cursor == trigger_offset);
(None, Some(0), new_text) (None, new_text)
}; };
if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET)) if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
@ -170,8 +176,8 @@ impl Completion {
Ok(snippet) => util::generate_transaction_from_snippet( Ok(snippet) => util::generate_transaction_from_snippet(
doc.text(), doc.text(),
selection, selection,
start_offset, edit_offset,
end_offset, replace_mode,
snippet, snippet,
doc.line_ending.as_str(), doc.line_ending.as_str(),
include_placeholder, include_placeholder,
@ -190,8 +196,8 @@ impl Completion {
util::generate_transaction_from_completion_edit( util::generate_transaction_from_completion_edit(
doc.text(), doc.text(),
selection, selection,
start_offset, edit_offset,
end_offset, replace_mode,
new_text, new_text,
) )
} }
@ -224,6 +230,7 @@ impl Completion {
offset_encoding, offset_encoding,
trigger_offset, trigger_offset,
true, true,
replace_mode,
); );
// initialize a savepoint // initialize a savepoint
@ -245,6 +252,7 @@ impl Completion {
offset_encoding, offset_encoding,
trigger_offset, trigger_offset,
false, false,
replace_mode,
); );
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);

@ -251,6 +251,9 @@ pub struct Config {
)] )]
pub idle_timeout: Duration, pub idle_timeout: Duration,
pub completion_trigger_len: u8, pub completion_trigger_len: u8,
/// Whether to instruct the LSP to replace the entire word when applying a completion
/// or to only insert new text
pub completion_replace: bool,
/// Whether to display infoboxes. Defaults to true. /// Whether to display infoboxes. Defaults to true.
pub auto_info: bool, pub auto_info: bool,
pub file_picker: FilePickerConfig, pub file_picker: FilePickerConfig,
@ -738,6 +741,7 @@ impl Default for Config {
color_modes: false, color_modes: false,
soft_wrap: SoftWrap::default(), soft_wrap: SoftWrap::default(),
text_width: 80, text_width: 80,
completion_replace: false,
} }
} }
} }

Loading…
Cancel
Save