|
|
@ -121,8 +121,9 @@ impl Completion {
|
|
|
|
include_placeholder: bool,
|
|
|
|
include_placeholder: bool,
|
|
|
|
) -> Transaction {
|
|
|
|
) -> Transaction {
|
|
|
|
use helix_lsp::snippet;
|
|
|
|
use helix_lsp::snippet;
|
|
|
|
|
|
|
|
let selection = doc.selection(view_id);
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(edit) = &item.text_edit {
|
|
|
|
let (start_offset, end_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) => {
|
|
|
@ -130,46 +131,27 @@ impl Completion {
|
|
|
|
lsp::TextEdit::new(item.replace, item.new_text.clone())
|
|
|
|
lsp::TextEdit::new(item.replace, item.new_text.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
let text = doc.text().slice(..);
|
|
|
|
if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
|
|
|
|
let primary_cursor = selection.primary().cursor(text);
|
|
|
|
|| matches!(
|
|
|
|
|
|
|
|
item.insert_text_format,
|
|
|
|
let start_offset =
|
|
|
|
Some(lsp::InsertTextFormat::SNIPPET)
|
|
|
|
match util::lsp_pos_to_pos(doc.text(), edit.range.start, offset_encoding) {
|
|
|
|
)
|
|
|
|
Some(start) => start as i128 - primary_cursor as i128,
|
|
|
|
{
|
|
|
|
None => return Transaction::new(doc.text()),
|
|
|
|
match snippet::parse(&edit.new_text) {
|
|
|
|
};
|
|
|
|
Ok(snippet) => util::generate_transaction_from_snippet(
|
|
|
|
let end_offset =
|
|
|
|
doc.text(),
|
|
|
|
match util::lsp_pos_to_pos(doc.text(), edit.range.end, offset_encoding) {
|
|
|
|
doc.selection(view_id),
|
|
|
|
Some(end) => end as i128 - primary_cursor as i128,
|
|
|
|
&edit.range,
|
|
|
|
None => return Transaction::new(doc.text()),
|
|
|
|
snippet,
|
|
|
|
};
|
|
|
|
doc.line_ending.as_str(),
|
|
|
|
|
|
|
|
include_placeholder,
|
|
|
|
(start_offset, end_offset, edit.new_text)
|
|
|
|
offset_encoding,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
Err(err) => {
|
|
|
|
|
|
|
|
log::error!(
|
|
|
|
|
|
|
|
"Failed to parse snippet: {:?}, remaining output: {}",
|
|
|
|
|
|
|
|
&edit.new_text,
|
|
|
|
|
|
|
|
err
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
Transaction::new(doc.text())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
util::generate_transaction_from_completion_edit(
|
|
|
|
|
|
|
|
doc.text(),
|
|
|
|
|
|
|
|
doc.selection(view_id),
|
|
|
|
|
|
|
|
edit,
|
|
|
|
|
|
|
|
offset_encoding, // TODO: should probably transcode in Client
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
let text = item.insert_text.as_ref().unwrap_or(&item.label);
|
|
|
|
let new_text = item.insert_text.as_ref().unwrap_or(&item.label);
|
|
|
|
// Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯
|
|
|
|
// Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯
|
|
|
|
// in these cases we need to check for a common prefix and remove it
|
|
|
|
// in these cases we need to check for a common prefix and remove it
|
|
|
|
let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset));
|
|
|
|
let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset));
|
|
|
|
let text = text.trim_start_matches::<&str>(&prefix);
|
|
|
|
let new_text = new_text.trim_start_matches::<&str>(&prefix);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: this needs to be true for the numbers to work out correctly
|
|
|
|
// TODO: this needs to be true for the numbers to work out correctly
|
|
|
|
// in the closure below. It's passed in to a callback as this same
|
|
|
|
// in the closure below. It's passed in to a callback as this same
|
|
|
@ -182,11 +164,42 @@ impl Completion {
|
|
|
|
== trigger_offset
|
|
|
|
== trigger_offset
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| {
|
|
|
|
(0, 0, new_text.into())
|
|
|
|
let cursor = range.cursor(doc.text().slice(..));
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
(cursor, cursor, Some(text.into()))
|
|
|
|
if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
|
|
|
|
})
|
|
|
|
|| matches!(
|
|
|
|
|
|
|
|
item.insert_text_format,
|
|
|
|
|
|
|
|
Some(lsp::InsertTextFormat::SNIPPET)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
match snippet::parse(&new_text) {
|
|
|
|
|
|
|
|
Ok(snippet) => util::generate_transaction_from_snippet(
|
|
|
|
|
|
|
|
doc.text(),
|
|
|
|
|
|
|
|
selection,
|
|
|
|
|
|
|
|
start_offset,
|
|
|
|
|
|
|
|
end_offset,
|
|
|
|
|
|
|
|
snippet,
|
|
|
|
|
|
|
|
doc.line_ending.as_str(),
|
|
|
|
|
|
|
|
include_placeholder,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
Err(err) => {
|
|
|
|
|
|
|
|
log::error!(
|
|
|
|
|
|
|
|
"Failed to parse snippet: {:?}, remaining output: {}",
|
|
|
|
|
|
|
|
&new_text,
|
|
|
|
|
|
|
|
err
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
Transaction::new(doc.text())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
util::generate_transaction_from_completion_edit(
|
|
|
|
|
|
|
|
doc.text(),
|
|
|
|
|
|
|
|
selection,
|
|
|
|
|
|
|
|
start_offset,
|
|
|
|
|
|
|
|
end_offset,
|
|
|
|
|
|
|
|
new_text,
|
|
|
|
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|