use ide::{
Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
- Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat,
- Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange,
- StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
+ Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind,
+ InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity,
+ SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
};
use itertools::Itertools;
use serde_json::to_value;
lsp_types::TextEdit { range, new_text }
}
+pub(crate) fn completion_text_edit(
+ line_index: &LineIndex,
+ insert_replace_support: Option<lsp_types::Position>,
+ indel: Indel,
+) -> lsp_types::CompletionTextEdit {
+ let text_edit = text_edit(line_index, indel);
+ match insert_replace_support {
+ Some(cursor_pos) => lsp_types::InsertReplaceEdit {
+ new_text: text_edit.new_text,
+ insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
+ replace: text_edit.range,
+ }
+ .into(),
+ None => text_edit.into(),
+ }
+}
+
pub(crate) fn snippet_text_edit(
line_index: &LineIndex,
is_snippet: bool,
}
pub(crate) fn completion_item(
+ insert_replace_support: Option<lsp_types::Position>,
line_index: &LineIndex,
item: CompletionItem,
) -> Vec<lsp_types::CompletionItem> {
for indel in item.text_edit().iter() {
if indel.delete.contains_range(source_range) {
text_edit = Some(if indel.delete == source_range {
- self::text_edit(line_index, indel.clone())
+ self::completion_text_edit(line_index, insert_replace_support, indel.clone())
} else {
assert!(source_range.end() == indel.delete.end());
let range1 = TextRange::new(indel.delete.start(), source_range.start());
let indel1 = Indel::replace(range1, String::new());
let indel2 = Indel::replace(range2, indel.insert.clone());
additional_text_edits.push(self::text_edit(line_index, indel1));
- self::text_edit(line_index, indel2)
+ self::completion_text_edit(line_index, insert_replace_support, indel2)
})
} else {
assert!(source_range.intersect(indel.delete).is_none());
detail: item.detail().map(|it| it.to_string()),
filter_text: Some(item.lookup().to_string()),
kind: item.kind().map(completion_item_kind),
- text_edit: Some(text_edit.into()),
+ text_edit: Some(text_edit),
additional_text_edits: Some(additional_text_edits),
documentation: item.documentation().map(documentation),
deprecated: Some(item.deprecated()),
SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
},
+ HlTag::Attribute => semantic_tokens::ATTRIBUTE,
+ HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
- HlTag::None => semantic_tokens::GENERIC,
HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
- HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
- HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
HlTag::CharLiteral => semantic_tokens::CHAR_LITERAL,
HlTag::Comment => lsp_types::SemanticTokenType::COMMENT,
- HlTag::Attribute => semantic_tokens::ATTRIBUTE,
+ HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
+ HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
+ HlTag::None => semantic_tokens::GENERIC,
+ HlTag::Operator(op) => match op {
+ HlOperator::Bitwise => semantic_tokens::BITWISE,
+ HlOperator::Arithmetic => semantic_tokens::ARITHMETIC,
+ HlOperator::Logical => semantic_tokens::LOGICAL,
+ HlOperator::Comparision => semantic_tokens::COMPARISION,
+ HlOperator::Other => semantic_tokens::OPERATOR,
+ },
+ HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
- HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
- HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
- HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
HlTag::Punctuation(punct) => match punct {
HlPunct::Bracket => semantic_tokens::BRACKET,
HlPunct::Brace => semantic_tokens::BRACE,
HlMod::Unsafe => semantic_tokens::UNSAFE,
HlMod::Callable => semantic_tokens::CALLABLE,
HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
+ HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
+ HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
HlMod::Associated => continue,
};
mods |= modifier;
FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region),
- FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None,
+ FoldKind::Mods
+ | FoldKind::Block
+ | FoldKind::ArgList
+ | FoldKind::Consts
+ | FoldKind::Statics
+ | FoldKind::Array => None,
};
let range = range(line_index, fold.range);
}
}
+pub(crate) fn text_document_edit(
+ snap: &GlobalStateSnapshot,
+ file_id: FileId,
+ edit: TextEdit,
+) -> Result<lsp_types::TextDocumentEdit> {
+ let text_document = optional_versioned_text_document_identifier(snap, file_id);
+ let line_index = snap.file_line_index(file_id)?;
+ let edits =
+ edit.into_iter().map(|it| lsp_types::OneOf::Left(text_edit(&line_index, it))).collect();
+ Ok(lsp_types::TextDocumentEdit { text_document, edits })
+}
+
pub(crate) fn snippet_text_document_edit(
snap: &GlobalStateSnapshot,
is_snippet: bool,
}
}
-pub(crate) fn unresolved_code_action(
+pub(crate) fn code_action(
snap: &GlobalStateSnapshot,
- code_action_params: lsp_types::CodeActionParams,
assist: Assist,
- index: usize,
+ resolve_data: Option<(usize, lsp_types::CodeActionParams)>,
) -> Result<lsp_ext::CodeAction> {
- assert!(assist.source_change.is_none());
- let res = lsp_ext::CodeAction {
+ let mut res = lsp_ext::CodeAction {
title: assist.label.to_string(),
group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
kind: Some(code_action_kind(assist.id.1)),
edit: None,
is_preferred: None,
- data: Some(lsp_ext::CodeActionData {
- id: format!("{}:{}", assist.id.0, index.to_string()),
- code_action_params,
- }),
- };
- Ok(res)
-}
-
-pub(crate) fn resolved_code_action(
- snap: &GlobalStateSnapshot,
- assist: Assist,
-) -> Result<lsp_ext::CodeAction> {
- let change = assist.source_change.unwrap();
- let res = lsp_ext::CodeAction {
- edit: Some(snippet_workspace_edit(snap, change)?),
- title: assist.label.to_string(),
- group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
- kind: Some(code_action_kind(assist.id.1)),
- is_preferred: None,
data: None,
};
+ match (assist.source_change, resolve_data) {
+ (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
+ (None, Some((index, code_action_params))) => {
+ res.data = Some(lsp_ext::CodeActionData {
+ id: format!("{}:{}", assist.id.0, index.to_string()),
+ code_action_params,
+ });
+ }
+ (None, None) => {
+ stdx::never!("assist should always be resolved if client can't do lazy resolving")
+ }
+ };
Ok(res)
}
mod tests {
use std::sync::Arc;
- use hir::PrefixKind;
use ide::Analysis;
- use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
+ use ide_db::helpers::{
+ insert_use::{InsertUseConfig, PrefixKind},
+ SnippetCap,
+ };
use super::*;
.unwrap()
.into_iter()
.filter(|c| c.label().ends_with("arg"))
- .map(|c| completion_item(&line_index, c))
+ .map(|c| completion_item(None, &line_index, c))
.flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
.collect();
expect_test::expect![[r#"
(
"&arg",
Some(
- "fffffffa",
+ "fffffff9",
),
),
(