2020-05-10 11:55:24 +00:00
|
|
|
//! Conversion of rust-analyzer specific types to lsp_types equivalents.
|
2020-08-06 01:35:35 +00:00
|
|
|
use std::{
|
2021-04-18 22:14:21 +00:00
|
|
|
iter::once,
|
2021-07-17 14:40:13 +00:00
|
|
|
path,
|
2020-08-06 01:35:35 +00:00
|
|
|
sync::atomic::{AtomicU32, Ordering},
|
|
|
|
};
|
2020-06-13 09:00:06 +00:00
|
|
|
|
2020-08-13 15:42:52 +00:00
|
|
|
use ide::{
|
2021-05-17 17:07:10 +00:00
|
|
|
Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancellable, CompletionItem,
|
2021-04-18 22:14:21 +00:00
|
|
|
CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
|
|
|
|
Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
|
2021-10-02 09:18:18 +00:00
|
|
|
InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity,
|
2021-07-04 12:41:28 +00:00
|
|
|
SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
|
2020-05-10 11:55:24 +00:00
|
|
|
};
|
2020-08-13 15:42:52 +00:00
|
|
|
use itertools::Itertools;
|
2021-02-13 11:07:47 +00:00
|
|
|
use serde_json::to_value;
|
2021-07-17 14:40:13 +00:00
|
|
|
use vfs::AbsPath;
|
2020-05-10 11:55:24 +00:00
|
|
|
|
2020-06-02 14:30:26 +00:00
|
|
|
use crate::{
|
2021-02-12 21:44:28 +00:00
|
|
|
cargo_target_spec::CargoTargetSpec,
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
config::Config,
|
2021-02-12 21:44:28 +00:00
|
|
|
global_state::GlobalStateSnapshot,
|
2021-02-12 22:28:48 +00:00
|
|
|
line_index::{LineEndings, LineIndex, OffsetEncoding},
|
2021-09-04 09:56:34 +00:00
|
|
|
lsp_ext,
|
|
|
|
lsp_utils::invalid_params_error,
|
|
|
|
semantic_tokens, Result,
|
2020-06-02 14:30:26 +00:00
|
|
|
};
|
2020-05-10 11:55:24 +00:00
|
|
|
|
|
|
|
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_col = line_index.index.line_col(offset);
|
2021-02-12 21:55:27 +00:00
|
|
|
match line_index.encoding {
|
|
|
|
OffsetEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
|
|
|
|
OffsetEncoding::Utf16 => {
|
|
|
|
let line_col = line_index.index.to_utf16(line_col);
|
|
|
|
lsp_types::Position::new(line_col.line, line_col.col)
|
|
|
|
}
|
|
|
|
}
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Range {
|
|
|
|
let start = position(line_index, range.start());
|
|
|
|
let end = position(line_index, range.end());
|
|
|
|
lsp_types::Range::new(start, end)
|
|
|
|
}
|
|
|
|
|
2020-12-17 11:29:05 +00:00
|
|
|
pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
|
|
|
|
match symbol_kind {
|
2021-10-14 06:37:57 +00:00
|
|
|
SymbolKind::Function => lsp_types::SymbolKind::FUNCTION,
|
|
|
|
SymbolKind::Struct => lsp_types::SymbolKind::STRUCT,
|
|
|
|
SymbolKind::Enum => lsp_types::SymbolKind::ENUM,
|
|
|
|
SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
|
|
|
|
SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE,
|
2021-12-04 17:18:09 +00:00
|
|
|
SymbolKind::Macro
|
|
|
|
| SymbolKind::BuiltinAttr
|
|
|
|
| SymbolKind::Attribute
|
|
|
|
| SymbolKind::Derive => lsp_types::SymbolKind::FUNCTION,
|
2021-12-03 16:15:19 +00:00
|
|
|
SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
|
2022-03-05 22:34:37 +00:00
|
|
|
SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => {
|
|
|
|
lsp_types::SymbolKind::TYPE_PARAMETER
|
|
|
|
}
|
2021-10-14 06:37:57 +00:00
|
|
|
SymbolKind::Field => lsp_types::SymbolKind::FIELD,
|
|
|
|
SymbolKind::Static => lsp_types::SymbolKind::CONSTANT,
|
|
|
|
SymbolKind::Const => lsp_types::SymbolKind::CONSTANT,
|
|
|
|
SymbolKind::ConstParam => lsp_types::SymbolKind::CONSTANT,
|
|
|
|
SymbolKind::Impl => lsp_types::SymbolKind::OBJECT,
|
2020-12-18 20:00:43 +00:00
|
|
|
SymbolKind::Local
|
|
|
|
| SymbolKind::SelfParam
|
|
|
|
| SymbolKind::LifetimeParam
|
2020-12-23 16:15:01 +00:00
|
|
|
| SymbolKind::ValueParam
|
2021-10-14 06:37:57 +00:00
|
|
|
| SymbolKind::Label => lsp_types::SymbolKind::VARIABLE,
|
|
|
|
SymbolKind::Union => lsp_types::SymbolKind::STRUCT,
|
2021-03-14 15:16:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolKind {
|
|
|
|
match kind {
|
|
|
|
StructureNodeKind::SymbolKind(symbol) => symbol_kind(symbol),
|
2021-10-14 06:37:57 +00:00
|
|
|
StructureNodeKind::Region => lsp_types::SymbolKind::NAMESPACE,
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn document_highlight_kind(
|
2021-10-02 09:18:18 +00:00
|
|
|
category: ReferenceCategory,
|
2020-05-10 11:55:24 +00:00
|
|
|
) -> lsp_types::DocumentHighlightKind {
|
2021-10-02 09:18:18 +00:00
|
|
|
match category {
|
2021-10-14 06:37:57 +00:00
|
|
|
ReferenceCategory::Read => lsp_types::DocumentHighlightKind::READ,
|
|
|
|
ReferenceCategory::Write => lsp_types::DocumentHighlightKind::WRITE,
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity {
|
|
|
|
match severity {
|
2021-10-14 06:37:57 +00:00
|
|
|
Severity::Error => lsp_types::DiagnosticSeverity::ERROR,
|
|
|
|
Severity::WeakWarning => lsp_types::DiagnosticSeverity::HINT,
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
|
|
|
|
let value = crate::markdown::format_docs(documentation.as_str());
|
|
|
|
let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
|
|
|
|
lsp_types::Documentation::MarkupContent(markup_content)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn completion_item_kind(
|
|
|
|
completion_item_kind: CompletionItemKind,
|
|
|
|
) -> lsp_types::CompletionItemKind {
|
|
|
|
match completion_item_kind {
|
2021-10-14 06:37:57 +00:00
|
|
|
CompletionItemKind::Binding => lsp_types::CompletionItemKind::VARIABLE,
|
|
|
|
CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::STRUCT,
|
|
|
|
CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD,
|
|
|
|
CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD,
|
|
|
|
CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET,
|
|
|
|
CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE,
|
2021-01-20 17:38:12 +00:00
|
|
|
CompletionItemKind::SymbolKind(symbol) => match symbol {
|
2021-12-04 17:01:22 +00:00
|
|
|
SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION,
|
2021-10-14 06:37:57 +00:00
|
|
|
SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,
|
|
|
|
SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
|
2021-12-04 17:18:09 +00:00
|
|
|
SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION,
|
2021-10-14 06:37:57 +00:00
|
|
|
SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM,
|
|
|
|
SymbolKind::Field => lsp_types::CompletionItemKind::FIELD,
|
|
|
|
SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION,
|
|
|
|
SymbolKind::Impl => lsp_types::CompletionItemKind::TEXT,
|
|
|
|
SymbolKind::Label => lsp_types::CompletionItemKind::VARIABLE,
|
|
|
|
SymbolKind::LifetimeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
|
|
|
|
SymbolKind::Local => lsp_types::CompletionItemKind::VARIABLE,
|
2021-12-04 17:01:22 +00:00
|
|
|
SymbolKind::Macro => lsp_types::CompletionItemKind::FUNCTION,
|
2021-10-14 06:37:57 +00:00
|
|
|
SymbolKind::Module => lsp_types::CompletionItemKind::MODULE,
|
|
|
|
SymbolKind::SelfParam => lsp_types::CompletionItemKind::VALUE,
|
2022-03-05 22:34:37 +00:00
|
|
|
SymbolKind::SelfType => lsp_types::CompletionItemKind::TYPE_PARAMETER,
|
2021-10-14 06:37:57 +00:00
|
|
|
SymbolKind::Static => lsp_types::CompletionItemKind::VALUE,
|
|
|
|
SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT,
|
|
|
|
SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE,
|
|
|
|
SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT,
|
|
|
|
SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
|
|
|
|
SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT,
|
|
|
|
SymbolKind::ValueParam => lsp_types::CompletionItemKind::VALUE,
|
|
|
|
SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER,
|
2021-12-03 15:53:30 +00:00
|
|
|
SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION,
|
2021-12-03 16:15:19 +00:00
|
|
|
SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE,
|
2021-01-20 17:38:12 +00:00
|
|
|
},
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 21:44:28 +00:00
|
|
|
pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::TextEdit {
|
2020-05-10 11:55:24 +00:00
|
|
|
let range = range(line_index, indel.delete);
|
2021-02-12 21:44:28 +00:00
|
|
|
let new_text = match line_index.endings {
|
2020-05-10 11:55:24 +00:00
|
|
|
LineEndings::Unix => indel.insert,
|
|
|
|
LineEndings::Dos => indel.insert.replace('\n', "\r\n"),
|
|
|
|
};
|
|
|
|
lsp_types::TextEdit { range, new_text }
|
|
|
|
}
|
|
|
|
|
2021-04-08 12:22:54 +00:00
|
|
|
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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-17 22:11:40 +00:00
|
|
|
pub(crate) fn snippet_text_edit(
|
|
|
|
line_index: &LineIndex,
|
|
|
|
is_snippet: bool,
|
|
|
|
indel: Indel,
|
|
|
|
) -> lsp_ext::SnippetTextEdit {
|
2021-02-12 21:44:28 +00:00
|
|
|
let text_edit = text_edit(line_index, indel);
|
2020-05-17 22:11:40 +00:00
|
|
|
let insert_text_format =
|
2021-10-14 06:37:57 +00:00
|
|
|
if is_snippet { Some(lsp_types::InsertTextFormat::SNIPPET) } else { None };
|
2020-05-17 22:11:40 +00:00
|
|
|
lsp_ext::SnippetTextEdit {
|
|
|
|
range: text_edit.range,
|
|
|
|
new_text: text_edit.new_text,
|
|
|
|
insert_text_format,
|
2021-04-16 15:31:47 +00:00
|
|
|
annotation_id: None,
|
2020-05-17 22:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 11:55:24 +00:00
|
|
|
pub(crate) fn text_edit_vec(
|
|
|
|
line_index: &LineIndex,
|
|
|
|
text_edit: TextEdit,
|
|
|
|
) -> Vec<lsp_types::TextEdit> {
|
2021-02-12 21:44:28 +00:00
|
|
|
text_edit.into_iter().map(|indel| self::text_edit(line_index, indel)).collect()
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-05-25 12:12:53 +00:00
|
|
|
pub(crate) fn snippet_text_edit_vec(
|
|
|
|
line_index: &LineIndex,
|
|
|
|
is_snippet: bool,
|
|
|
|
text_edit: TextEdit,
|
|
|
|
) -> Vec<lsp_ext::SnippetTextEdit> {
|
|
|
|
text_edit
|
|
|
|
.into_iter()
|
2021-02-12 21:44:28 +00:00
|
|
|
.map(|indel| self::snippet_text_edit(line_index, is_snippet, indel))
|
2020-05-25 12:12:53 +00:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2021-07-04 11:08:33 +00:00
|
|
|
pub(crate) fn completion_items(
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
config: &Config,
|
2020-05-10 11:55:24 +00:00
|
|
|
line_index: &LineIndex,
|
2021-07-04 11:08:33 +00:00
|
|
|
tdpp: lsp_types::TextDocumentPositionParams,
|
|
|
|
items: Vec<CompletionItem>,
|
2020-09-29 20:24:56 +00:00
|
|
|
) -> Vec<lsp_types::CompletionItem> {
|
2021-07-04 13:50:02 +00:00
|
|
|
let max_relevance = items.iter().map(|it| it.relevance().score()).max().unwrap_or_default();
|
2021-07-04 11:08:33 +00:00
|
|
|
let mut res = Vec::with_capacity(items.len());
|
|
|
|
for item in items {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
completion_item(&mut res, config, line_index, &tdpp, max_relevance, item)
|
2021-07-04 11:08:33 +00:00
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
fn completion_item(
|
|
|
|
acc: &mut Vec<lsp_types::CompletionItem>,
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
config: &Config,
|
2021-07-04 11:08:33 +00:00
|
|
|
line_index: &LineIndex,
|
|
|
|
tdpp: &lsp_types::TextDocumentPositionParams,
|
2021-07-04 13:50:02 +00:00
|
|
|
max_relevance: u32,
|
2021-07-04 11:08:33 +00:00
|
|
|
item: CompletionItem,
|
|
|
|
) {
|
2020-05-10 11:55:24 +00:00
|
|
|
let mut additional_text_edits = Vec::new();
|
2021-07-04 08:12:41 +00:00
|
|
|
|
2020-05-10 11:55:24 +00:00
|
|
|
// LSP does not allow arbitrary edits in completion, so we have to do a
|
|
|
|
// non-trivial mapping here.
|
2021-07-04 08:12:41 +00:00
|
|
|
let text_edit = {
|
|
|
|
let mut text_edit = None;
|
|
|
|
let source_range = item.source_range();
|
|
|
|
for indel in item.text_edit().iter() {
|
|
|
|
if indel.delete.contains_range(source_range) {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
let insert_replace_support = config.insert_replace_support().then(|| tdpp.position);
|
2021-07-04 08:12:41 +00:00
|
|
|
text_edit = Some(if indel.delete == source_range {
|
|
|
|
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 range2 = source_range;
|
|
|
|
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::completion_text_edit(line_index, insert_replace_support, indel2)
|
|
|
|
})
|
2020-05-10 11:55:24 +00:00
|
|
|
} else {
|
2021-07-04 08:12:41 +00:00
|
|
|
assert!(source_range.intersect(indel.delete).is_none());
|
|
|
|
let text_edit = self::text_edit(line_index, indel.clone());
|
|
|
|
additional_text_edits.push(text_edit);
|
|
|
|
}
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
2021-07-04 08:12:41 +00:00
|
|
|
text_edit.unwrap()
|
|
|
|
};
|
2020-05-10 11:55:24 +00:00
|
|
|
|
2021-03-09 14:39:22 +00:00
|
|
|
let mut lsp_item = lsp_types::CompletionItem {
|
|
|
|
label: item.label().to_string(),
|
|
|
|
detail: item.detail().map(|it| it.to_string()),
|
|
|
|
filter_text: Some(item.lookup().to_string()),
|
2021-10-27 15:18:42 +00:00
|
|
|
kind: Some(completion_item_kind(item.kind())),
|
2021-04-08 12:22:54 +00:00
|
|
|
text_edit: Some(text_edit),
|
2020-05-10 11:55:24 +00:00
|
|
|
additional_text_edits: Some(additional_text_edits),
|
2021-03-09 14:39:22 +00:00
|
|
|
documentation: item.documentation().map(documentation),
|
|
|
|
deprecated: Some(item.deprecated()),
|
2020-05-10 11:55:24 +00:00
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
2021-07-04 13:50:02 +00:00
|
|
|
set_score(&mut lsp_item, max_relevance, item.relevance());
|
2021-03-12 03:11:14 +00:00
|
|
|
|
2021-03-09 14:39:22 +00:00
|
|
|
if item.deprecated() {
|
2021-10-14 06:37:57 +00:00
|
|
|
lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::DEPRECATED])
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
if item.trigger_call_info() && config.client_commands().trigger_parameter_hints {
|
2021-03-09 14:39:22 +00:00
|
|
|
lsp_item.command = Some(command::trigger_parameter_hints());
|
2020-05-14 13:36:15 +00:00
|
|
|
}
|
|
|
|
|
2021-07-04 12:41:28 +00:00
|
|
|
if item.is_snippet() {
|
2021-10-14 06:37:57 +00:00
|
|
|
lsp_item.insert_text_format = Some(lsp_types::InsertTextFormat::SNIPPET);
|
2021-07-04 12:41:28 +00:00
|
|
|
}
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
if config.completion().enable_imports_on_the_fly {
|
2021-10-04 19:44:33 +00:00
|
|
|
if let imports @ [_, ..] = item.imports_to_add() {
|
|
|
|
let imports: Vec<_> = imports
|
|
|
|
.iter()
|
|
|
|
.filter_map(|import_edit| {
|
|
|
|
let import_path = &import_edit.import.import_path;
|
|
|
|
let import_name = import_path.segments().last()?;
|
|
|
|
Some(lsp_ext::CompletionImport {
|
|
|
|
full_import_path: import_path.to_string(),
|
|
|
|
imported_name: import_name.to_string(),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
if !imports.is_empty() {
|
|
|
|
let data = lsp_ext::CompletionResolveData { position: tdpp.clone(), imports };
|
2021-07-04 11:08:33 +00:00
|
|
|
lsp_item.data = Some(to_value(data).unwrap());
|
2021-03-09 16:04:27 +00:00
|
|
|
}
|
2020-09-29 20:24:56 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-04 08:12:41 +00:00
|
|
|
|
2021-07-04 11:08:33 +00:00
|
|
|
if let Some((mutability, relevance)) = item.ref_match() {
|
|
|
|
let mut lsp_item_with_ref = lsp_item.clone();
|
2021-07-04 13:50:02 +00:00
|
|
|
set_score(&mut lsp_item_with_ref, max_relevance, relevance);
|
2021-07-04 11:08:33 +00:00
|
|
|
lsp_item_with_ref.label =
|
|
|
|
format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
|
|
|
|
if let Some(it) = &mut lsp_item_with_ref.text_edit {
|
|
|
|
let new_text = match it {
|
|
|
|
lsp_types::CompletionTextEdit::Edit(it) => &mut it.new_text,
|
|
|
|
lsp_types::CompletionTextEdit::InsertAndReplace(it) => &mut it.new_text,
|
|
|
|
};
|
|
|
|
*new_text = format!("&{}{}", mutability.as_keyword_for_ref(), new_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
acc.push(lsp_item_with_ref);
|
|
|
|
};
|
|
|
|
|
|
|
|
acc.push(lsp_item);
|
2021-07-04 08:12:41 +00:00
|
|
|
|
2021-07-04 13:50:02 +00:00
|
|
|
fn set_score(
|
|
|
|
res: &mut lsp_types::CompletionItem,
|
|
|
|
max_relevance: u32,
|
|
|
|
relevance: CompletionRelevance,
|
|
|
|
) {
|
|
|
|
if relevance.is_relevant() && relevance.score() == max_relevance {
|
2021-07-04 08:12:41 +00:00
|
|
|
res.preselect = Some(true);
|
|
|
|
}
|
|
|
|
// The relevance needs to be inverted to come up with a sort score
|
|
|
|
// because the client will sort ascending.
|
|
|
|
let sort_score = relevance.score() ^ 0xFF_FF_FF_FF;
|
|
|
|
// Zero pad the string to ensure values can be properly sorted
|
|
|
|
// by the client. Hex format is used because it is easier to
|
|
|
|
// visually compare very large values, which the sort text
|
|
|
|
// tends to be since it is the opposite of the score.
|
|
|
|
res.sort_text = Some(format!("{:08x}", sort_score));
|
|
|
|
}
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 16:41:16 +00:00
|
|
|
pub(crate) fn signature_help(
|
|
|
|
call_info: CallInfo,
|
|
|
|
concise: bool,
|
|
|
|
label_offsets: bool,
|
|
|
|
) -> lsp_types::SignatureHelp {
|
|
|
|
let (label, parameters) = match (concise, label_offsets) {
|
|
|
|
(_, false) => {
|
|
|
|
let params = call_info
|
|
|
|
.parameter_labels()
|
|
|
|
.map(|label| lsp_types::ParameterInformation {
|
|
|
|
label: lsp_types::ParameterLabel::Simple(label.to_string()),
|
|
|
|
documentation: None,
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let label =
|
|
|
|
if concise { call_info.parameter_labels().join(", ") } else { call_info.signature };
|
|
|
|
(label, params)
|
|
|
|
}
|
|
|
|
(false, true) => {
|
|
|
|
let params = call_info
|
|
|
|
.parameter_ranges()
|
|
|
|
.iter()
|
2021-03-17 00:27:56 +00:00
|
|
|
.map(|it| [u32::from(it.start()), u32::from(it.end())])
|
2020-07-16 16:41:16 +00:00
|
|
|
.map(|label_offsets| lsp_types::ParameterInformation {
|
|
|
|
label: lsp_types::ParameterLabel::LabelOffsets(label_offsets),
|
|
|
|
documentation: None,
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
(call_info.signature, params)
|
|
|
|
}
|
|
|
|
(true, true) => {
|
|
|
|
let mut params = Vec::new();
|
|
|
|
let mut label = String::new();
|
|
|
|
let mut first = true;
|
|
|
|
for param in call_info.parameter_labels() {
|
|
|
|
if !first {
|
|
|
|
label.push_str(", ");
|
|
|
|
}
|
|
|
|
first = false;
|
2020-11-16 20:10:13 +00:00
|
|
|
let start = label.len() as u32;
|
2020-07-16 16:41:16 +00:00
|
|
|
label.push_str(param);
|
2020-11-16 20:10:13 +00:00
|
|
|
let end = label.len() as u32;
|
2020-07-16 16:41:16 +00:00
|
|
|
params.push(lsp_types::ParameterInformation {
|
|
|
|
label: lsp_types::ParameterLabel::LabelOffsets([start, end]),
|
|
|
|
documentation: None,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
(label, params)
|
|
|
|
}
|
|
|
|
};
|
2020-05-10 11:55:24 +00:00
|
|
|
|
2020-07-18 13:14:44 +00:00
|
|
|
let documentation = if concise {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
call_info.doc.map(|doc| {
|
|
|
|
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
|
|
|
|
kind: lsp_types::MarkupKind::Markdown,
|
|
|
|
value: doc,
|
|
|
|
})
|
2020-07-16 11:00:56 +00:00
|
|
|
})
|
2020-07-18 13:14:44 +00:00
|
|
|
};
|
2020-07-16 11:00:56 +00:00
|
|
|
|
2020-11-16 20:10:13 +00:00
|
|
|
let active_parameter = call_info.active_parameter.map(|it| it as u32);
|
2020-09-18 15:39:25 +00:00
|
|
|
|
|
|
|
let signature = lsp_types::SignatureInformation {
|
|
|
|
label,
|
|
|
|
documentation,
|
|
|
|
parameters: Some(parameters),
|
|
|
|
active_parameter,
|
|
|
|
};
|
2020-07-16 11:00:56 +00:00
|
|
|
lsp_types::SignatureHelp {
|
|
|
|
signatures: vec![signature],
|
2021-10-11 19:42:16 +00:00
|
|
|
active_signature: Some(0),
|
2020-09-18 15:39:25 +00:00
|
|
|
active_parameter,
|
2020-07-16 11:00:56 +00:00
|
|
|
}
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-08-28 18:55:24 +00:00
|
|
|
pub(crate) fn inlay_hint(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ext::InlayHint {
|
2020-05-10 17:25:37 +00:00
|
|
|
lsp_ext::InlayHint {
|
2022-03-08 10:01:02 +00:00
|
|
|
label: match inlay_hint.kind {
|
|
|
|
InlayKind::ParameterHint => format!("{}:", inlay_hint.label),
|
|
|
|
InlayKind::TypeHint => format!(": {}", inlay_hint.label),
|
|
|
|
InlayKind::ChainingHint => inlay_hint.label.to_string(),
|
|
|
|
},
|
2022-03-07 13:24:54 +00:00
|
|
|
position: match inlay_hint.kind {
|
|
|
|
InlayKind::ParameterHint => position(line_index, inlay_hint.range.start()),
|
2022-03-07 17:18:36 +00:00
|
|
|
InlayKind::TypeHint | InlayKind::ChainingHint => {
|
|
|
|
position(line_index, inlay_hint.range.end())
|
|
|
|
}
|
2022-03-07 13:24:54 +00:00
|
|
|
},
|
2020-05-10 11:55:24 +00:00
|
|
|
kind: match inlay_hint.kind {
|
2022-02-11 23:03:32 +00:00
|
|
|
InlayKind::ParameterHint => Some(lsp_ext::InlayHintKind::PARAMETER),
|
|
|
|
InlayKind::TypeHint => Some(lsp_ext::InlayHintKind::TYPE),
|
|
|
|
InlayKind::ChainingHint => None,
|
2020-05-10 11:55:24 +00:00
|
|
|
},
|
2022-02-12 06:33:04 +00:00
|
|
|
tooltip: None,
|
2022-03-07 17:18:36 +00:00
|
|
|
padding_left: Some(match inlay_hint.kind {
|
2022-03-08 10:01:02 +00:00
|
|
|
InlayKind::TypeHint => false,
|
2022-03-07 17:18:36 +00:00
|
|
|
InlayKind::ParameterHint => false,
|
|
|
|
InlayKind::ChainingHint => true,
|
|
|
|
}),
|
|
|
|
padding_right: Some(match inlay_hint.kind {
|
|
|
|
InlayKind::TypeHint => false,
|
|
|
|
InlayKind::ParameterHint => true,
|
|
|
|
InlayKind::ChainingHint => false,
|
|
|
|
}),
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-06 01:35:35 +00:00
|
|
|
static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
|
|
|
|
|
2020-05-10 17:09:22 +00:00
|
|
|
pub(crate) fn semantic_tokens(
|
|
|
|
text: &str,
|
|
|
|
line_index: &LineIndex,
|
2021-01-09 11:48:15 +00:00
|
|
|
highlights: Vec<HlRange>,
|
2021-05-17 15:37:06 +00:00
|
|
|
highlight_strings: bool,
|
2020-05-10 17:09:22 +00:00
|
|
|
) -> lsp_types::SemanticTokens {
|
2020-08-06 01:35:35 +00:00
|
|
|
let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
|
2020-07-24 21:55:17 +00:00
|
|
|
let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
|
2020-05-10 17:09:22 +00:00
|
|
|
|
|
|
|
for highlight_range in highlights {
|
2021-01-07 22:39:02 +00:00
|
|
|
if highlight_range.highlight.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-17 15:37:06 +00:00
|
|
|
let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
|
|
|
|
if !highlight_strings && ty == lsp_types::SemanticTokenType::STRING {
|
2021-05-10 20:09:38 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-05-17 15:37:06 +00:00
|
|
|
let token_index = semantic_tokens::type_index(ty);
|
2020-05-10 17:14:02 +00:00
|
|
|
let modifier_bitset = mods.0;
|
|
|
|
|
2021-02-12 21:44:28 +00:00
|
|
|
for mut text_range in line_index.index.lines(highlight_range.range) {
|
2020-05-10 17:09:22 +00:00
|
|
|
if text[text_range].ends_with('\n') {
|
|
|
|
text_range =
|
|
|
|
TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n'));
|
|
|
|
}
|
2021-06-13 03:54:16 +00:00
|
|
|
let range = range(line_index, text_range);
|
2020-05-10 17:09:22 +00:00
|
|
|
builder.push(range, token_index, modifier_bitset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.build()
|
|
|
|
}
|
|
|
|
|
2020-09-01 16:53:07 +00:00
|
|
|
pub(crate) fn semantic_token_delta(
|
2020-07-24 21:55:17 +00:00
|
|
|
previous: &lsp_types::SemanticTokens,
|
|
|
|
current: &lsp_types::SemanticTokens,
|
2020-09-01 16:53:07 +00:00
|
|
|
) -> lsp_types::SemanticTokensDelta {
|
2020-07-24 21:55:17 +00:00
|
|
|
let result_id = current.result_id.clone();
|
|
|
|
let edits = semantic_tokens::diff_tokens(&previous.data, ¤t.data);
|
2020-09-01 16:53:07 +00:00
|
|
|
lsp_types::SemanticTokensDelta { result_id, edits }
|
2020-07-24 21:55:17 +00:00
|
|
|
}
|
|
|
|
|
2020-05-10 17:14:02 +00:00
|
|
|
fn semantic_token_type_and_modifiers(
|
|
|
|
highlight: Highlight,
|
|
|
|
) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
|
2020-05-10 11:55:24 +00:00
|
|
|
let mut mods = semantic_tokens::ModifierSet::default();
|
|
|
|
let type_ = match highlight.tag {
|
2021-01-09 11:44:01 +00:00
|
|
|
HlTag::Symbol(symbol) => match symbol {
|
2021-12-04 17:01:22 +00:00
|
|
|
SymbolKind::Attribute => semantic_tokens::ATTRIBUTE,
|
2021-12-04 17:18:09 +00:00
|
|
|
SymbolKind::Derive => semantic_tokens::DERIVE,
|
2020-12-18 20:00:43 +00:00
|
|
|
SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
|
2021-05-16 13:11:48 +00:00
|
|
|
SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
|
2020-12-18 20:00:43 +00:00
|
|
|
SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
|
|
|
|
SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
|
2021-01-01 09:07:01 +00:00
|
|
|
SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
|
2020-12-18 20:00:43 +00:00
|
|
|
SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
|
2020-12-23 16:15:01 +00:00
|
|
|
SymbolKind::Label => semantic_tokens::LABEL,
|
2020-12-18 20:00:43 +00:00
|
|
|
SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
|
|
|
|
SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
|
2022-03-05 22:34:37 +00:00
|
|
|
SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD,
|
2020-12-18 20:00:43 +00:00
|
|
|
SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE,
|
2020-12-19 14:10:47 +00:00
|
|
|
SymbolKind::Function => {
|
2021-01-09 11:44:01 +00:00
|
|
|
if highlight.mods.contains(HlMod::Associated) {
|
2020-12-19 14:10:47 +00:00
|
|
|
lsp_types::SemanticTokenType::METHOD
|
|
|
|
} else {
|
|
|
|
lsp_types::SemanticTokenType::FUNCTION
|
|
|
|
}
|
|
|
|
}
|
2020-12-18 20:00:43 +00:00
|
|
|
SymbolKind::Const => {
|
|
|
|
mods |= semantic_tokens::CONSTANT;
|
|
|
|
mods |= lsp_types::SemanticTokenModifier::STATIC;
|
|
|
|
lsp_types::SemanticTokenType::VARIABLE
|
|
|
|
}
|
|
|
|
SymbolKind::Static => {
|
|
|
|
mods |= lsp_types::SemanticTokenModifier::STATIC;
|
|
|
|
lsp_types::SemanticTokenType::VARIABLE
|
|
|
|
}
|
|
|
|
SymbolKind::Struct => lsp_types::SemanticTokenType::STRUCT,
|
|
|
|
SymbolKind::Enum => lsp_types::SemanticTokenType::ENUM,
|
|
|
|
SymbolKind::Variant => lsp_types::SemanticTokenType::ENUM_MEMBER,
|
|
|
|
SymbolKind::Union => semantic_tokens::UNION,
|
|
|
|
SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS,
|
|
|
|
SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
|
|
|
|
SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
|
2021-12-03 15:53:30 +00:00
|
|
|
SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE,
|
2021-12-03 16:15:19 +00:00
|
|
|
SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE,
|
2020-12-18 20:00:43 +00:00
|
|
|
},
|
2021-12-04 17:01:22 +00:00
|
|
|
HlTag::AttributeBracket => semantic_tokens::ATTRIBUTE_BRACKET,
|
2021-03-17 19:57:30 +00:00
|
|
|
HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
|
2021-01-09 11:44:01 +00:00
|
|
|
HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
|
|
|
|
HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
|
2021-05-26 13:23:05 +00:00
|
|
|
HlTag::CharLiteral => semantic_tokens::CHAR,
|
2021-01-09 11:44:01 +00:00
|
|
|
HlTag::Comment => lsp_types::SemanticTokenType::COMMENT,
|
2021-03-17 19:57:30 +00:00
|
|
|
HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
|
2021-01-09 11:44:01 +00:00
|
|
|
HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
|
2021-03-17 19:57:30 +00:00
|
|
|
HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
|
|
|
|
HlTag::None => semantic_tokens::GENERIC,
|
2021-04-05 18:39:17 +00:00
|
|
|
HlTag::Operator(op) => match op {
|
|
|
|
HlOperator::Bitwise => semantic_tokens::BITWISE,
|
|
|
|
HlOperator::Arithmetic => semantic_tokens::ARITHMETIC,
|
|
|
|
HlOperator::Logical => semantic_tokens::LOGICAL,
|
2021-04-19 15:44:38 +00:00
|
|
|
HlOperator::Comparison => semantic_tokens::COMPARISON,
|
2021-04-05 18:39:17 +00:00
|
|
|
HlOperator::Other => semantic_tokens::OPERATOR,
|
|
|
|
},
|
2021-03-17 19:57:30 +00:00
|
|
|
HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
|
|
|
|
HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
|
2021-01-10 12:33:03 +00:00
|
|
|
HlTag::Punctuation(punct) => match punct {
|
|
|
|
HlPunct::Bracket => semantic_tokens::BRACKET,
|
|
|
|
HlPunct::Brace => semantic_tokens::BRACE,
|
|
|
|
HlPunct::Parenthesis => semantic_tokens::PARENTHESIS,
|
|
|
|
HlPunct::Angle => semantic_tokens::ANGLE,
|
|
|
|
HlPunct::Comma => semantic_tokens::COMMA,
|
|
|
|
HlPunct::Dot => semantic_tokens::DOT,
|
|
|
|
HlPunct::Colon => semantic_tokens::COLON,
|
|
|
|
HlPunct::Semi => semantic_tokens::SEMICOLON,
|
|
|
|
HlPunct::Other => semantic_tokens::PUNCTUATION,
|
2022-01-02 18:10:10 +00:00
|
|
|
HlPunct::MacroBang => semantic_tokens::MACRO_BANG,
|
2021-01-10 12:33:03 +00:00
|
|
|
},
|
2020-05-10 11:55:24 +00:00
|
|
|
};
|
|
|
|
|
2021-01-09 11:44:01 +00:00
|
|
|
for modifier in highlight.mods.iter() {
|
2020-05-10 11:55:24 +00:00
|
|
|
let modifier = match modifier {
|
2021-09-30 13:40:17 +00:00
|
|
|
HlMod::Associated => continue,
|
|
|
|
HlMod::Async => semantic_tokens::ASYNC,
|
2021-01-09 11:44:01 +00:00
|
|
|
HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
|
2021-09-30 13:40:17 +00:00
|
|
|
HlMod::Callable => semantic_tokens::CALLABLE,
|
|
|
|
HlMod::Consuming => semantic_tokens::CONSUMING,
|
|
|
|
HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
|
2021-09-30 15:01:31 +00:00
|
|
|
HlMod::CrateRoot => semantic_tokens::CRATE_ROOT,
|
2021-09-30 15:55:29 +00:00
|
|
|
HlMod::DefaultLibrary => lsp_types::SemanticTokenModifier::DEFAULT_LIBRARY,
|
2021-01-09 11:44:01 +00:00
|
|
|
HlMod::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
|
|
|
|
HlMod::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION,
|
|
|
|
HlMod::Injected => semantic_tokens::INJECTED,
|
2021-09-30 13:40:17 +00:00
|
|
|
HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
|
2021-05-24 04:48:42 +00:00
|
|
|
HlMod::Library => semantic_tokens::LIBRARY,
|
2021-09-30 13:40:17 +00:00
|
|
|
HlMod::Mutable => semantic_tokens::MUTABLE,
|
2021-05-27 23:25:32 +00:00
|
|
|
HlMod::Public => semantic_tokens::PUBLIC,
|
2021-09-30 13:40:17 +00:00
|
|
|
HlMod::Reference => semantic_tokens::REFERENCE,
|
2021-01-09 11:44:01 +00:00
|
|
|
HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
|
2021-03-29 17:27:05 +00:00
|
|
|
HlMod::Trait => semantic_tokens::TRAIT_MODIFIER,
|
2021-09-30 13:40:17 +00:00
|
|
|
HlMod::Unsafe => semantic_tokens::UNSAFE,
|
2020-05-10 11:55:24 +00:00
|
|
|
};
|
|
|
|
mods |= modifier;
|
|
|
|
}
|
|
|
|
|
2020-05-10 17:14:02 +00:00
|
|
|
(type_, mods)
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn folding_range(
|
|
|
|
text: &str,
|
|
|
|
line_index: &LineIndex,
|
|
|
|
line_folding_only: bool,
|
|
|
|
fold: Fold,
|
|
|
|
) -> lsp_types::FoldingRange {
|
|
|
|
let kind = match fold.kind {
|
|
|
|
FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
|
|
|
|
FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
|
2021-03-13 16:24:28 +00:00
|
|
|
FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region),
|
2021-03-29 11:56:02 +00:00
|
|
|
FoldKind::Mods
|
|
|
|
| FoldKind::Block
|
|
|
|
| FoldKind::ArgList
|
|
|
|
| FoldKind::Consts
|
2021-04-07 22:05:08 +00:00
|
|
|
| FoldKind::Statics
|
2021-04-30 08:18:36 +00:00
|
|
|
| FoldKind::WhereClause
|
2021-05-28 11:39:02 +00:00
|
|
|
| FoldKind::ReturnType
|
2021-04-07 22:05:08 +00:00
|
|
|
| FoldKind::Array => None,
|
2020-05-10 11:55:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let range = range(line_index, fold.range);
|
|
|
|
|
|
|
|
if line_folding_only {
|
|
|
|
// Clients with line_folding_only == true (such as VSCode) will fold the whole end line
|
|
|
|
// even if it contains text not in the folding range. To prevent that we exclude
|
|
|
|
// range.end.line from the folding region if there is more text after range.end
|
|
|
|
// on the same line.
|
|
|
|
let has_more_text_on_end_line = text[TextRange::new(fold.range.end(), TextSize::of(text))]
|
|
|
|
.chars()
|
|
|
|
.take_while(|it| *it != '\n')
|
|
|
|
.any(|it| !it.is_whitespace());
|
|
|
|
|
|
|
|
let end_line = if has_more_text_on_end_line {
|
|
|
|
range.end.line.saturating_sub(1)
|
|
|
|
} else {
|
|
|
|
range.end.line
|
|
|
|
};
|
|
|
|
|
|
|
|
lsp_types::FoldingRange {
|
|
|
|
start_line: range.start.line,
|
|
|
|
start_character: None,
|
|
|
|
end_line,
|
|
|
|
end_character: None,
|
|
|
|
kind,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lsp_types::FoldingRange {
|
|
|
|
start_line: range.start.line,
|
|
|
|
start_character: Some(range.start.character),
|
|
|
|
end_line: range.end.line,
|
|
|
|
end_character: Some(range.end.character),
|
|
|
|
kind,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 09:00:06 +00:00
|
|
|
pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url {
|
|
|
|
snap.file_id_to_url(file_id)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a `Url` object from a given path, will lowercase drive letters if present.
|
|
|
|
/// This will only happen when processing windows paths.
|
|
|
|
///
|
|
|
|
/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
|
2021-07-17 14:40:13 +00:00
|
|
|
pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url {
|
2020-06-13 09:00:06 +00:00
|
|
|
let url = lsp_types::Url::from_file_path(path).unwrap();
|
2021-07-17 14:40:13 +00:00
|
|
|
match path.as_ref().components().next() {
|
2021-02-12 11:09:54 +00:00
|
|
|
Some(path::Component::Prefix(prefix))
|
|
|
|
if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) =>
|
2020-06-13 09:00:06 +00:00
|
|
|
{
|
|
|
|
// Need to lowercase driver letter
|
|
|
|
}
|
|
|
|
_ => return url,
|
|
|
|
}
|
|
|
|
|
|
|
|
let driver_letter_range = {
|
|
|
|
let (scheme, drive_letter, _rest) = match url.as_str().splitn(3, ':').collect_tuple() {
|
|
|
|
Some(it) => it,
|
|
|
|
None => return url,
|
|
|
|
};
|
|
|
|
let start = scheme.len() + ':'.len_utf8();
|
|
|
|
start..(start + drive_letter.len())
|
|
|
|
};
|
|
|
|
|
|
|
|
// Note: lowercasing the `path` itself doesn't help, the `Url::parse`
|
|
|
|
// machinery *also* canonicalizes the drive letter. So, just massage the
|
|
|
|
// string in place.
|
2021-05-24 13:31:54 +00:00
|
|
|
let mut url: String = url.into();
|
2020-06-13 09:00:06 +00:00
|
|
|
url[driver_letter_range].make_ascii_lowercase();
|
|
|
|
lsp_types::Url::parse(&url).unwrap()
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-11-16 20:10:13 +00:00
|
|
|
pub(crate) fn optional_versioned_text_document_identifier(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-10 11:55:24 +00:00
|
|
|
file_id: FileId,
|
2020-11-16 20:10:13 +00:00
|
|
|
) -> lsp_types::OptionalVersionedTextDocumentIdentifier {
|
2020-07-21 18:07:42 +00:00
|
|
|
let url = url(snap, file_id);
|
|
|
|
let version = snap.url_file_version(&url);
|
2020-11-16 20:10:13 +00:00
|
|
|
lsp_types::OptionalVersionedTextDocumentIdentifier { uri: url, version }
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-06-03 09:16:08 +00:00
|
|
|
pub(crate) fn location(
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
frange: FileRange,
|
|
|
|
) -> Result<lsp_types::Location> {
|
2020-06-13 09:00:06 +00:00
|
|
|
let url = url(snap, frange.file_id);
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(frange.file_id)?;
|
2020-05-10 11:55:24 +00:00
|
|
|
let range = range(&line_index, frange.range);
|
|
|
|
let loc = lsp_types::Location::new(url, range);
|
2020-06-30 11:27:13 +00:00
|
|
|
Ok(loc)
|
|
|
|
}
|
|
|
|
|
2021-10-28 14:13:37 +00:00
|
|
|
/// Prefer using `location_link`, if the client has the cap.
|
2020-06-30 11:27:13 +00:00
|
|
|
pub(crate) fn location_from_nav(
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
nav: NavigationTarget,
|
|
|
|
) -> Result<lsp_types::Location> {
|
2020-07-17 10:42:48 +00:00
|
|
|
let url = url(snap, nav.file_id);
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(nav.file_id)?;
|
2020-07-17 10:42:48 +00:00
|
|
|
let range = range(&line_index, nav.full_range);
|
2020-06-30 11:27:13 +00:00
|
|
|
let loc = lsp_types::Location::new(url, range);
|
2020-05-10 11:55:24 +00:00
|
|
|
Ok(loc)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn location_link(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-25 13:55:25 +00:00
|
|
|
src: Option<FileRange>,
|
2020-05-10 11:55:24 +00:00
|
|
|
target: NavigationTarget,
|
|
|
|
) -> Result<lsp_types::LocationLink> {
|
2020-05-25 13:55:25 +00:00
|
|
|
let origin_selection_range = match src {
|
|
|
|
Some(src) => {
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(src.file_id)?;
|
2020-05-25 13:55:25 +00:00
|
|
|
let range = range(&line_index, src.range);
|
|
|
|
Some(range)
|
|
|
|
}
|
|
|
|
None => None,
|
|
|
|
};
|
2020-06-03 09:16:08 +00:00
|
|
|
let (target_uri, target_range, target_selection_range) = location_info(snap, target)?;
|
2020-05-10 11:55:24 +00:00
|
|
|
let res = lsp_types::LocationLink {
|
2020-05-25 13:55:25 +00:00
|
|
|
origin_selection_range,
|
2020-05-10 11:55:24 +00:00
|
|
|
target_uri,
|
|
|
|
target_range,
|
|
|
|
target_selection_range,
|
|
|
|
};
|
|
|
|
Ok(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn location_info(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-10 11:55:24 +00:00
|
|
|
target: NavigationTarget,
|
|
|
|
) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(target.file_id)?;
|
2020-05-10 11:55:24 +00:00
|
|
|
|
2020-07-17 10:42:48 +00:00
|
|
|
let target_uri = url(snap, target.file_id);
|
|
|
|
let target_range = range(&line_index, target.full_range);
|
2020-05-10 11:55:24 +00:00
|
|
|
let target_selection_range =
|
2020-07-17 10:42:48 +00:00
|
|
|
target.focus_range.map(|it| range(&line_index, it)).unwrap_or(target_range);
|
2020-05-10 11:55:24 +00:00
|
|
|
Ok((target_uri, target_range, target_selection_range))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn goto_definition_response(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-25 13:55:25 +00:00
|
|
|
src: Option<FileRange>,
|
2020-05-10 11:55:24 +00:00
|
|
|
targets: Vec<NavigationTarget>,
|
|
|
|
) -> Result<lsp_types::GotoDefinitionResponse> {
|
2021-01-05 13:57:05 +00:00
|
|
|
if snap.config.location_link() {
|
2020-05-10 11:55:24 +00:00
|
|
|
let links = targets
|
|
|
|
.into_iter()
|
2020-06-03 09:16:08 +00:00
|
|
|
.map(|nav| location_link(snap, src, nav))
|
2020-05-10 11:55:24 +00:00
|
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
Ok(links.into())
|
|
|
|
} else {
|
|
|
|
let locations = targets
|
|
|
|
.into_iter()
|
|
|
|
.map(|nav| {
|
2020-07-17 10:42:48 +00:00
|
|
|
location(snap, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
|
2020-05-10 11:55:24 +00:00
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
Ok(locations.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-18 22:14:21 +00:00
|
|
|
fn outside_workspace_annotation_id() -> String {
|
|
|
|
String::from("OutsideWorkspace")
|
2021-04-16 15:31:47 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 22:11:40 +00:00
|
|
|
pub(crate) fn snippet_text_document_edit(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-17 22:11:40 +00:00
|
|
|
is_snippet: bool,
|
2021-01-14 17:35:22 +00:00
|
|
|
file_id: FileId,
|
|
|
|
edit: TextEdit,
|
2020-05-17 22:11:40 +00:00
|
|
|
) -> Result<lsp_ext::SnippetTextDocumentEdit> {
|
2021-01-14 17:35:22 +00:00
|
|
|
let text_document = optional_versioned_text_document_identifier(snap, file_id);
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(file_id)?;
|
2021-04-18 22:14:21 +00:00
|
|
|
let mut edits: Vec<_> =
|
|
|
|
edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
|
|
|
|
|
|
|
|
if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() {
|
|
|
|
for edit in &mut edits {
|
|
|
|
edit.annotation_id = Some(outside_workspace_annotation_id())
|
|
|
|
}
|
|
|
|
}
|
2020-05-17 22:11:40 +00:00
|
|
|
Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 13:09:55 +00:00
|
|
|
pub(crate) fn snippet_text_document_ops(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-10 11:55:24 +00:00
|
|
|
file_system_edit: FileSystemEdit,
|
2021-05-17 17:07:10 +00:00
|
|
|
) -> Cancellable<Vec<lsp_ext::SnippetDocumentChangeOperation>> {
|
2020-12-17 13:09:55 +00:00
|
|
|
let mut ops = Vec::new();
|
2020-06-13 09:00:06 +00:00
|
|
|
match file_system_edit {
|
2020-12-17 13:09:55 +00:00
|
|
|
FileSystemEdit::CreateFile { dst, initial_contents } => {
|
2020-12-09 16:01:15 +00:00
|
|
|
let uri = snap.anchored_path(&dst);
|
2020-12-17 13:09:55 +00:00
|
|
|
let create_file = lsp_types::ResourceOp::Create(lsp_types::CreateFile {
|
|
|
|
uri: uri.clone(),
|
2020-11-24 13:52:24 +00:00
|
|
|
options: None,
|
2020-12-09 01:56:43 +00:00
|
|
|
annotation_id: None,
|
2020-12-17 13:09:55 +00:00
|
|
|
});
|
|
|
|
ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(create_file));
|
|
|
|
if !initial_contents.is_empty() {
|
|
|
|
let text_document =
|
|
|
|
lsp_types::OptionalVersionedTextDocumentIdentifier { uri, version: None };
|
|
|
|
let text_edit = lsp_ext::SnippetTextEdit {
|
2021-02-12 21:44:28 +00:00
|
|
|
range: lsp_types::Range::default(),
|
2020-12-17 13:09:55 +00:00
|
|
|
new_text: initial_contents,
|
2021-10-14 06:37:57 +00:00
|
|
|
insert_text_format: Some(lsp_types::InsertTextFormat::PLAIN_TEXT),
|
2021-04-16 15:31:47 +00:00
|
|
|
annotation_id: None,
|
2020-12-17 13:09:55 +00:00
|
|
|
};
|
|
|
|
let edit_file =
|
|
|
|
lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
|
|
|
|
ops.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit_file));
|
|
|
|
}
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
2020-12-09 16:01:15 +00:00
|
|
|
FileSystemEdit::MoveFile { src, dst } => {
|
2020-06-13 09:00:06 +00:00
|
|
|
let old_uri = snap.file_id_to_url(src);
|
2020-12-09 16:01:15 +00:00
|
|
|
let new_uri = snap.anchored_path(&dst);
|
2021-04-18 22:14:21 +00:00
|
|
|
let mut rename_file =
|
|
|
|
lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
|
2021-05-17 17:07:10 +00:00
|
|
|
if snap.analysis.is_library_file(src).ok() == Some(true)
|
2021-04-18 22:14:21 +00:00
|
|
|
&& snap.config.change_annotation_support()
|
|
|
|
{
|
|
|
|
rename_file.annotation_id = Some(outside_workspace_annotation_id())
|
|
|
|
}
|
|
|
|
ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
|
|
|
|
rename_file,
|
|
|
|
)))
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
2020-06-13 09:00:06 +00:00
|
|
|
}
|
2021-04-18 22:14:21 +00:00
|
|
|
Ok(ops)
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 22:11:40 +00:00
|
|
|
pub(crate) fn snippet_workspace_edit(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-17 22:11:40 +00:00
|
|
|
source_change: SourceChange,
|
|
|
|
) -> Result<lsp_ext::SnippetWorkspaceEdit> {
|
|
|
|
let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
|
2021-04-16 15:31:47 +00:00
|
|
|
|
2020-05-10 11:55:24 +00:00
|
|
|
for op in source_change.file_system_edits {
|
2021-04-18 22:14:21 +00:00
|
|
|
let ops = snippet_text_document_ops(snap, op)?;
|
2020-12-17 13:09:55 +00:00
|
|
|
document_changes.extend_from_slice(&ops);
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
2021-01-14 21:43:36 +00:00
|
|
|
for (file_id, edit) in source_change.source_file_edits {
|
2021-06-13 03:54:16 +00:00
|
|
|
let edit = snippet_text_document_edit(snap, source_change.is_snippet, file_id, edit)?;
|
2020-05-17 22:11:40 +00:00
|
|
|
document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
|
|
|
|
}
|
2021-04-18 22:14:21 +00:00
|
|
|
let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
|
2021-04-16 15:31:47 +00:00
|
|
|
changes: None,
|
|
|
|
document_changes: Some(document_changes),
|
2021-04-18 22:14:21 +00:00
|
|
|
change_annotations: None,
|
2021-04-16 15:31:47 +00:00
|
|
|
};
|
2021-04-18 22:14:21 +00:00
|
|
|
if snap.config.change_annotation_support() {
|
|
|
|
workspace_edit.change_annotations = Some(
|
|
|
|
once((
|
|
|
|
outside_workspace_annotation_id(),
|
|
|
|
lsp_types::ChangeAnnotation {
|
|
|
|
label: String::from("Edit outside of the workspace"),
|
|
|
|
needs_confirmation: Some(true),
|
|
|
|
description: Some(String::from(
|
|
|
|
"This edit lies outside of the workspace and may affect dependencies",
|
|
|
|
)),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
}
|
2020-05-17 22:11:40 +00:00
|
|
|
Ok(workspace_edit)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn workspace_edit(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-17 22:11:40 +00:00
|
|
|
source_change: SourceChange,
|
|
|
|
) -> Result<lsp_types::WorkspaceEdit> {
|
|
|
|
assert!(!source_change.is_snippet);
|
2020-06-03 09:16:08 +00:00
|
|
|
snippet_workspace_edit(snap, source_change).map(|it| it.into())
|
2020-05-17 22:11:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
|
|
|
|
fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit {
|
|
|
|
lsp_types::WorkspaceEdit {
|
|
|
|
changes: None,
|
|
|
|
document_changes: snippet_workspace_edit.document_changes.map(|changes| {
|
|
|
|
lsp_types::DocumentChanges::Operations(
|
|
|
|
changes
|
|
|
|
.into_iter()
|
|
|
|
.map(|change| match change {
|
|
|
|
lsp_ext::SnippetDocumentChangeOperation::Op(op) => {
|
|
|
|
lsp_types::DocumentChangeOperation::Op(op)
|
|
|
|
}
|
|
|
|
lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => {
|
|
|
|
lsp_types::DocumentChangeOperation::Edit(
|
|
|
|
lsp_types::TextDocumentEdit {
|
|
|
|
text_document: edit.text_document,
|
2021-04-16 15:31:47 +00:00
|
|
|
edits: edit.edits.into_iter().map(From::from).collect(),
|
2020-05-17 22:11:40 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
}),
|
2021-04-16 15:31:47 +00:00
|
|
|
change_annotations: snippet_workspace_edit.change_annotations,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<lsp_ext::SnippetTextEdit>
|
|
|
|
for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit>
|
|
|
|
{
|
|
|
|
fn from(
|
|
|
|
lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit,
|
|
|
|
) -> Self {
|
|
|
|
match annotation_id {
|
|
|
|
Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit {
|
|
|
|
text_edit: lsp_types::TextEdit { range, new_text },
|
|
|
|
annotation_id,
|
|
|
|
}),
|
|
|
|
None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }),
|
2020-05-17 22:11:40 +00:00
|
|
|
}
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 09:00:06 +00:00
|
|
|
pub(crate) fn call_hierarchy_item(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-05-10 11:55:24 +00:00
|
|
|
target: NavigationTarget,
|
|
|
|
) -> Result<lsp_types::CallHierarchyItem> {
|
2020-07-17 10:42:48 +00:00
|
|
|
let name = target.name.to_string();
|
|
|
|
let detail = target.description.clone();
|
2021-10-14 06:37:57 +00:00
|
|
|
let kind = target.kind.map(symbol_kind).unwrap_or(lsp_types::SymbolKind::FUNCTION);
|
2020-06-03 09:16:08 +00:00
|
|
|
let (uri, range, selection_range) = location_info(snap, target)?;
|
2020-10-21 18:57:55 +00:00
|
|
|
Ok(lsp_types::CallHierarchyItem {
|
|
|
|
name,
|
|
|
|
kind,
|
|
|
|
tags: None,
|
|
|
|
detail,
|
|
|
|
uri,
|
|
|
|
range,
|
|
|
|
selection_range,
|
|
|
|
data: None,
|
|
|
|
})
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
|
|
|
|
2020-07-11 21:29:45 +00:00
|
|
|
pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind {
|
2020-06-28 22:36:05 +00:00
|
|
|
match kind {
|
2020-07-11 21:29:45 +00:00
|
|
|
AssistKind::None | AssistKind::Generate => lsp_types::CodeActionKind::EMPTY,
|
|
|
|
AssistKind::QuickFix => lsp_types::CodeActionKind::QUICKFIX,
|
|
|
|
AssistKind::Refactor => lsp_types::CodeActionKind::REFACTOR,
|
|
|
|
AssistKind::RefactorExtract => lsp_types::CodeActionKind::REFACTOR_EXTRACT,
|
|
|
|
AssistKind::RefactorInline => lsp_types::CodeActionKind::REFACTOR_INLINE,
|
|
|
|
AssistKind::RefactorRewrite => lsp_types::CodeActionKind::REFACTOR_REWRITE,
|
2020-06-28 22:36:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-12 20:08:56 +00:00
|
|
|
pub(crate) fn code_action(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-06-02 20:21:48 +00:00
|
|
|
assist: Assist,
|
2021-04-12 20:08:56 +00:00
|
|
|
resolve_data: Option<(usize, lsp_types::CodeActionParams)>,
|
2020-06-02 20:21:48 +00:00
|
|
|
) -> Result<lsp_ext::CodeAction> {
|
2021-04-12 20:08:56 +00:00
|
|
|
let mut res = lsp_ext::CodeAction {
|
2020-08-18 14:41:21 +00:00
|
|
|
title: assist.label.to_string(),
|
2021-01-05 13:57:05 +00:00
|
|
|
group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
|
2020-08-17 14:11:29 +00:00
|
|
|
kind: Some(code_action_kind(assist.id.1)),
|
2020-06-02 20:21:48 +00:00
|
|
|
edit: None,
|
2020-07-11 23:12:42 +00:00
|
|
|
is_preferred: None,
|
2020-11-10 17:20:01 +00:00
|
|
|
data: None,
|
|
|
|
};
|
2021-04-12 20:08:56 +00:00
|
|
|
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 {
|
2021-05-03 19:58:53 +00:00
|
|
|
id: format!("{}:{}:{}", assist.id.0, assist.id.1.name(), index),
|
2021-04-12 20:08:56 +00:00
|
|
|
code_action_params,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
(None, None) => {
|
|
|
|
stdx::never!("assist should always be resolved if client can't do lazy resolving")
|
|
|
|
}
|
|
|
|
};
|
2020-11-10 17:20:01 +00:00
|
|
|
Ok(res)
|
2020-05-10 11:55:24 +00:00
|
|
|
}
|
2020-06-02 14:30:26 +00:00
|
|
|
|
|
|
|
pub(crate) fn runnable(
|
2020-06-03 09:16:08 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
2020-06-08 10:56:31 +00:00
|
|
|
runnable: Runnable,
|
2020-06-02 14:30:26 +00:00
|
|
|
) -> Result<lsp_ext::Runnable> {
|
2021-01-06 10:54:28 +00:00
|
|
|
let config = snap.config.runnables();
|
2021-02-27 16:07:14 +00:00
|
|
|
let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?;
|
2020-06-11 09:04:09 +00:00
|
|
|
let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
|
2020-06-02 14:30:26 +00:00
|
|
|
let target = spec.as_ref().map(|s| s.target.clone());
|
2020-06-02 15:22:23 +00:00
|
|
|
let (cargo_args, executable_args) =
|
2020-10-22 17:19:18 +00:00
|
|
|
CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg)?;
|
2020-06-06 09:00:46 +00:00
|
|
|
let label = runnable.label(target);
|
2020-06-08 10:56:31 +00:00
|
|
|
let location = location_link(snap, None, runnable.nav)?;
|
2020-06-02 14:30:26 +00:00
|
|
|
|
|
|
|
Ok(lsp_ext::Runnable {
|
|
|
|
label,
|
2020-06-02 15:22:23 +00:00
|
|
|
location: Some(location),
|
2020-06-02 14:30:26 +00:00
|
|
|
kind: lsp_ext::RunnableKind::Cargo,
|
2020-06-02 15:22:23 +00:00
|
|
|
args: lsp_ext::CargoRunnable {
|
2020-06-24 13:52:07 +00:00
|
|
|
workspace_root: workspace_root.map(|it| it.into()),
|
2021-01-06 10:54:28 +00:00
|
|
|
override_cargo: config.override_cargo,
|
2020-06-02 15:22:23 +00:00
|
|
|
cargo_args,
|
2021-01-06 10:54:28 +00:00
|
|
|
cargo_extra_args: config.cargo_extra_args,
|
2020-06-02 15:22:23 +00:00
|
|
|
executable_args,
|
2020-06-27 15:53:50 +00:00
|
|
|
expect_test: None,
|
2020-06-02 14:30:26 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
2020-06-13 09:00:06 +00:00
|
|
|
|
2021-02-13 11:07:47 +00:00
|
|
|
pub(crate) fn code_lens(
|
2021-07-11 10:34:22 +00:00
|
|
|
acc: &mut Vec<lsp_types::CodeLens>,
|
2021-02-13 11:07:47 +00:00
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
annotation: Annotation,
|
2021-07-11 10:34:22 +00:00
|
|
|
) -> Result<()> {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
let client_commands_config = snap.config.client_commands();
|
2021-02-13 11:07:47 +00:00
|
|
|
match annotation.kind {
|
2021-07-11 10:49:38 +00:00
|
|
|
AnnotationKind::Runnable(run) => {
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(run.nav.file_id)?;
|
2021-02-13 11:07:47 +00:00
|
|
|
let annotation_range = range(&line_index, annotation.range);
|
|
|
|
|
2021-07-01 18:40:31 +00:00
|
|
|
let title = run.title();
|
2021-07-11 10:45:58 +00:00
|
|
|
let can_debug = match run.kind {
|
|
|
|
ide::RunnableKind::DocTest { .. } => false,
|
|
|
|
ide::RunnableKind::TestMod { .. }
|
|
|
|
| ide::RunnableKind::Test { .. }
|
|
|
|
| ide::RunnableKind::Bench { .. }
|
|
|
|
| ide::RunnableKind::Bin => true,
|
|
|
|
};
|
2021-06-13 03:54:16 +00:00
|
|
|
let r = runnable(snap, run)?;
|
2021-02-13 11:07:47 +00:00
|
|
|
|
2021-07-11 10:42:19 +00:00
|
|
|
let lens_config = snap.config.lens();
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
if lens_config.run && client_commands_config.run_single {
|
2021-07-11 10:42:19 +00:00
|
|
|
let command = command::run_single(&r, &title);
|
|
|
|
acc.push(lsp_types::CodeLens {
|
|
|
|
range: annotation_range,
|
|
|
|
command: Some(command),
|
|
|
|
data: None,
|
|
|
|
})
|
|
|
|
}
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
if lens_config.debug && can_debug && client_commands_config.debug_single {
|
2021-07-11 10:42:19 +00:00
|
|
|
let command = command::debug_single(&r);
|
|
|
|
acc.push(lsp_types::CodeLens {
|
|
|
|
range: annotation_range,
|
|
|
|
command: Some(command),
|
|
|
|
data: None,
|
|
|
|
})
|
|
|
|
}
|
2021-02-13 11:07:47 +00:00
|
|
|
}
|
|
|
|
AnnotationKind::HasImpls { position: file_position, data } => {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
if !client_commands_config.show_reference {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(file_position.file_id)?;
|
2021-02-13 11:07:47 +00:00
|
|
|
let annotation_range = range(&line_index, annotation.range);
|
|
|
|
let url = url(snap, file_position.file_id);
|
|
|
|
|
|
|
|
let position = position(&line_index, file_position.offset);
|
|
|
|
|
|
|
|
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
|
|
|
|
|
2021-02-16 15:55:34 +00:00
|
|
|
let doc_pos = lsp_types::TextDocumentPositionParams::new(id, position);
|
2021-02-13 11:07:47 +00:00
|
|
|
|
|
|
|
let goto_params = lsp_types::request::GotoImplementationParams {
|
2021-02-16 15:55:34 +00:00
|
|
|
text_document_position_params: doc_pos,
|
2021-02-13 11:07:47 +00:00
|
|
|
work_done_progress_params: Default::default(),
|
|
|
|
partial_result_params: Default::default(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let command = data.map(|ranges| {
|
|
|
|
let locations: Vec<lsp_types::Location> = ranges
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|target| {
|
|
|
|
location(
|
|
|
|
snap,
|
|
|
|
FileRange { file_id: target.file_id, range: target.full_range },
|
|
|
|
)
|
|
|
|
.ok()
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2021-02-14 16:36:44 +00:00
|
|
|
command::show_references(
|
2021-02-13 11:07:47 +00:00
|
|
|
implementation_title(locations.len()),
|
|
|
|
&url,
|
|
|
|
position,
|
|
|
|
locations,
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
2021-07-11 10:34:22 +00:00
|
|
|
acc.push(lsp_types::CodeLens {
|
2021-02-13 11:07:47 +00:00
|
|
|
range: annotation_range,
|
|
|
|
command,
|
|
|
|
data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
AnnotationKind::HasReferences { position: file_position, data } => {
|
feat: gate custom clint-side commands behind capabilities
Some features of rust-analyzer requires support for custom commands on
the client side. Specifically, hover & code lens need this.
Stock LSP doesn't have a way for the server to know which client-side
commands are available. For that reason, we historically were just
sending the commands, not worrying whether the client supports then or
not.
That's not really great though, so in this PR we add infrastructure for
the client to explicitly opt-into custom commands, via `extensions`
field of the ClientCapabilities.
To preserve backwards compatability, if the client doesn't set the
field, we assume that it does support all custom commands. In the
future, we'll start treating that case as if the client doesn't support
commands.
So, if you maintain a rust-analyzer client and implement
`rust-analyzer/runSingle` and such, please also advertise this via a
capability.
2021-07-30 16:16:33 +00:00
|
|
|
if !client_commands_config.show_reference {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2021-02-12 21:44:28 +00:00
|
|
|
let line_index = snap.file_line_index(file_position.file_id)?;
|
2021-02-13 11:07:47 +00:00
|
|
|
let annotation_range = range(&line_index, annotation.range);
|
|
|
|
let url = url(snap, file_position.file_id);
|
|
|
|
|
|
|
|
let position = position(&line_index, file_position.offset);
|
|
|
|
|
|
|
|
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
|
|
|
|
|
|
|
|
let doc_pos = lsp_types::TextDocumentPositionParams::new(id, position);
|
|
|
|
|
|
|
|
let command = data.map(|ranges| {
|
|
|
|
let locations: Vec<lsp_types::Location> =
|
|
|
|
ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
|
|
|
|
|
2021-02-14 16:36:44 +00:00
|
|
|
command::show_references(
|
|
|
|
reference_title(locations.len()),
|
|
|
|
&url,
|
|
|
|
position,
|
|
|
|
locations,
|
|
|
|
)
|
2021-02-13 11:07:47 +00:00
|
|
|
});
|
|
|
|
|
2021-07-11 10:34:22 +00:00
|
|
|
acc.push(lsp_types::CodeLens {
|
2021-02-13 11:07:47 +00:00
|
|
|
range: annotation_range,
|
|
|
|
command,
|
|
|
|
data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-07-11 10:34:22 +00:00
|
|
|
Ok(())
|
2021-02-13 11:07:47 +00:00
|
|
|
}
|
|
|
|
|
2021-02-14 16:36:44 +00:00
|
|
|
pub(crate) mod command {
|
|
|
|
use ide::{FileRange, NavigationTarget};
|
|
|
|
use serde_json::to_value;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
global_state::GlobalStateSnapshot,
|
|
|
|
lsp_ext,
|
|
|
|
to_proto::{location, location_link},
|
|
|
|
};
|
|
|
|
|
|
|
|
pub(crate) fn show_references(
|
|
|
|
title: String,
|
|
|
|
uri: &lsp_types::Url,
|
|
|
|
position: lsp_types::Position,
|
|
|
|
locations: Vec<lsp_types::Location>,
|
|
|
|
) -> lsp_types::Command {
|
|
|
|
// We cannot use the 'editor.action.showReferences' command directly
|
|
|
|
// because that command requires vscode types which we convert in the handler
|
|
|
|
// on the client side.
|
|
|
|
|
|
|
|
lsp_types::Command {
|
|
|
|
title,
|
|
|
|
command: "rust-analyzer.showReferences".into(),
|
|
|
|
arguments: Some(vec![
|
|
|
|
to_value(uri).unwrap(),
|
|
|
|
to_value(position).unwrap(),
|
|
|
|
to_value(locations).unwrap(),
|
|
|
|
]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn run_single(runnable: &lsp_ext::Runnable, title: &str) -> lsp_types::Command {
|
|
|
|
lsp_types::Command {
|
|
|
|
title: title.to_string(),
|
|
|
|
command: "rust-analyzer.runSingle".into(),
|
|
|
|
arguments: Some(vec![to_value(runnable).unwrap()]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command {
|
|
|
|
lsp_types::Command {
|
|
|
|
title: "Debug".into(),
|
|
|
|
command: "rust-analyzer.debugSingle".into(),
|
|
|
|
arguments: Some(vec![to_value(runnable).unwrap()]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn goto_location(
|
|
|
|
snap: &GlobalStateSnapshot,
|
|
|
|
nav: &NavigationTarget,
|
|
|
|
) -> Option<lsp_types::Command> {
|
|
|
|
let value = if snap.config.location_link() {
|
|
|
|
let link = location_link(snap, None, nav.clone()).ok()?;
|
|
|
|
to_value(link).ok()?
|
|
|
|
} else {
|
|
|
|
let range = FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() };
|
|
|
|
let location = location(snap, range).ok()?;
|
|
|
|
to_value(location).ok()?
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(lsp_types::Command {
|
|
|
|
title: nav.name.to_string(),
|
|
|
|
command: "rust-analyzer.gotoLocation".into(),
|
|
|
|
arguments: Some(vec![value]),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn trigger_parameter_hints() -> lsp_types::Command {
|
|
|
|
lsp_types::Command {
|
|
|
|
title: "triggerParameterHints".into(),
|
|
|
|
command: "editor.action.triggerParameterHints".into(),
|
|
|
|
arguments: None,
|
|
|
|
}
|
2021-02-13 11:07:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn implementation_title(count: usize) -> String {
|
|
|
|
if count == 1 {
|
|
|
|
"1 implementation".into()
|
|
|
|
} else {
|
|
|
|
format!("{} implementations", count)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn reference_title(count: usize) -> String {
|
|
|
|
if count == 1 {
|
|
|
|
"1 reference".into()
|
|
|
|
} else {
|
|
|
|
format!("{} references", count)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-29 15:22:48 +00:00
|
|
|
pub(crate) fn markup_content(
|
|
|
|
markup: Markup,
|
|
|
|
kind: ide::HoverDocFormat,
|
|
|
|
) -> lsp_types::MarkupContent {
|
|
|
|
let kind = match kind {
|
|
|
|
ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
|
|
|
|
ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
|
|
|
|
};
|
2020-08-09 13:33:14 +00:00
|
|
|
let value = crate::markdown::format_docs(markup.as_str());
|
2021-10-29 15:22:48 +00:00
|
|
|
lsp_types::MarkupContent { kind, value }
|
2020-07-08 20:37:35 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 11:09:51 +00:00
|
|
|
pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
|
2021-09-04 09:56:34 +00:00
|
|
|
// This is wrong, but we don't have a better alternative I suppose?
|
|
|
|
// https://github.com/microsoft/language-server-protocol/issues/1341
|
|
|
|
invalid_params_error(err.to_string())
|
2021-01-13 11:09:51 +00:00
|
|
|
}
|
|
|
|
|
2020-06-13 09:00:06 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-02-12 21:44:28 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
2020-08-13 15:42:52 +00:00
|
|
|
use ide::Analysis;
|
2020-06-13 09:00:06 +00:00
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn conv_fold_line_folding_only_fixup() {
|
2020-07-01 16:17:08 +00:00
|
|
|
let text = r#"mod a;
|
2020-06-13 09:00:06 +00:00
|
|
|
mod b;
|
2020-07-01 16:17:08 +00:00
|
|
|
mod c;
|
2020-06-13 09:00:06 +00:00
|
|
|
|
2020-07-01 16:17:08 +00:00
|
|
|
fn main() {
|
|
|
|
if cond {
|
2020-06-13 09:00:06 +00:00
|
|
|
a::do_a();
|
2020-07-01 16:17:08 +00:00
|
|
|
} else {
|
2020-06-13 09:00:06 +00:00
|
|
|
b::do_b();
|
2020-07-01 16:17:08 +00:00
|
|
|
}
|
|
|
|
}"#;
|
|
|
|
|
|
|
|
let (analysis, file_id) = Analysis::from_single_file(text.to_string());
|
|
|
|
let folds = analysis.folding_ranges(file_id).unwrap();
|
|
|
|
assert_eq!(folds.len(), 4);
|
2020-06-13 09:00:06 +00:00
|
|
|
|
2021-02-12 21:55:27 +00:00
|
|
|
let line_index = LineIndex {
|
2021-06-13 03:54:16 +00:00
|
|
|
index: Arc::new(ide::LineIndex::new(text)),
|
2021-02-12 21:55:27 +00:00
|
|
|
endings: LineEndings::Unix,
|
|
|
|
encoding: OffsetEncoding::Utf16,
|
|
|
|
};
|
2020-06-13 09:00:06 +00:00
|
|
|
let converted: Vec<lsp_types::FoldingRange> =
|
2021-06-13 03:54:16 +00:00
|
|
|
folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();
|
2020-06-13 09:00:06 +00:00
|
|
|
|
|
|
|
let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
|
|
|
|
assert_eq!(converted.len(), expected_lines.len());
|
|
|
|
for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) {
|
|
|
|
assert_eq!(folding_range.start_line, *start_line);
|
|
|
|
assert_eq!(folding_range.start_character, None);
|
|
|
|
assert_eq!(folding_range.end_line, *end_line);
|
|
|
|
assert_eq!(folding_range.end_character, None);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// `Url` is not able to parse windows paths on unix machines.
|
|
|
|
#[test]
|
|
|
|
#[cfg(target_os = "windows")]
|
2021-07-17 14:40:13 +00:00
|
|
|
fn test_lowercase_drive_letter() {
|
|
|
|
use std::{convert::TryInto, path::Path};
|
|
|
|
|
|
|
|
let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap());
|
2020-06-13 09:00:06 +00:00
|
|
|
assert_eq!(url.to_string(), "file:///c:/Test");
|
|
|
|
|
2021-07-17 14:40:13 +00:00
|
|
|
let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap());
|
2020-06-13 09:00:06 +00:00
|
|
|
assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
|
|
|
|
}
|
|
|
|
}
|