From 1586bab0b97bef411e6187dfc389557edbc5a16e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 10 May 2020 13:55:24 +0200 Subject: [PATCH 1/5] Simplify proto conversion Trait based infra in conv.rs is significantly more complicated than what we actually need here. --- .../ra_ide/src/display/navigation_target.rs | 9 +- crates/ra_text_edit/src/lib.rs | 1 + crates/rust-analyzer/src/conv.rs | 726 ------------------ crates/rust-analyzer/src/from_proto.rs | 42 + crates/rust-analyzer/src/lib.rs | 4 +- crates/rust-analyzer/src/main_loop.rs | 6 +- .../rust-analyzer/src/main_loop/handlers.rs | 392 +++++----- crates/rust-analyzer/src/to_proto.rs | 566 ++++++++++++++ 8 files changed, 807 insertions(+), 939 deletions(-) delete mode 100644 crates/rust-analyzer/src/conv.rs create mode 100644 crates/rust-analyzer/src/from_proto.rs create mode 100644 crates/rust-analyzer/src/to_proto.rs diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index de35c67114..5da28edd24 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs @@ -11,7 +11,7 @@ use ra_syntax::{ TextRange, }; -use crate::FileSymbol; +use crate::{FileRange, FileSymbol}; use super::short_label::ShortLabel; @@ -22,10 +22,11 @@ use super::short_label::ShortLabel; /// code, like a function or a struct, but this is not strictly required. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct NavigationTarget { + // FIXME: use FileRange? file_id: FileId, + full_range: TextRange, name: SmolStr, kind: SyntaxKind, - full_range: TextRange, focus_range: Option, container_name: Option, description: Option, @@ -63,6 +64,10 @@ impl NavigationTarget { self.file_id } + pub fn file_range(&self) -> FileRange { + FileRange { file_id: self.file_id, range: self.full_range } + } + pub fn full_range(&self) -> TextRange { self.full_range } diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index 3409713ff3..37f77cc47c 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs @@ -75,6 +75,7 @@ impl TextEdit { self.indels.is_empty() } + // FXME: impl IntoIter instead pub fn as_indels(&self) -> &[Indel] { &self.indels } diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs deleted file mode 100644 index f64c90b5b1..0000000000 --- a/crates/rust-analyzer/src/conv.rs +++ /dev/null @@ -1,726 +0,0 @@ -//! Convenience module responsible for translating between rust-analyzer's types -//! and LSP types. - -use lsp_types::{ - self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, - Location, LocationLink, MarkupContent, MarkupKind, ParameterInformation, ParameterLabel, - Position, Range, RenameFile, ResourceOp, SemanticTokenModifier, SemanticTokenType, - SignatureInformation, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem, - TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit, -}; -use ra_ide::{ - translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, - FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, - InlayHint, InlayKind, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, - ReferenceAccess, Severity, SourceChange, SourceFileEdit, -}; -use ra_syntax::{SyntaxKind, TextRange, TextSize}; -use ra_text_edit::{Indel, TextEdit}; -use ra_vfs::LineEndings; - -use crate::{ - req, - semantic_tokens::{self, ModifierSet, CONSTANT, CONTROL_FLOW, MUTABLE, UNSAFE}, - world::WorldSnapshot, - Result, -}; -use semantic_tokens::{ - ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION, - UNRESOLVED_REFERENCE, -}; - -pub trait Conv { - type Output; - fn conv(self) -> Self::Output; -} - -pub trait ConvWith { - type Output; - fn conv_with(self, ctx: CTX) -> Self::Output; -} - -pub trait TryConvWith { - type Output; - fn try_conv_with(self, ctx: CTX) -> Result; -} - -impl Conv for SyntaxKind { - type Output = SymbolKind; - - fn conv(self) -> ::Output { - match self { - SyntaxKind::FN_DEF => SymbolKind::Function, - SyntaxKind::STRUCT_DEF => SymbolKind::Struct, - SyntaxKind::ENUM_DEF => SymbolKind::Enum, - SyntaxKind::ENUM_VARIANT => SymbolKind::EnumMember, - SyntaxKind::TRAIT_DEF => SymbolKind::Interface, - SyntaxKind::MACRO_CALL => SymbolKind::Function, - SyntaxKind::MODULE => SymbolKind::Module, - SyntaxKind::TYPE_ALIAS_DEF => SymbolKind::TypeParameter, - SyntaxKind::RECORD_FIELD_DEF => SymbolKind::Field, - SyntaxKind::STATIC_DEF => SymbolKind::Constant, - SyntaxKind::CONST_DEF => SymbolKind::Constant, - SyntaxKind::IMPL_DEF => SymbolKind::Object, - _ => SymbolKind::Variable, - } - } -} - -impl Conv for ReferenceAccess { - type Output = ::lsp_types::DocumentHighlightKind; - - fn conv(self) -> Self::Output { - use lsp_types::DocumentHighlightKind; - match self { - ReferenceAccess::Read => DocumentHighlightKind::Read, - ReferenceAccess::Write => DocumentHighlightKind::Write, - } - } -} - -impl Conv for CompletionItemKind { - type Output = ::lsp_types::CompletionItemKind; - - fn conv(self) -> ::Output { - use lsp_types::CompletionItemKind::*; - match self { - CompletionItemKind::Keyword => Keyword, - CompletionItemKind::Snippet => Snippet, - CompletionItemKind::Module => Module, - CompletionItemKind::Function => Function, - CompletionItemKind::Struct => Struct, - CompletionItemKind::Enum => Enum, - CompletionItemKind::EnumVariant => EnumMember, - CompletionItemKind::BuiltinType => Struct, - CompletionItemKind::Binding => Variable, - CompletionItemKind::Field => Field, - CompletionItemKind::Trait => Interface, - CompletionItemKind::TypeAlias => Struct, - CompletionItemKind::Const => Constant, - CompletionItemKind::Static => Value, - CompletionItemKind::Method => Method, - CompletionItemKind::TypeParam => TypeParameter, - CompletionItemKind::Macro => Method, - CompletionItemKind::Attribute => EnumMember, - } - } -} - -impl Conv for Severity { - type Output = DiagnosticSeverity; - fn conv(self) -> DiagnosticSeverity { - match self { - Severity::Error => DiagnosticSeverity::Error, - Severity::WeakWarning => DiagnosticSeverity::Hint, - } - } -} - -impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { - type Output = ::lsp_types::CompletionItem; - - fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> ::lsp_types::CompletionItem { - let mut additional_text_edits = Vec::new(); - let mut text_edit = None; - // LSP does not allow arbitrary edits in completion, so we have to do a - // non-trivial mapping here. - for indel in self.text_edit().as_indels() { - if indel.delete.contains_range(self.source_range()) { - text_edit = Some(if indel.delete == self.source_range() { - indel.conv_with((ctx.0, ctx.1)) - } else { - assert!(self.source_range().end() == indel.delete.end()); - let range1 = TextRange::new(indel.delete.start(), self.source_range().start()); - let range2 = self.source_range(); - let edit1 = Indel::replace(range1, String::new()); - let edit2 = Indel::replace(range2, indel.insert.clone()); - additional_text_edits.push(edit1.conv_with((ctx.0, ctx.1))); - edit2.conv_with((ctx.0, ctx.1)) - }) - } else { - assert!(self.source_range().intersect(indel.delete).is_none()); - additional_text_edits.push(indel.conv_with((ctx.0, ctx.1))); - } - } - let text_edit = text_edit.unwrap(); - - let mut res = lsp_types::CompletionItem { - label: self.label().to_string(), - detail: self.detail().map(|it| it.to_string()), - filter_text: Some(self.lookup().to_string()), - kind: self.kind().map(|it| it.conv()), - text_edit: Some(text_edit.into()), - additional_text_edits: Some(additional_text_edits), - documentation: self.documentation().map(|it| it.conv()), - deprecated: Some(self.deprecated()), - command: if self.trigger_call_info() { - let cmd = lsp_types::Command { - title: "triggerParameterHints".into(), - command: "editor.action.triggerParameterHints".into(), - arguments: None, - }; - Some(cmd) - } else { - None - }, - ..Default::default() - }; - - if self.score().is_some() { - res.preselect = Some(true) - } - - if self.deprecated() { - res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) - } - - res.insert_text_format = Some(match self.insert_text_format() { - InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, - InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, - }); - - res - } -} - -impl ConvWith<&LineIndex> for Position { - type Output = TextSize; - - fn conv_with(self, line_index: &LineIndex) -> TextSize { - let line_col = LineCol { line: self.line as u32, col_utf16: self.character as u32 }; - line_index.offset(line_col) - } -} - -impl ConvWith<&LineIndex> for TextSize { - type Output = Position; - - fn conv_with(self, line_index: &LineIndex) -> Position { - let line_col = line_index.line_col(self); - Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)) - } -} - -impl ConvWith<&LineIndex> for TextRange { - type Output = Range; - - fn conv_with(self, line_index: &LineIndex) -> Range { - Range::new(self.start().conv_with(line_index), self.end().conv_with(line_index)) - } -} - -impl ConvWith<&LineIndex> for Range { - type Output = TextRange; - - fn conv_with(self, line_index: &LineIndex) -> TextRange { - TextRange::new(self.start.conv_with(line_index), self.end.conv_with(line_index)) - } -} - -impl Conv for ra_ide::Documentation { - type Output = lsp_types::Documentation; - fn conv(self) -> Documentation { - Documentation::MarkupContent(MarkupContent { - kind: MarkupKind::Markdown, - value: crate::markdown::format_docs(self.as_str()), - }) - } -} - -impl ConvWith for ra_ide::FunctionSignature { - type Output = lsp_types::SignatureInformation; - fn conv_with(self, concise: bool) -> Self::Output { - let (label, documentation, params) = if concise { - let mut params = self.parameters; - if self.has_self_param { - params.remove(0); - } - (params.join(", "), None, params) - } else { - (self.to_string(), self.doc.map(|it| it.conv()), self.parameters) - }; - - let parameters: Vec = params - .into_iter() - .map(|param| ParameterInformation { - label: ParameterLabel::Simple(param), - documentation: None, - }) - .collect(); - - SignatureInformation { label, documentation, parameters: Some(parameters) } - } -} - -impl ConvWith<(&LineIndex, LineEndings)> for TextEdit { - type Output = Vec; - - fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec { - self.as_indels().iter().map_conv_with(ctx).collect() - } -} - -impl ConvWith<(&LineIndex, LineEndings)> for &Indel { - type Output = lsp_types::TextEdit; - - fn conv_with( - self, - (line_index, line_endings): (&LineIndex, LineEndings), - ) -> lsp_types::TextEdit { - let mut new_text = self.insert.clone(); - if line_endings == LineEndings::Dos { - new_text = new_text.replace('\n', "\r\n"); - } - lsp_types::TextEdit { range: self.delete.conv_with(line_index), new_text } - } -} - -pub(crate) struct FoldConvCtx<'a> { - pub(crate) text: &'a str, - pub(crate) line_index: &'a LineIndex, - pub(crate) line_folding_only: bool, -} - -impl ConvWith<&FoldConvCtx<'_>> for Fold { - type Output = lsp_types::FoldingRange; - - fn conv_with(self, ctx: &FoldConvCtx) -> lsp_types::FoldingRange { - let kind = match self.kind { - FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), - FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), - FoldKind::Mods => None, - FoldKind::Block => None, - }; - - let range = self.range.conv_with(&ctx.line_index); - - if ctx.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 = ctx.text - [TextRange::new(self.range.end(), TextSize::of(ctx.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, - } - } - } -} - -impl ConvWith<&LineIndex> for InlayHint { - type Output = req::InlayHint; - fn conv_with(self, line_index: &LineIndex) -> Self::Output { - req::InlayHint { - label: self.label.to_string(), - range: self.range.conv_with(line_index), - kind: match self.kind { - InlayKind::ParameterHint => req::InlayKind::ParameterHint, - InlayKind::TypeHint => req::InlayKind::TypeHint, - InlayKind::ChainingHint => req::InlayKind::ChainingHint, - }, - } - } -} - -impl Conv for Highlight { - type Output = (u32, u32); - - fn conv(self) -> Self::Output { - let mut mods = ModifierSet::default(); - let type_ = match self.tag { - HighlightTag::Struct => SemanticTokenType::STRUCT, - HighlightTag::Enum => SemanticTokenType::ENUM, - HighlightTag::Union => UNION, - HighlightTag::TypeAlias => TYPE_ALIAS, - HighlightTag::Trait => SemanticTokenType::INTERFACE, - HighlightTag::BuiltinType => BUILTIN_TYPE, - HighlightTag::SelfType => SemanticTokenType::TYPE, - HighlightTag::Field => SemanticTokenType::MEMBER, - HighlightTag::Function => SemanticTokenType::FUNCTION, - HighlightTag::Module => SemanticTokenType::NAMESPACE, - HighlightTag::Constant => { - mods |= CONSTANT; - mods |= SemanticTokenModifier::STATIC; - SemanticTokenType::VARIABLE - } - HighlightTag::Static => { - mods |= SemanticTokenModifier::STATIC; - SemanticTokenType::VARIABLE - } - HighlightTag::EnumVariant => ENUM_MEMBER, - HighlightTag::Macro => SemanticTokenType::MACRO, - HighlightTag::Local => SemanticTokenType::VARIABLE, - HighlightTag::TypeParam => SemanticTokenType::TYPE_PARAMETER, - HighlightTag::Lifetime => LIFETIME, - HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => SemanticTokenType::NUMBER, - HighlightTag::CharLiteral | HighlightTag::StringLiteral => SemanticTokenType::STRING, - HighlightTag::Comment => SemanticTokenType::COMMENT, - HighlightTag::Attribute => ATTRIBUTE, - HighlightTag::Keyword => SemanticTokenType::KEYWORD, - HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE, - HighlightTag::FormatSpecifier => FORMAT_SPECIFIER, - }; - - for modifier in self.modifiers.iter() { - let modifier = match modifier { - HighlightModifier::Definition => SemanticTokenModifier::DECLARATION, - HighlightModifier::ControlFlow => CONTROL_FLOW, - HighlightModifier::Mutable => MUTABLE, - HighlightModifier::Unsafe => UNSAFE, - }; - mods |= modifier; - } - - (semantic_tokens::type_index(type_), mods.0) - } -} - -impl, CTX> ConvWith for Option { - type Output = Option; - - fn conv_with(self, ctx: CTX) -> Self::Output { - self.map(|x| ConvWith::conv_with(x, ctx)) - } -} - -impl TryConvWith<&WorldSnapshot> for &Url { - type Output = FileId; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - world.uri_to_file_id(self) - } -} - -impl TryConvWith<&WorldSnapshot> for FileId { - type Output = Url; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - world.file_id_to_uri(self) - } -} - -impl TryConvWith<&WorldSnapshot> for &TextDocumentItem { - type Output = FileId; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - self.uri.try_conv_with(world) - } -} - -impl TryConvWith<&WorldSnapshot> for &VersionedTextDocumentIdentifier { - type Output = FileId; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - self.uri.try_conv_with(world) - } -} - -impl TryConvWith<&WorldSnapshot> for &TextDocumentIdentifier { - type Output = FileId; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - world.uri_to_file_id(&self.uri) - } -} - -impl TryConvWith<&WorldSnapshot> for &TextDocumentPositionParams { - type Output = FilePosition; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let file_id = self.text_document.try_conv_with(world)?; - let line_index = world.analysis().file_line_index(file_id)?; - let offset = self.position.conv_with(&line_index); - Ok(FilePosition { file_id, offset }) - } -} - -impl TryConvWith<&WorldSnapshot> for (&TextDocumentIdentifier, Range) { - type Output = FileRange; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let file_id = self.0.try_conv_with(world)?; - let line_index = world.analysis().file_line_index(file_id)?; - let range = self.1.conv_with(&line_index); - Ok(FileRange { file_id, range }) - } -} - -impl, CTX: Copy> TryConvWith for Vec { - type Output = Vec<>::Output>; - fn try_conv_with(self, ctx: CTX) -> Result { - let mut res = Vec::with_capacity(self.len()); - for item in self { - res.push(item.try_conv_with(ctx)?); - } - Ok(res) - } -} - -impl TryConvWith<&WorldSnapshot> for SourceChange { - type Output = req::SourceChange; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let cursor_position = match self.cursor_position { - None => None, - Some(pos) => { - let line_index = world.analysis().file_line_index(pos.file_id)?; - let edit = self - .source_file_edits - .iter() - .find(|it| it.file_id == pos.file_id) - .map(|it| &it.edit); - let line_col = match edit { - Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), - None => line_index.line_col(pos.offset), - }; - let position = - Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); - Some(TextDocumentPositionParams { - text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), - position, - }) - } - }; - let mut document_changes: Vec = Vec::new(); - for resource_op in self.file_system_edits.try_conv_with(world)? { - document_changes.push(DocumentChangeOperation::Op(resource_op)); - } - for text_document_edit in self.source_file_edits.try_conv_with(world)? { - document_changes.push(DocumentChangeOperation::Edit(text_document_edit)); - } - let workspace_edit = WorkspaceEdit { - changes: None, - document_changes: Some(DocumentChanges::Operations(document_changes)), - }; - Ok(req::SourceChange { label: self.label, workspace_edit, cursor_position }) - } -} - -impl TryConvWith<&WorldSnapshot> for SourceFileEdit { - type Output = TextDocumentEdit; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let text_document = VersionedTextDocumentIdentifier { - uri: self.file_id.try_conv_with(world)?, - version: None, - }; - let line_index = world.analysis().file_line_index(self.file_id)?; - let line_endings = world.file_line_endings(self.file_id); - let edits = - self.edit.as_indels().iter().map_conv_with((&line_index, line_endings)).collect(); - Ok(TextDocumentEdit { text_document, edits }) - } -} - -impl TryConvWith<&WorldSnapshot> for FileSystemEdit { - type Output = ResourceOp; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let res = match self { - FileSystemEdit::CreateFile { source_root, path } => { - let uri = world.path_to_uri(source_root, &path)?; - ResourceOp::Create(CreateFile { uri, options: None }) - } - FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { - let old_uri = world.file_id_to_uri(src)?; - let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; - ResourceOp::Rename(RenameFile { old_uri, new_uri, options: None }) - } - }; - Ok(res) - } -} - -impl TryConvWith<&WorldSnapshot> for &NavigationTarget { - type Output = Location; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let line_index = world.analysis().file_line_index(self.file_id())?; - let range = self.range(); - to_location(self.file_id(), range, &world, &line_index) - } -} - -impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo) { - type Output = LocationLink; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let (src_file_id, target) = self; - - let target_uri = target.info.file_id().try_conv_with(world)?; - let src_line_index = world.analysis().file_line_index(src_file_id)?; - let tgt_line_index = world.analysis().file_line_index(target.info.file_id())?; - - let target_range = target.info.full_range().conv_with(&tgt_line_index); - - let target_selection_range = target - .info - .focus_range() - .map(|it| it.conv_with(&tgt_line_index)) - .unwrap_or(target_range); - - let res = LocationLink { - origin_selection_range: Some(target.range.conv_with(&src_line_index)), - target_uri, - target_range, - target_selection_range, - }; - Ok(res) - } -} - -impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo>) { - type Output = req::GotoDefinitionResponse; - fn try_conv_with(self, world: &WorldSnapshot) -> Result { - let (file_id, RangeInfo { range, info: navs }) = self; - let links = navs - .into_iter() - .map(|nav| (file_id, RangeInfo::new(range, nav))) - .try_conv_with_to_vec(world)?; - if world.config.client_caps.location_link { - Ok(links.into()) - } else { - let locations: Vec = links - .into_iter() - .map(|link| Location { uri: link.target_uri, range: link.target_selection_range }) - .collect(); - Ok(locations.into()) - } - } -} - -pub fn to_call_hierarchy_item( - file_id: FileId, - range: TextRange, - world: &WorldSnapshot, - line_index: &LineIndex, - nav: NavigationTarget, -) -> Result { - Ok(lsp_types::CallHierarchyItem { - name: nav.name().to_string(), - kind: nav.kind().conv(), - tags: None, - detail: nav.description().map(|it| it.to_string()), - uri: file_id.try_conv_with(&world)?, - range: nav.range().conv_with(&line_index), - selection_range: range.conv_with(&line_index), - }) -} - -pub fn to_location( - file_id: FileId, - range: TextRange, - world: &WorldSnapshot, - line_index: &LineIndex, -) -> Result { - let url = file_id.try_conv_with(world)?; - let loc = Location::new(url, range.conv_with(line_index)); - Ok(loc) -} - -pub trait MapConvWith: Sized { - type Output; - - fn map_conv_with(self, ctx: CTX) -> ConvWithIter { - ConvWithIter { iter: self, ctx } - } -} - -impl MapConvWith for I -where - I: Iterator, - I::Item: ConvWith, -{ - type Output = >::Output; -} - -pub struct ConvWithIter { - iter: I, - ctx: CTX, -} - -impl Iterator for ConvWithIter -where - I: Iterator, - I::Item: ConvWith, - CTX: Copy, -{ - type Item = >::Output; - - fn next(&mut self) -> Option { - self.iter.next().map(|item| item.conv_with(self.ctx)) - } -} - -pub trait TryConvWithToVec: Sized { - type Output; - - fn try_conv_with_to_vec(self, ctx: CTX) -> Result>; -} - -impl TryConvWithToVec for I -where - I: Iterator, - I::Item: TryConvWith, - CTX: Copy, -{ - type Output = >::Output; - - fn try_conv_with_to_vec(self, ctx: CTX) -> Result> { - self.map(|it| it.try_conv_with(ctx)).collect() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use test_utils::extract_ranges; - - #[test] - fn conv_fold_line_folding_only_fixup() { - let text = r#"mod a; -mod b; -mod c; - -fn main() { - if cond { - a::do_a(); - } else { - b::do_b(); - } -}"#; - - let (ranges, text) = extract_ranges(text, "fold"); - assert_eq!(ranges.len(), 4); - let folds = vec![ - Fold { range: ranges[0], kind: FoldKind::Mods }, - Fold { range: ranges[1], kind: FoldKind::Block }, - Fold { range: ranges[2], kind: FoldKind::Block }, - Fold { range: ranges[3], kind: FoldKind::Block }, - ]; - - let line_index = LineIndex::new(&text); - let ctx = FoldConvCtx { text: &text, line_index: &line_index, line_folding_only: true }; - let converted: Vec<_> = folds.into_iter().map_conv_with(&ctx).collect(); - - 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); - } - } -} diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs new file mode 100644 index 0000000000..4bb16a496c --- /dev/null +++ b/crates/rust-analyzer/src/from_proto.rs @@ -0,0 +1,42 @@ +//! Conversion lsp_types types to rust-analyzer specific ones. +use ra_db::{FileId, FilePosition, FileRange}; +use ra_ide::{LineCol, LineIndex}; +use ra_syntax::{TextRange, TextSize}; + +use crate::{world::WorldSnapshot, Result}; + +pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { + let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; + line_index.offset(line_col) +} + +pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> TextRange { + let start = offset(line_index, range.start); + let end = offset(line_index, range.end); + TextRange::new(start, end) +} + +pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result { + world.uri_to_file_id(url) +} + +pub(crate) fn file_position( + world: &WorldSnapshot, + tdpp: lsp_types::TextDocumentPositionParams, +) -> Result { + let file_id = file_id(world, &tdpp.text_document.uri)?; + let line_index = world.analysis().file_line_index(file_id)?; + let offset = offset(&*line_index, tdpp.position); + Ok(FilePosition { file_id, offset }) +} + +pub(crate) fn file_range( + world: &WorldSnapshot, + text_document_identifier: lsp_types::TextDocumentIdentifier, + range: lsp_types::Range, +) -> Result { + let file_id = file_id(world, &text_document_identifier.uri)?; + let line_index = world.analysis().file_line_index(file_id)?; + let range = text_range(&line_index, range); + Ok(FileRange { file_id, range }) +} diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 036bf62a73..6d719108c3 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -20,9 +20,11 @@ macro_rules! eprintln { mod vfs_glob; mod caps; mod cargo_target_spec; -mod conv; +mod to_proto; +mod from_proto; mod main_loop; mod markdown; +// TODO: rename to lsp_ext pub mod req; pub mod config; mod world; diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 17b0b95b9d..e27d85dc92 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -37,8 +37,8 @@ use threadpool::ThreadPool; use crate::{ config::{Config, FilesWatcher}, - conv::{ConvWith, TryConvWith}, diagnostics::DiagnosticTask, + from_proto, main_loop::{ pending_requests::{PendingRequest, PendingRequests}, subscriptions::Subscriptions, @@ -584,7 +584,7 @@ fn on_notification( Ok(params) => { let DidChangeTextDocumentParams { text_document, content_changes } = params; let world = state.snapshot(); - let file_id = text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, &text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; let uri = text_document.uri; let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; @@ -694,7 +694,7 @@ fn apply_document_changes( line_index = Cow::Owned(LineIndex::new(&old_text)); } index_valid = IndexValid::UpToLineExclusive(range.start.line); - let range = range.conv_with(&line_index); + let range = from_proto::text_range(&line_index, range); let mut text = old_text.to_owned(); match std::panic::catch_unwind(move || { text.replace_range(Range::::from(range), &change.text); diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index f4353af647..daa5b4411a 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -22,6 +22,7 @@ use ra_ide::{ Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, }; use ra_prof::profile; +use ra_project_model::TargetKind; use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; @@ -31,18 +32,14 @@ use stdx::format_to; use crate::{ cargo_target_spec::CargoTargetSpec, config::RustfmtConfig, - conv::{ - to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, - TryConvWithToVec, - }, diagnostics::DiagnosticTask, - from_json, + from_json, from_proto, req::{self, InlayHint, InlayHintsParams}, semantic_tokens::SemanticTokensBuilder, + to_proto, world::WorldSnapshot, LspError, Result, }; -use ra_project_model::TargetKind; pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { let _p = profile("handle_analyzer_status"); @@ -58,9 +55,9 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result { let _p = profile("handle_syntax_tree"); - let id = params.text_document.try_conv_with(&world)?; + let id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(id)?; - let text_range = params.range.map(|p| p.conv_with(&line_index)); + let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); let res = world.analysis().syntax_tree(id, text_range)?; Ok(res) } @@ -70,9 +67,9 @@ pub fn handle_expand_macro( params: req::ExpandMacroParams, ) -> Result> { let _p = profile("handle_expand_macro"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; - let offset = params.position.map(|p| p.conv_with(&line_index)); + let offset = params.position.map(|p| from_proto::offset(&line_index, p)); match offset { None => Ok(None), @@ -88,16 +85,16 @@ pub fn handle_selection_range( params: req::SelectionRangeParams, ) -> Result>> { let _p = profile("handle_selection_range"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; let res: Result> = params .positions .into_iter() - .map_conv_with(&line_index) .map(|position| { + let offset = from_proto::offset(&line_index, position); let mut ranges = Vec::new(); { - let mut range = TextRange::new(position, position); + let mut range = TextRange::new(offset, offset); loop { ranges.push(range); let frange = FileRange { file_id, range }; @@ -110,12 +107,12 @@ pub fn handle_selection_range( } } let mut range = req::SelectionRange { - range: ranges.last().unwrap().conv_with(&line_index), + range: to_proto::range(&line_index, *ranges.last().unwrap()), parent: None, }; - for r in ranges.iter().rev().skip(1) { + for &r in ranges.iter().rev().skip(1) { range = req::SelectionRange { - range: r.conv_with(&line_index), + range: to_proto::range(&line_index, r), parent: Some(Box::new(range)), } } @@ -131,22 +128,19 @@ pub fn handle_find_matching_brace( params: req::FindMatchingBraceParams, ) -> Result> { let _p = profile("handle_find_matching_brace"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; let res = params .offsets .into_iter() - .map_conv_with(&line_index) - .map(|offset| { - if let Ok(Some(matching_brace_offset)) = - world.analysis().matching_brace(FilePosition { file_id, offset }) - { - matching_brace_offset - } else { - offset - } + .map(|position| { + let offset = from_proto::offset(&line_index, position); + let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { + Ok(Some(matching_brace_offset)) => matching_brace_offset, + Err(_) | Ok(None) => offset, + }; + to_proto::position(&line_index, offset) }) - .map_conv_with(&line_index) .collect(); Ok(res) } @@ -156,8 +150,9 @@ pub fn handle_join_lines( params: req::JoinLinesParams, ) -> Result { let _p = profile("handle_join_lines"); - let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; - world.analysis().join_lines(frange)?.try_conv_with(&world) + let frange = from_proto::file_range(&world, params.text_document, params.range)?; + let source_change = world.analysis().join_lines(frange)?; + to_proto::source_change(&world, source_change) } pub fn handle_on_enter( @@ -165,10 +160,10 @@ pub fn handle_on_enter( params: req::TextDocumentPositionParams, ) -> Result> { let _p = profile("handle_on_enter"); - let position = params.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params)?; match world.analysis().on_enter(position)? { None => Ok(None), - Some(edit) => Ok(Some(edit.try_conv_with(&world)?)), + Some(source_change) => to_proto::source_change(&world, source_change).map(Some), } } @@ -178,7 +173,7 @@ pub fn handle_on_type_formatting( params: req::DocumentOnTypeFormattingParams, ) -> Result>> { let _p = profile("handle_on_type_formatting"); - let mut position = params.text_document_position.try_conv_with(&world)?; + let mut position = from_proto::file_position(&world, params.text_document_position)?; let line_index = world.analysis().file_line_index(position.file_id)?; let line_endings = world.file_line_endings(position.file_id); @@ -208,7 +203,7 @@ pub fn handle_on_type_formatting( // This should be a single-file edit let edit = edit.source_file_edits.pop().unwrap(); - let change: Vec = edit.edit.conv_with((&line_index, line_endings)); + let change = to_proto::text_edit_vec(&line_index, line_endings, edit.edit); Ok(Some(change)) } @@ -217,9 +212,8 @@ pub fn handle_document_symbol( params: req::DocumentSymbolParams, ) -> Result> { let _p = profile("handle_document_symbol"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; - let url = file_id.try_conv_with(&world)?; let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); @@ -227,10 +221,10 @@ pub fn handle_document_symbol( let doc_symbol = DocumentSymbol { name: symbol.label, detail: symbol.detail, - kind: symbol.kind.conv(), + kind: to_proto::symbol_kind(symbol.kind), deprecated: Some(symbol.deprecated), - range: symbol.node_range.conv_with(&line_index), - selection_range: symbol.navigation_range.conv_with(&line_index), + range: to_proto::range(&line_index, symbol.node_range), + selection_range: to_proto::range(&line_index, symbol.navigation_range), children: None, }; parents.push((doc_symbol, symbol.parent)); @@ -249,34 +243,35 @@ pub fn handle_document_symbol( } } - if world.config.client_caps.hierarchical_symbols { - Ok(Some(document_symbols.into())) + let res = if world.config.client_caps.hierarchical_symbols { + document_symbols.into() } else { + let url = to_proto::url(&world, file_id)?; let mut symbol_information = Vec::::new(); for symbol in document_symbols { flatten_document_symbol(&symbol, None, &url, &mut symbol_information); } + symbol_information.into() + }; + return Ok(Some(res)); - Ok(Some(symbol_information.into())) - } -} + fn flatten_document_symbol( + symbol: &DocumentSymbol, + container_name: Option, + url: &Url, + res: &mut Vec, + ) { + res.push(SymbolInformation { + name: symbol.name.clone(), + kind: symbol.kind, + deprecated: symbol.deprecated, + location: Location::new(url.clone(), symbol.range), + container_name: container_name, + }); -fn flatten_document_symbol( - symbol: &DocumentSymbol, - container_name: Option, - url: &Url, - res: &mut Vec, -) { - res.push(SymbolInformation { - name: symbol.name.clone(), - kind: symbol.kind, - deprecated: symbol.deprecated, - location: Location::new(url.clone(), symbol.range), - container_name: container_name, - }); - - for child in symbol.children.iter().flatten() { - flatten_document_symbol(child, Some(symbol.name.clone()), url, res); + for child in symbol.children.iter().flatten() { + flatten_document_symbol(child, Some(symbol.name.clone()), url, res); + } } } @@ -313,8 +308,8 @@ pub fn handle_workspace_symbol( for nav in world.analysis().symbol_search(query)? { let info = SymbolInformation { name: nav.name().to_string(), - kind: nav.kind().conv(), - location: nav.try_conv_with(world)?, + kind: to_proto::symbol_kind(nav.kind()), + location: to_proto::location(world, nav.file_range())?, container_name: nav.container_name().map(|v| v.to_string()), deprecated: None, }; @@ -329,12 +324,16 @@ pub fn handle_goto_definition( params: req::GotoDefinitionParams, ) -> Result> { let _p = profile("handle_goto_definition"); - let position = params.text_document_position_params.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params.text_document_position_params)?; let nav_info = match world.analysis().goto_definition(position)? { None => return Ok(None), Some(it) => it, }; - let res = (position.file_id, nav_info).try_conv_with(&world)?; + let res = to_proto::goto_definition_response( + &world, + FileRange { file_id: position.file_id, range: nav_info.range }, + nav_info.info, + )?; Ok(Some(res)) } @@ -343,12 +342,16 @@ pub fn handle_goto_implementation( params: req::GotoImplementationParams, ) -> Result> { let _p = profile("handle_goto_implementation"); - let position = params.text_document_position_params.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params.text_document_position_params)?; let nav_info = match world.analysis().goto_implementation(position)? { None => return Ok(None), Some(it) => it, }; - let res = (position.file_id, nav_info).try_conv_with(&world)?; + let res = to_proto::goto_definition_response( + &world, + FileRange { file_id: position.file_id, range: nav_info.range }, + nav_info.info, + )?; Ok(Some(res)) } @@ -357,12 +360,16 @@ pub fn handle_goto_type_definition( params: req::GotoTypeDefinitionParams, ) -> Result> { let _p = profile("handle_goto_type_definition"); - let position = params.text_document_position_params.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params.text_document_position_params)?; let nav_info = match world.analysis().goto_type_definition(position)? { None => return Ok(None), Some(it) => it, }; - let res = (position.file_id, nav_info).try_conv_with(&world)?; + let res = to_proto::goto_definition_response( + &world, + FileRange { file_id: position.file_id, range: nav_info.range }, + nav_info.info, + )?; Ok(Some(res)) } @@ -371,8 +378,13 @@ pub fn handle_parent_module( params: req::TextDocumentPositionParams, ) -> Result> { let _p = profile("handle_parent_module"); - let position = params.try_conv_with(&world)?; - world.analysis().parent_module(position)?.iter().try_conv_with_to_vec(&world) + let position = from_proto::file_position(&world, params)?; + world + .analysis() + .parent_module(position)? + .into_iter() + .map(|it| to_proto::location(&world, it.file_range())) + .collect::>>() } pub fn handle_runnables( @@ -380,9 +392,9 @@ pub fn handle_runnables( params: req::RunnablesParams, ) -> Result> { let _p = profile("handle_runnables"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; - let offset = params.position.map(|it| it.conv_with(&line_index)); + let offset = params.position.map(|it| from_proto::offset(&line_index, it)); let mut res = Vec::new(); let workspace_root = world.workspace_root_for(file_id); let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; @@ -439,7 +451,7 @@ pub fn handle_completion( params: req::CompletionParams, ) -> Result> { let _p = profile("handle_completion"); - let position = params.text_document_position.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params.text_document_position)?; let completion_triggered_after_single_colon = { let mut res = false; if let Some(ctx) = params.context { @@ -468,8 +480,10 @@ pub fn handle_completion( }; let line_index = world.analysis().file_line_index(position.file_id)?; let line_endings = world.file_line_endings(position.file_id); - let items: Vec = - items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect(); + let items: Vec = items + .into_iter() + .map(|item| to_proto::completion_item(&line_index, line_endings, item)) + .collect(); Ok(Some(items.into())) } @@ -479,17 +493,16 @@ pub fn handle_folding_range( params: FoldingRangeParams, ) -> Result>> { let _p = profile("handle_folding_range"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let folds = world.analysis().folding_ranges(file_id)?; let text = world.analysis().file_text(file_id)?; let line_index = world.analysis().file_line_index(file_id)?; - let ctx = FoldConvCtx { - text: &text, - line_index: &line_index, - line_folding_only: world.config.client_caps.line_folding_only, - }; - let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); - Ok(res) + let line_folding_only = world.config.client_caps.line_folding_only; + let res = folds + .into_iter() + .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) + .collect(); + Ok(Some(res)) } pub fn handle_signature_help( @@ -497,34 +510,34 @@ pub fn handle_signature_help( params: req::SignatureHelpParams, ) -> Result> { let _p = profile("handle_signature_help"); - let position = params.text_document_position_params.try_conv_with(&world)?; - if let Some(call_info) = world.analysis().call_info(position)? { - let concise = !world.config.call_info_full; - let mut active_parameter = call_info.active_parameter.map(|it| it as i64); - if concise && call_info.signature.has_self_param { - active_parameter = active_parameter.map(|it| it.saturating_sub(1)); - } - let sig_info = call_info.signature.conv_with(concise); - - Ok(Some(req::SignatureHelp { - signatures: vec![sig_info], - active_signature: Some(0), - active_parameter, - })) - } else { - Ok(None) + let position = from_proto::file_position(&world, params.text_document_position_params)?; + let call_info = match world.analysis().call_info(position)? { + None => return Ok(None), + Some(it) => it, + }; + let concise = !world.config.call_info_full; + let mut active_parameter = call_info.active_parameter.map(|it| it as i64); + if concise && call_info.signature.has_self_param { + active_parameter = active_parameter.map(|it| it.saturating_sub(1)); } + let sig_info = to_proto::signature_information(call_info.signature, concise); + + Ok(Some(req::SignatureHelp { + signatures: vec![sig_info], + active_signature: Some(0), + active_parameter, + })) } pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result> { let _p = profile("handle_hover"); - let position = params.text_document_position_params.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params.text_document_position_params)?; let info = match world.analysis().hover(position)? { None => return Ok(None), Some(info) => info, }; let line_index = world.analysis.file_line_index(position.file_id)?; - let range = info.range.conv_with(&line_index); + let range = to_proto::range(&line_index, info.range); let res = Hover { contents: HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, @@ -540,7 +553,7 @@ pub fn handle_prepare_rename( params: req::TextDocumentPositionParams, ) -> Result> { let _p = profile("handle_prepare_rename"); - let position = params.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params)?; let optional_change = world.analysis().rename(position, "dummy")?; let range = match optional_change { @@ -548,15 +561,14 @@ pub fn handle_prepare_rename( Some(it) => it.range, }; - let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; - let range = range.conv_with(&line_index); + let line_index = world.analysis().file_line_index(position.file_id)?; + let range = to_proto::range(&line_index, range); Ok(Some(PrepareRenameResponse::Range(range))) } pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result> { let _p = profile("handle_rename"); - let position = params.text_document_position.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params.text_document_position)?; if params.new_name.is_empty() { return Err(LspError::new( @@ -567,14 +579,13 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result return Ok(None), Some(it) => it.info, }; - let source_change_req = change.try_conv_with(&world)?; - - Ok(Some(source_change_req.workspace_edit)) + let source_change = to_proto::source_change(&world, source_change)?; + Ok(Some(source_change.workspace_edit)) } pub fn handle_references( @@ -582,7 +593,7 @@ pub fn handle_references( params: req::ReferenceParams, ) -> Result>> { let _p = profile("handle_references"); - let position = params.text_document_position.try_conv_with(&world)?; + let position = from_proto::file_position(&world, params.text_document_position)?; let refs = match world.analysis().find_all_refs(position, None)? { None => return Ok(None), @@ -591,33 +602,13 @@ pub fn handle_references( let locations = if params.context.include_declaration { refs.into_iter() - .filter_map(|reference| { - let line_index = - world.analysis().file_line_index(reference.file_range.file_id).ok()?; - to_location( - reference.file_range.file_id, - reference.file_range.range, - &world, - &line_index, - ) - .ok() - }) + .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) .collect() } else { // Only iterate over the references if include_declaration was false refs.references() .iter() - .filter_map(|reference| { - let line_index = - world.analysis().file_line_index(reference.file_range.file_id).ok()?; - to_location( - reference.file_range.file_id, - reference.file_range.range, - &world, - &line_index, - ) - .ok() - }) + .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) .collect() }; @@ -629,12 +620,12 @@ pub fn handle_formatting( params: DocumentFormattingParams, ) -> Result>> { let _p = profile("handle_formatting"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let file = world.analysis().file_text(file_id)?; let crate_ids = world.analysis().crate_for(file_id)?; let file_line_index = world.analysis().file_line_index(file_id)?; - let end_position = TextSize::of(file.as_str()).conv_with(&file_line_index); + let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); let mut rustfmt = match &world.config.rustfmt { RustfmtConfig::Rustfmt { extra_args } => { @@ -700,33 +691,14 @@ pub fn handle_formatting( }])) } -fn create_single_code_action(assist: Assist, world: &WorldSnapshot) -> Result { - let arg = to_value(assist.source_change.try_conv_with(world)?)?; - let title = assist.label; - let command = Command { - title: title.clone(), - command: "rust-analyzer.applySourceChange".to_string(), - arguments: Some(vec![arg]), - }; - - Ok(CodeAction { - title, - kind: Some(String::new()), - diagnostics: None, - edit: None, - command: Some(command), - is_preferred: None, - }) -} - pub fn handle_code_action( world: WorldSnapshot, params: req::CodeActionParams, ) -> Result> { let _p = profile("handle_code_action"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; - let range = params.range.conv_with(&line_index); + let range = from_proto::text_range(&line_index, params.range); let diagnostics = world.analysis().diagnostics(file_id)?; let mut res = CodeActionResponse::default(); @@ -739,7 +711,7 @@ pub fn handle_code_action( for source_edit in fixes_from_diagnostics { let title = source_edit.label.clone(); - let edit = source_edit.try_conv_with(&world)?; + let edit = to_proto::source_change(&world, source_edit)?; let command = Command { title, @@ -758,7 +730,7 @@ pub fn handle_code_action( } for fix in world.check_fixes.get(&file_id).into_iter().flatten() { - let fix_range = fix.range.conv_with(&line_index); + let fix_range = from_proto::text_range(&line_index, fix.range); if fix_range.intersect(range).is_none() { continue; } @@ -779,21 +751,21 @@ pub fn handle_code_action( .1 .push(assist), None => { - res.push(create_single_code_action(assist, &world)?.into()); + res.push(to_proto::code_action(&world, assist)?.into()); } } } for (group_label, (idx, assists)) in grouped_assists { if assists.len() == 1 { - res[idx] = - create_single_code_action(assists.into_iter().next().unwrap(), &world)?.into(); + res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into(); } else { let title = group_label; let mut arguments = Vec::with_capacity(assists.len()); for assist in assists { - arguments.push(to_value(assist.source_change.try_conv_with(&world)?)?); + let source_change = to_proto::source_change(&world, assist.source_change)?; + arguments.push(to_value(source_change)?); } let command = Some(Command { @@ -838,7 +810,7 @@ pub fn handle_code_lens( params: req::CodeLensParams, ) -> Result>> { let _p = profile("handle_code_lens"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; let mut lenses: Vec = Default::default(); @@ -902,7 +874,7 @@ pub fn handle_code_lens( _ => false, }) .map(|it| { - let range = it.node_range.conv_with(&line_index); + let range = to_proto::range(&line_index, it.node_range); let pos = range.start; let lens_params = req::GotoImplementationParams { text_document_position_params: req::TextDocumentPositionParams::new( @@ -979,34 +951,33 @@ pub fn handle_document_highlight( params: req::DocumentHighlightParams, ) -> Result>> { let _p = profile("handle_document_highlight"); - let file_id = params.text_document_position_params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id)?; + let position = from_proto::file_position(&world, params.text_document_position_params)?; + let line_index = world.analysis().file_line_index(position.file_id)?; - let refs = match world.analysis().find_all_refs( - params.text_document_position_params.try_conv_with(&world)?, - Some(SearchScope::single_file(file_id)), - )? { + let refs = match world + .analysis() + .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? + { None => return Ok(None), Some(refs) => refs, }; - Ok(Some( - refs.into_iter() - .filter(|reference| reference.file_range.file_id == file_id) - .map(|reference| DocumentHighlight { - range: reference.file_range.range.conv_with(&line_index), - kind: reference.access.map(|it| it.conv()), - }) - .collect(), - )) + let res = refs + .into_iter() + .filter(|reference| reference.file_range.file_id == position.file_id) + .map(|reference| DocumentHighlight { + range: to_proto::range(&line_index, reference.file_range.range), + kind: reference.access.map(to_proto::document_highlight_kind), + }) + .collect(); + Ok(Some(res)) } pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result { let _p = profile("handle_ssr"); - world - .analysis() - .structural_search_replace(¶ms.query, params.parse_only)?? - .try_conv_with(&world) + let source_change = + world.analysis().structural_search_replace(¶ms.query, params.parse_only)??; + to_proto::source_change(&world, source_change) } pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result { @@ -1017,8 +988,8 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result "run binary".to_string(), }; Ok(req::Runnable { - range: runnable.range.conv_with(&line_index), + range: to_proto::range(&line_index, runnable.range), label, bin: "cargo".to_string(), args, @@ -1064,13 +1035,13 @@ pub fn handle_inlay_hints( params: InlayHintsParams, ) -> Result> { let _p = profile("handle_inlay_hints"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let analysis = world.analysis(); let line_index = analysis.file_line_index(file_id)?; Ok(analysis .inlay_hints(file_id, &world.config.inlay_hints)? .into_iter() - .map_conv_with(&line_index) + .map(|it| to_proto::inlay_int(&line_index, it)) .collect()) } @@ -1079,21 +1050,19 @@ pub fn handle_call_hierarchy_prepare( params: CallHierarchyPrepareParams, ) -> Result>> { let _p = profile("handle_call_hierarchy_prepare"); - let position = params.text_document_position_params.try_conv_with(&world)?; - let file_id = position.file_id; + let position = from_proto::file_position(&world, params.text_document_position_params)?; let nav_info = match world.analysis().call_hierarchy(position)? { None => return Ok(None), Some(it) => it, }; - let line_index = world.analysis().file_line_index(file_id)?; - let RangeInfo { range, info: navs } = nav_info; + let RangeInfo { range: _, info: navs } = nav_info; let res = navs .into_iter() .filter(|it| it.kind() == SyntaxKind::FN_DEF) - .filter_map(|it| to_call_hierarchy_item(file_id, range, &world, &line_index, it).ok()) - .collect(); + .map(|it| to_proto::call_hierarchy_item(&world, it)) + .collect::>>()?; Ok(Some(res)) } @@ -1106,7 +1075,7 @@ pub fn handle_call_hierarchy_incoming( let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; + let frange = from_proto::file_range(&world, doc, item.range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let call_items = match world.analysis().incoming_calls(fpos)? { @@ -1119,11 +1088,14 @@ pub fn handle_call_hierarchy_incoming( for call_item in call_items.into_iter() { let file_id = call_item.target.file_id(); let line_index = world.analysis().file_line_index(file_id)?; - let range = call_item.target.range(); - let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; + let item = to_proto::call_hierarchy_item(&world, call_item.target)?; res.push(CallHierarchyIncomingCall { from: item, - from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), + from_ranges: call_item + .ranges + .into_iter() + .map(|it| to_proto::range(&line_index, it)) + .collect(), }); } @@ -1138,7 +1110,7 @@ pub fn handle_call_hierarchy_outgoing( let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange: FileRange = (&doc, item.range).try_conv_with(&world)?; + let frange = from_proto::file_range(&world, doc, item.range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let call_items = match world.analysis().outgoing_calls(fpos)? { @@ -1151,11 +1123,14 @@ pub fn handle_call_hierarchy_outgoing( for call_item in call_items.into_iter() { let file_id = call_item.target.file_id(); let line_index = world.analysis().file_line_index(file_id)?; - let range = call_item.target.range(); - let item = to_call_hierarchy_item(file_id, range, &world, &line_index, call_item.target)?; + let item = to_proto::call_hierarchy_item(&world, call_item.target)?; res.push(CallHierarchyOutgoingCall { to: item, - from_ranges: call_item.ranges.iter().map(|it| it.conv_with(&line_index)).collect(), + from_ranges: call_item + .ranges + .into_iter() + .map(|it| to_proto::range(&line_index, it)) + .collect(), }); } @@ -1168,19 +1143,20 @@ pub fn handle_semantic_tokens( ) -> Result> { let _p = profile("handle_semantic_tokens"); - let file_id = params.text_document.try_conv_with(&world)?; + let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let text = world.analysis().file_text(file_id)?; let line_index = world.analysis().file_line_index(file_id)?; let mut builder = SemanticTokensBuilder::default(); for highlight_range in world.analysis().highlight(file_id)?.into_iter() { - let (token_index, modifier_bitset) = highlight_range.highlight.conv(); + let (token_index, modifier_bitset) = + to_proto::token_type_index_modifiers_bitself(highlight_range.highlight); for mut range in line_index.lines(highlight_range.range) { if text[range].ends_with('\n') { range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); } - let range = range.conv_with(&line_index); + let range = to_proto::range(&line_index, range); builder.push(range, token_index, modifier_bitset); } } @@ -1196,14 +1172,16 @@ pub fn handle_semantic_tokens_range( ) -> Result> { let _p = profile("handle_semantic_tokens_range"); - let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; + let frange = from_proto::file_range(&world, params.text_document, params.range)?; let line_index = world.analysis().file_line_index(frange.file_id)?; let mut builder = SemanticTokensBuilder::default(); for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { - let (token_type, token_modifiers) = highlight_range.highlight.conv(); - builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); + let (token_type, token_modifiers) = + to_proto::token_type_index_modifiers_bitself(highlight_range.highlight); + let range = to_proto::range(&line_index, highlight_range.range); + builder.push(range, token_type, token_modifiers); } let tokens = builder.build(); diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs new file mode 100644 index 0000000000..39f2e6d4d1 --- /dev/null +++ b/crates/rust-analyzer/src/to_proto.rs @@ -0,0 +1,566 @@ +//! Conversion of rust-analyzer specific types to lsp_types equivalents. +use ra_db::{FileId, FileRange}; +use ra_ide::{ + translate_offset_with_edit, Assist, CompletionItem, CompletionItemKind, Documentation, + FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, + InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, + SourceChange, SourceFileEdit, +}; +use ra_syntax::{SyntaxKind, TextRange, TextSize}; +use ra_text_edit::{Indel, TextEdit}; +use ra_vfs::LineEndings; + +use crate::{req, semantic_tokens, world::WorldSnapshot, Result}; + +pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { + let line_col = line_index.line_col(offset); + let line = u64::from(line_col.line); + let character = u64::from(line_col.col_utf16); + lsp_types::Position::new(line, character) +} + +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) +} + +pub(crate) fn symbol_kind(syntax_kind: SyntaxKind) -> lsp_types::SymbolKind { + match syntax_kind { + SyntaxKind::FN_DEF => lsp_types::SymbolKind::Function, + SyntaxKind::STRUCT_DEF => lsp_types::SymbolKind::Struct, + SyntaxKind::ENUM_DEF => lsp_types::SymbolKind::Enum, + SyntaxKind::ENUM_VARIANT => lsp_types::SymbolKind::EnumMember, + SyntaxKind::TRAIT_DEF => lsp_types::SymbolKind::Interface, + SyntaxKind::MACRO_CALL => lsp_types::SymbolKind::Function, + SyntaxKind::MODULE => lsp_types::SymbolKind::Module, + SyntaxKind::TYPE_ALIAS_DEF => lsp_types::SymbolKind::TypeParameter, + SyntaxKind::RECORD_FIELD_DEF => lsp_types::SymbolKind::Field, + SyntaxKind::STATIC_DEF => lsp_types::SymbolKind::Constant, + SyntaxKind::CONST_DEF => lsp_types::SymbolKind::Constant, + SyntaxKind::IMPL_DEF => lsp_types::SymbolKind::Object, + _ => lsp_types::SymbolKind::Variable, + } +} + +pub(crate) fn document_highlight_kind( + reference_access: ReferenceAccess, +) -> lsp_types::DocumentHighlightKind { + match reference_access { + ReferenceAccess::Read => lsp_types::DocumentHighlightKind::Read, + ReferenceAccess::Write => lsp_types::DocumentHighlightKind::Write, + } +} + +pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity { + match severity { + Severity::Error => lsp_types::DiagnosticSeverity::Error, + Severity::WeakWarning => lsp_types::DiagnosticSeverity::Hint, + } +} + +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 insert_text_format( + insert_text_format: InsertTextFormat, +) -> lsp_types::InsertTextFormat { + match insert_text_format { + InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, + InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, + } +} + +pub(crate) fn completion_item_kind( + completion_item_kind: CompletionItemKind, +) -> lsp_types::CompletionItemKind { + match completion_item_kind { + CompletionItemKind::Keyword => lsp_types::CompletionItemKind::Keyword, + CompletionItemKind::Snippet => lsp_types::CompletionItemKind::Snippet, + CompletionItemKind::Module => lsp_types::CompletionItemKind::Module, + CompletionItemKind::Function => lsp_types::CompletionItemKind::Function, + CompletionItemKind::Struct => lsp_types::CompletionItemKind::Struct, + CompletionItemKind::Enum => lsp_types::CompletionItemKind::Enum, + CompletionItemKind::EnumVariant => lsp_types::CompletionItemKind::EnumMember, + CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::Struct, + CompletionItemKind::Binding => lsp_types::CompletionItemKind::Variable, + CompletionItemKind::Field => lsp_types::CompletionItemKind::Field, + CompletionItemKind::Trait => lsp_types::CompletionItemKind::Interface, + CompletionItemKind::TypeAlias => lsp_types::CompletionItemKind::Struct, + CompletionItemKind::Const => lsp_types::CompletionItemKind::Constant, + CompletionItemKind::Static => lsp_types::CompletionItemKind::Value, + CompletionItemKind::Method => lsp_types::CompletionItemKind::Method, + CompletionItemKind::TypeParam => lsp_types::CompletionItemKind::TypeParameter, + CompletionItemKind::Macro => lsp_types::CompletionItemKind::Method, + CompletionItemKind::Attribute => lsp_types::CompletionItemKind::EnumMember, + } +} + +pub(crate) fn text_edit( + line_index: &LineIndex, + line_endings: LineEndings, + indel: Indel, +) -> lsp_types::TextEdit { + let range = range(line_index, indel.delete); + let new_text = match line_endings { + LineEndings::Unix => indel.insert, + LineEndings::Dos => indel.insert.replace('\n', "\r\n"), + }; + lsp_types::TextEdit { range, new_text } +} + +pub(crate) fn text_edit_vec( + line_index: &LineIndex, + line_endings: LineEndings, + text_edit: TextEdit, +) -> Vec { + text_edit + .as_indels() + .iter() + .map(|it| self::text_edit(line_index, line_endings, it.clone())) + .collect() +} + +pub(crate) fn completion_item( + line_index: &LineIndex, + line_endings: LineEndings, + completion_item: CompletionItem, +) -> lsp_types::CompletionItem { + let mut additional_text_edits = Vec::new(); + let mut text_edit = None; + // LSP does not allow arbitrary edits in completion, so we have to do a + // non-trivial mapping here. + let source_range = completion_item.source_range(); + for indel in completion_item.text_edit().as_indels() { + if indel.delete.contains_range(source_range) { + text_edit = Some(if indel.delete == source_range { + self::text_edit(line_index, line_endings, 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, line_endings, indel1)); + self::text_edit(line_index, line_endings, indel2) + }) + } else { + assert!(source_range.intersect(indel.delete).is_none()); + let text_edit = self::text_edit(line_index, line_endings, indel.clone()); + additional_text_edits.push(text_edit); + } + } + let text_edit = text_edit.unwrap(); + + let mut res = lsp_types::CompletionItem { + label: completion_item.label().to_string(), + detail: completion_item.detail().map(|it| it.to_string()), + filter_text: Some(completion_item.lookup().to_string()), + kind: completion_item.kind().map(completion_item_kind), + text_edit: Some(text_edit.into()), + additional_text_edits: Some(additional_text_edits), + documentation: completion_item.documentation().map(documentation), + deprecated: Some(completion_item.deprecated()), + command: if completion_item.trigger_call_info() { + let cmd = lsp_types::Command { + title: "triggerParameterHints".into(), + command: "editor.action.triggerParameterHints".into(), + arguments: None, + }; + Some(cmd) + } else { + None + }, + ..Default::default() + }; + + if completion_item.score().is_some() { + res.preselect = Some(true) + } + + if completion_item.deprecated() { + res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) + } + + res.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); + + res +} + +pub(crate) fn signature_information( + signature: FunctionSignature, + concise: bool, +) -> lsp_types::SignatureInformation { + let (label, documentation, params) = if concise { + let mut params = signature.parameters; + if signature.has_self_param { + params.remove(0); + } + (params.join(", "), None, params) + } else { + (signature.to_string(), signature.doc.map(documentation), signature.parameters) + }; + + let parameters: Vec = params + .into_iter() + .map(|param| lsp_types::ParameterInformation { + label: lsp_types::ParameterLabel::Simple(param), + documentation: None, + }) + .collect(); + + lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) } +} + +pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> req::InlayHint { + req::InlayHint { + label: inlay_hint.label.to_string(), + range: range(line_index, inlay_hint.range), + kind: match inlay_hint.kind { + InlayKind::ParameterHint => req::InlayKind::ParameterHint, + InlayKind::TypeHint => req::InlayKind::TypeHint, + InlayKind::ChainingHint => req::InlayKind::ChainingHint, + }, + } +} + +// TODO: this is wrong +pub(crate) fn token_type_index_modifiers_bitself(highlight: Highlight) -> (u32, u32) { + let mut mods = semantic_tokens::ModifierSet::default(); + let type_ = match highlight.tag { + HighlightTag::Struct => lsp_types::SemanticTokenType::STRUCT, + HighlightTag::Enum => lsp_types::SemanticTokenType::ENUM, + HighlightTag::Union => semantic_tokens::UNION, + HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS, + HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE, + HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, + HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, + HighlightTag::Field => lsp_types::SemanticTokenType::MEMBER, + HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, + HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE, + HighlightTag::Constant => { + mods |= semantic_tokens::CONSTANT; + mods |= lsp_types::SemanticTokenModifier::STATIC; + lsp_types::SemanticTokenType::VARIABLE + } + HighlightTag::Static => { + mods |= lsp_types::SemanticTokenModifier::STATIC; + lsp_types::SemanticTokenType::VARIABLE + } + HighlightTag::EnumVariant => semantic_tokens::ENUM_MEMBER, + HighlightTag::Macro => lsp_types::SemanticTokenType::MACRO, + HighlightTag::Local => lsp_types::SemanticTokenType::VARIABLE, + HighlightTag::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, + HighlightTag::Lifetime => semantic_tokens::LIFETIME, + HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { + lsp_types::SemanticTokenType::NUMBER + } + HighlightTag::CharLiteral | HighlightTag::StringLiteral => { + lsp_types::SemanticTokenType::STRING + } + HighlightTag::Comment => lsp_types::SemanticTokenType::COMMENT, + HighlightTag::Attribute => semantic_tokens::ATTRIBUTE, + HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, + HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, + HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, + }; + + for modifier in highlight.modifiers.iter() { + let modifier = match modifier { + HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, + HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, + HighlightModifier::Mutable => semantic_tokens::MUTABLE, + HighlightModifier::Unsafe => semantic_tokens::UNSAFE, + }; + mods |= modifier; + } + + (semantic_tokens::type_index(type_), mods.0) +} + +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), + FoldKind::Mods | FoldKind::Block => None, + }; + + 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, + } + } +} + +pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result { + world.file_id_to_uri(file_id) +} + +pub(crate) fn text_document_identifier( + world: &WorldSnapshot, + file_id: FileId, +) -> Result { + let res = lsp_types::TextDocumentIdentifier { uri: url(world, file_id)? }; + Ok(res) +} + +pub(crate) fn versioned_text_document_identifier( + world: &WorldSnapshot, + file_id: FileId, + version: Option, +) -> Result { + let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; + Ok(res) +} + +pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result { + let url = url(world, frange.file_id)?; + let line_index = world.analysis().file_line_index(frange.file_id)?; + let range = range(&line_index, frange.range); + let loc = lsp_types::Location::new(url, range); + Ok(loc) +} + +pub(crate) fn location_link( + world: &WorldSnapshot, + src: FileRange, + target: NavigationTarget, +) -> Result { + let src_location = location(world, src)?; + let (target_uri, target_range, target_selection_range) = location_info(world, target)?; + let res = lsp_types::LocationLink { + origin_selection_range: Some(src_location.range), + target_uri, + target_range, + target_selection_range, + }; + Ok(res) +} + +fn location_info( + world: &WorldSnapshot, + target: NavigationTarget, +) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { + let line_index = world.analysis().file_line_index(target.file_id())?; + + let target_uri = url(world, target.file_id())?; + let target_range = range(&line_index, target.full_range()); + let target_selection_range = + target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); + Ok((target_uri, target_range, target_selection_range)) +} + +pub(crate) fn goto_definition_response( + world: &WorldSnapshot, + src: FileRange, + targets: Vec, +) -> Result { + if world.config.client_caps.location_link { + let links = targets + .into_iter() + .map(|nav| location_link(world, src, nav)) + .collect::>>()?; + Ok(links.into()) + } else { + let locations = targets + .into_iter() + .map(|nav| { + location( + world, + FileRange { + file_id: nav.file_id(), + range: nav.focus_range().unwrap_or(nav.range()), + }, + ) + }) + .collect::>>()?; + Ok(locations.into()) + } +} + +pub(crate) fn text_document_edit( + world: &WorldSnapshot, + source_file_edit: SourceFileEdit, +) -> Result { + let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; + let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; + let line_endings = world.file_line_endings(source_file_edit.file_id); + let edits = source_file_edit + .edit + .as_indels() + .iter() + .map(|it| text_edit(&line_index, line_endings, it.clone())) + .collect(); + Ok(lsp_types::TextDocumentEdit { text_document, edits }) +} + +pub(crate) fn resource_op( + world: &WorldSnapshot, + file_system_edit: FileSystemEdit, +) -> Result { + let res = match file_system_edit { + FileSystemEdit::CreateFile { source_root, path } => { + let uri = world.path_to_uri(source_root, &path)?; + lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) + } + FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { + let old_uri = world.file_id_to_uri(src)?; + let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; + lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) + } + }; + Ok(res) +} + +pub(crate) fn source_change( + world: &WorldSnapshot, + source_change: SourceChange, +) -> Result { + let cursor_position = match source_change.cursor_position { + None => None, + Some(pos) => { + let line_index = world.analysis().file_line_index(pos.file_id)?; + let edit = source_change + .source_file_edits + .iter() + .find(|it| it.file_id == pos.file_id) + .map(|it| &it.edit); + let line_col = match edit { + Some(edit) => translate_offset_with_edit(&*line_index, pos.offset, edit), + None => line_index.line_col(pos.offset), + }; + let position = + lsp_types::Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); + Some(lsp_types::TextDocumentPositionParams { + text_document: text_document_identifier(world, pos.file_id)?, + position, + }) + } + }; + let mut document_changes: Vec = Vec::new(); + for op in source_change.file_system_edits { + let op = resource_op(&world, op)?; + document_changes.push(lsp_types::DocumentChangeOperation::Op(op)); + } + for edit in source_change.source_file_edits { + let edit = text_document_edit(&world, edit)?; + document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit)); + } + let workspace_edit = lsp_types::WorkspaceEdit { + changes: None, + document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), + }; + Ok(req::SourceChange { label: source_change.label, workspace_edit, cursor_position }) +} + +pub fn call_hierarchy_item( + world: &WorldSnapshot, + target: NavigationTarget, +) -> Result { + let name = target.name().to_string(); + let detail = target.description().map(|it| it.to_string()); + let kind = symbol_kind(target.kind()); + let (uri, range, selection_range) = location_info(world, target)?; + Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) +} + +#[cfg(test)] +mod tests { + use test_utils::extract_ranges; + + use super::*; + + #[test] + fn conv_fold_line_folding_only_fixup() { + let text = r#"mod a; +mod b; +mod c; + +fn main() { + if cond { + a::do_a(); + } else { + b::do_b(); + } +}"#; + + let (ranges, text) = extract_ranges(text, "fold"); + assert_eq!(ranges.len(), 4); + let folds = vec![ + Fold { range: ranges[0], kind: FoldKind::Mods }, + Fold { range: ranges[1], kind: FoldKind::Block }, + Fold { range: ranges[2], kind: FoldKind::Block }, + Fold { range: ranges[3], kind: FoldKind::Block }, + ]; + + let line_index = LineIndex::new(&text); + let converted: Vec = + folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect(); + + 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); + } + } +} + +pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result { + let source_change = source_change(&world, assist.source_change)?; + let arg = serde_json::to_value(source_change)?; + let title = assist.label; + let command = lsp_types::Command { + title: title.clone(), + command: "rust-analyzer.applySourceChange".to_string(), + arguments: Some(vec![arg]), + }; + + Ok(lsp_types::CodeAction { + title, + kind: Some(String::new()), + diagnostics: None, + edit: None, + command: Some(command), + is_preferred: None, + }) +} From bd8422643ad381fa603d569d0553723c1aa4cd1e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 10 May 2020 19:09:22 +0200 Subject: [PATCH 2/5] to_proto::semantic_tokens --- .../rust-analyzer/src/main_loop/handlers.rs | 37 ++++--------------- crates/rust-analyzer/src/to_proto.rs | 30 +++++++++++++-- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index daa5b4411a..4f619654fc 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -35,7 +35,6 @@ use crate::{ diagnostics::DiagnosticTask, from_json, from_proto, req::{self, InlayHint, InlayHintsParams}, - semantic_tokens::SemanticTokensBuilder, to_proto, world::WorldSnapshot, LspError, Result, @@ -1147,23 +1146,9 @@ pub fn handle_semantic_tokens( let text = world.analysis().file_text(file_id)?; let line_index = world.analysis().file_line_index(file_id)?; - let mut builder = SemanticTokensBuilder::default(); - - for highlight_range in world.analysis().highlight(file_id)?.into_iter() { - let (token_index, modifier_bitset) = - to_proto::token_type_index_modifiers_bitself(highlight_range.highlight); - for mut range in line_index.lines(highlight_range.range) { - if text[range].ends_with('\n') { - range = TextRange::new(range.start(), range.end() - TextSize::of('\n')); - } - let range = to_proto::range(&line_index, range); - builder.push(range, token_index, modifier_bitset); - } - } - - let tokens = builder.build(); - - Ok(Some(tokens.into())) + let highlights = world.analysis().highlight(file_id)?; + let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); + Ok(Some(semantic_tokens.into())) } pub fn handle_semantic_tokens_range( @@ -1173,18 +1158,10 @@ pub fn handle_semantic_tokens_range( let _p = profile("handle_semantic_tokens_range"); let frange = from_proto::file_range(&world, params.text_document, params.range)?; + let text = world.analysis().file_text(frange.file_id)?; let line_index = world.analysis().file_line_index(frange.file_id)?; - let mut builder = SemanticTokensBuilder::default(); - - for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { - let (token_type, token_modifiers) = - to_proto::token_type_index_modifiers_bitself(highlight_range.highlight); - let range = to_proto::range(&line_index, highlight_range.range); - builder.push(range, token_type, token_modifiers); - } - - let tokens = builder.build(); - - Ok(Some(tokens.into())) + let highlights = world.analysis().highlight_range(frange)?; + let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); + Ok(Some(semantic_tokens.into())) } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 39f2e6d4d1..29b47608ea 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -3,8 +3,8 @@ use ra_db::{FileId, FileRange}; use ra_ide::{ translate_offset_with_edit, Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, - InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, - SourceChange, SourceFileEdit, + HighlightedRange, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, + ReferenceAccess, Severity, SourceChange, SourceFileEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_text_edit::{Indel, TextEdit}; @@ -227,8 +227,30 @@ pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> req::I } } -// TODO: this is wrong -pub(crate) fn token_type_index_modifiers_bitself(highlight: Highlight) -> (u32, u32) { +pub(crate) fn semantic_tokens( + text: &str, + line_index: &LineIndex, + highlights: Vec, +) -> lsp_types::SemanticTokens { + let mut builder = semantic_tokens::SemanticTokensBuilder::default(); + + for highlight_range in highlights { + let (token_index, modifier_bitset) = + token_type_index_modifiers_bitself(highlight_range.highlight); + for mut text_range in line_index.lines(highlight_range.range) { + if text[text_range].ends_with('\n') { + text_range = + TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n')); + } + let range = range(&line_index, text_range); + builder.push(range, token_index, modifier_bitset); + } + } + + builder.build() +} + +fn token_type_index_modifiers_bitself(highlight: Highlight) -> (u32, u32) { let mut mods = semantic_tokens::ModifierSet::default(); let type_ = match highlight.tag { HighlightTag::Struct => lsp_types::SemanticTokenType::STRUCT, From a78e1573b1c65e1387102a42bb978d9e299fea39 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 10 May 2020 19:14:02 +0200 Subject: [PATCH 3/5] Better fn signature --- crates/rust-analyzer/src/to_proto.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 29b47608ea..4157ce8ea1 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -235,8 +235,10 @@ pub(crate) fn semantic_tokens( let mut builder = semantic_tokens::SemanticTokensBuilder::default(); for highlight_range in highlights { - let (token_index, modifier_bitset) = - token_type_index_modifiers_bitself(highlight_range.highlight); + let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); + let token_index = semantic_tokens::type_index(type_); + let modifier_bitset = mods.0; + for mut text_range in line_index.lines(highlight_range.range) { if text[text_range].ends_with('\n') { text_range = @@ -250,7 +252,9 @@ pub(crate) fn semantic_tokens( builder.build() } -fn token_type_index_modifiers_bitself(highlight: Highlight) -> (u32, u32) { +fn semantic_token_type_and_modifiers( + highlight: Highlight, +) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { let mut mods = semantic_tokens::ModifierSet::default(); let type_ = match highlight.tag { HighlightTag::Struct => lsp_types::SemanticTokenType::STRUCT, @@ -300,7 +304,7 @@ fn token_type_index_modifiers_bitself(highlight: Highlight) -> (u32, u32) { mods |= modifier; } - (semantic_tokens::type_index(type_), mods.0) + (type_, mods) } pub(crate) fn folding_range( From bec3bf701c9e7d0a99c5a89e5c95ce543ce21dbe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 10 May 2020 19:24:02 +0200 Subject: [PATCH 4/5] Don't re-export lsp_types --- crates/rust-analyzer/src/main_loop.rs | 147 ++++++++++-------- .../rust-analyzer/src/main_loop/handlers.rs | 68 ++++---- crates/rust-analyzer/src/req.rs | 23 +-- 3 files changed, 119 insertions(+), 119 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e27d85dc92..333ecc8598 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -104,7 +104,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) if project_roots.is_empty() && config.notifications.cargo_toml_not_found { show_message( - req::MessageType::Error, + lsp_types::MessageType::Error, format!( "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", ws_roots.iter().format_with(", ", |it, f| f(&it.display())) @@ -124,7 +124,7 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) .map_err(|err| { log::error!("failed to load workspace: {:#}", err); show_message( - req::MessageType::Error, + lsp_types::MessageType::Error, format!("rust-analyzer failed to load workspace: {:#}", err), &connection.sender, ); @@ -142,23 +142,25 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) .collect::, _>>()?; if let FilesWatcher::Client = config.files.watcher { - let registration_options = req::DidChangeWatchedFilesRegistrationOptions { + let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { watchers: workspaces .iter() .flat_map(ProjectWorkspace::to_roots) .filter(PackageRoot::is_member) .map(|root| format!("{}/**/*.rs", root.path().display())) - .map(|glob_pattern| req::FileSystemWatcher { glob_pattern, kind: None }) + .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) .collect(), }; - let registration = req::Registration { + let registration = lsp_types::Registration { id: "file-watcher".to_string(), method: "workspace/didChangeWatchedFiles".to_string(), register_options: Some(serde_json::to_value(registration_options).unwrap()), }; - let params = req::RegistrationParams { registrations: vec![registration] }; - let request = - request_new::(loop_state.next_request_id(), params); + let params = lsp_types::RegistrationParams { registrations: vec![registration] }; + let request = request_new::( + loop_state.next_request_id(), + params, + ); connection.sender.send(request.into()).unwrap(); } @@ -258,14 +260,14 @@ impl fmt::Debug for Event { match self { Event::Msg(Message::Notification(not)) => { - if notification_is::(not) - || notification_is::(not) + if notification_is::(not) + || notification_is::(not) { return debug_verbose_not(not, f); } } Event::Task(Task::Notify(not)) => { - if notification_is::(not) { + if notification_is::(not) { return debug_verbose_not(not, f); } } @@ -450,7 +452,7 @@ fn loop_turn( log::error!("overly long loop turn: {:?}", loop_duration); if env::var("RA_PROFILE").is_ok() { show_message( - req::MessageType::Error, + lsp_types::MessageType::Error, format!("overly long loop turn: {:?}", loop_duration), &connection.sender, ); @@ -503,7 +505,7 @@ fn on_request( .on_sync::(|s, ()| Ok(s.collect_garbage()))? .on_sync::(|s, p| handlers::handle_join_lines(s.snapshot(), p))? .on_sync::(|s, p| handlers::handle_on_enter(s.snapshot(), p))? - .on_sync::(|s, p| { + .on_sync::(|s, p| { handlers::handle_selection_range(s.snapshot(), p) })? .on_sync::(|s, p| { @@ -512,32 +514,38 @@ fn on_request( .on::(handlers::handle_analyzer_status)? .on::(handlers::handle_syntax_tree)? .on::(handlers::handle_expand_macro)? - .on::(handlers::handle_on_type_formatting)? - .on::(handlers::handle_document_symbol)? - .on::(handlers::handle_workspace_symbol)? - .on::(handlers::handle_goto_definition)? - .on::(handlers::handle_goto_implementation)? - .on::(handlers::handle_goto_type_definition)? + .on::(handlers::handle_on_type_formatting)? + .on::(handlers::handle_document_symbol)? + .on::(handlers::handle_workspace_symbol)? + .on::(handlers::handle_goto_definition)? + .on::(handlers::handle_goto_implementation)? + .on::(handlers::handle_goto_type_definition)? .on::(handlers::handle_parent_module)? .on::(handlers::handle_runnables)? - .on::(handlers::handle_completion)? - .on::(handlers::handle_code_action)? - .on::(handlers::handle_code_lens)? - .on::(handlers::handle_code_lens_resolve)? - .on::(handlers::handle_folding_range)? - .on::(handlers::handle_signature_help)? - .on::(handlers::handle_hover)? - .on::(handlers::handle_prepare_rename)? - .on::(handlers::handle_rename)? - .on::(handlers::handle_references)? - .on::(handlers::handle_formatting)? - .on::(handlers::handle_document_highlight)? + .on::(handlers::handle_completion)? + .on::(handlers::handle_code_action)? + .on::(handlers::handle_code_lens)? + .on::(handlers::handle_code_lens_resolve)? + .on::(handlers::handle_folding_range)? + .on::(handlers::handle_signature_help)? + .on::(handlers::handle_hover)? + .on::(handlers::handle_prepare_rename)? + .on::(handlers::handle_rename)? + .on::(handlers::handle_references)? + .on::(handlers::handle_formatting)? + .on::(handlers::handle_document_highlight)? .on::(handlers::handle_inlay_hints)? - .on::(handlers::handle_call_hierarchy_prepare)? - .on::(handlers::handle_call_hierarchy_incoming)? - .on::(handlers::handle_call_hierarchy_outgoing)? - .on::(handlers::handle_semantic_tokens)? - .on::(handlers::handle_semantic_tokens_range)? + .on::(handlers::handle_call_hierarchy_prepare)? + .on::( + handlers::handle_call_hierarchy_incoming, + )? + .on::( + handlers::handle_call_hierarchy_outgoing, + )? + .on::(handlers::handle_semantic_tokens)? + .on::( + handlers::handle_semantic_tokens_range, + )? .on::(handlers::handle_ssr)? .finish(); Ok(()) @@ -549,7 +557,7 @@ fn on_notification( loop_state: &mut LoopState, not: Notification, ) -> Result<()> { - let not = match notification_cast::(not) { + let not = match notification_cast::(not) { Ok(params) => { let id: RequestId = match params.id { NumberOrString::Number(id) => id.into(), @@ -567,7 +575,7 @@ fn on_notification( } Err(not) => not, }; - let not = match notification_cast::(not) { + let not = match notification_cast::(not) { Ok(params) => { let uri = params.text_document.uri; let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; @@ -580,7 +588,7 @@ fn on_notification( } Err(not) => not, }; - let not = match notification_cast::(not) { + let not = match notification_cast::(not) { Ok(params) => { let DidChangeTextDocumentParams { text_document, content_changes } = params; let world = state.snapshot(); @@ -595,7 +603,7 @@ fn on_notification( } Err(not) => not, }; - let not = match notification_cast::(not) { + let not = match notification_cast::(not) { Ok(_params) => { if let Some(flycheck) = &state.flycheck { flycheck.update(); @@ -604,7 +612,7 @@ fn on_notification( } Err(not) => not, }; - let not = match notification_cast::(not) { + let not = match notification_cast::(not) { Ok(params) => { let uri = params.text_document.uri; let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; @@ -612,22 +620,22 @@ fn on_notification( loop_state.subscriptions.remove_sub(FileId(file_id.0)); } let params = - req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None }; - let not = notification_new::(params); + lsp_types::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None }; + let not = notification_new::(params); msg_sender.send(not.into()).unwrap(); return Ok(()); } Err(not) => not, }; - let not = match notification_cast::(not) { + let not = match notification_cast::(not) { Ok(_) => { // As stated in https://github.com/microsoft/language-server-protocol/issues/676, // this notification's parameters should be ignored and the actual config queried separately. let request_id = loop_state.next_request_id(); - let request = request_new::( + let request = request_new::( request_id.clone(), - req::ConfigurationParams { - items: vec![req::ConfigurationItem { + lsp_types::ConfigurationParams { + items: vec![lsp_types::ConfigurationItem { scope_uri: None, section: Some("rust-analyzer".to_string()), }], @@ -640,7 +648,7 @@ fn on_notification( } Err(not) => not, }; - let not = match notification_cast::(not) { + let not = match notification_cast::(not) { Ok(params) => { let mut vfs = state.vfs.write(); for change in params.changes { @@ -742,11 +750,11 @@ fn on_check_task( } CheckTask::Status(progress) => { - let params = req::ProgressParams { - token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), - value: req::ProgressParamsValue::WorkDone(progress), + let params = lsp_types::ProgressParams { + token: lsp_types::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), + value: lsp_types::ProgressParamsValue::WorkDone(progress), }; - let not = notification_new::(params); + let not = notification_new::(params); task_sender.send(Task::Notify(not)).unwrap(); } }; @@ -768,8 +776,8 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender, state: }; let diagnostics = state.diagnostics.diagnostics_for(file_id).cloned().collect(); - let params = req::PublishDiagnosticsParams { uri, diagnostics, version: None }; - let not = notification_new::(params); + let params = lsp_types::PublishDiagnosticsParams { uri, diagnostics, version: None }; + let not = notification_new::(params); msg_sender.send(not.into()).unwrap(); } } @@ -782,10 +790,10 @@ fn send_startup_progress(sender: &Sender, loop_state: &mut LoopState) { match (prev, loop_state.workspace_loaded) { (None, false) => { - let work_done_progress_create = request_new::( + let work_done_progress_create = request_new::( loop_state.next_request_id(), WorkDoneProgressCreateParams { - token: req::ProgressToken::String("rustAnalyzer/startup".into()), + token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), }, ); sender.send(work_done_progress_create.into()).unwrap(); @@ -817,10 +825,11 @@ fn send_startup_progress(sender: &Sender, loop_state: &mut LoopState) { } fn send_startup_progress_notif(sender: &Sender, work_done_progress: WorkDoneProgress) { - let notif = notification_new::(req::ProgressParams { - token: req::ProgressToken::String("rustAnalyzer/startup".into()), - value: req::ProgressParamsValue::WorkDone(work_done_progress), - }); + let notif = + notification_new::(lsp_types::ProgressParams { + token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), + value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), + }); sender.send(notif.into()).unwrap(); } } @@ -842,7 +851,7 @@ impl<'a> PoolDispatcher<'a> { f: fn(&mut WorldState, R::Params) -> Result, ) -> Result<&mut Self> where - R: req::Request + 'static, + R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + panic::UnwindSafe + 'static, R::Result: Serialize + 'static, { @@ -865,7 +874,7 @@ impl<'a> PoolDispatcher<'a> { /// Dispatches the request onto thread pool fn on(&mut self, f: fn(WorldSnapshot, R::Params) -> Result) -> Result<&mut Self> where - R: req::Request + 'static, + R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + Send + 'static, R::Result: Serialize + 'static, { @@ -891,7 +900,7 @@ impl<'a> PoolDispatcher<'a> { fn parse(&mut self) -> Option<(RequestId, R::Params)> where - R: req::Request + 'static, + R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + 'static, { let req = self.req.take()?; @@ -928,7 +937,7 @@ impl<'a> PoolDispatcher<'a> { fn result_to_task(id: RequestId, result: Result) -> Task where - R: req::Request + 'static, + R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + 'static, R::Result: Serialize + 'static, { @@ -984,10 +993,14 @@ fn update_file_notifications_on_threadpool( } } -pub fn show_message(typ: req::MessageType, message: impl Into, sender: &Sender) { +pub fn show_message( + typ: lsp_types::MessageType, + message: impl Into, + sender: &Sender, +) { let message = message.into(); - let params = req::ShowMessageParams { typ, message }; - let not = notification_new::(params); + let params = lsp_types::ShowMessageParams { typ, message }; + let not = notification_new::(params); sender.send(not.into()).unwrap(); } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 4f619654fc..7aa77d19a4 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -81,12 +81,12 @@ pub fn handle_expand_macro( pub fn handle_selection_range( world: WorldSnapshot, - params: req::SelectionRangeParams, -) -> Result>> { + params: lsp_types::SelectionRangeParams, +) -> Result>> { let _p = profile("handle_selection_range"); let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; - let res: Result> = params + let res: Result> = params .positions .into_iter() .map(|position| { @@ -105,12 +105,12 @@ pub fn handle_selection_range( } } } - let mut range = req::SelectionRange { + let mut range = lsp_types::SelectionRange { range: to_proto::range(&line_index, *ranges.last().unwrap()), parent: None, }; for &r in ranges.iter().rev().skip(1) { - range = req::SelectionRange { + range = lsp_types::SelectionRange { range: to_proto::range(&line_index, r), parent: Some(Box::new(range)), } @@ -156,7 +156,7 @@ pub fn handle_join_lines( pub fn handle_on_enter( world: WorldSnapshot, - params: req::TextDocumentPositionParams, + params: lsp_types::TextDocumentPositionParams, ) -> Result> { let _p = profile("handle_on_enter"); let position = from_proto::file_position(&world, params)?; @@ -169,7 +169,7 @@ pub fn handle_on_enter( // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. pub fn handle_on_type_formatting( world: WorldSnapshot, - params: req::DocumentOnTypeFormattingParams, + params: lsp_types::DocumentOnTypeFormattingParams, ) -> Result>> { let _p = profile("handle_on_type_formatting"); let mut position = from_proto::file_position(&world, params.text_document_position)?; @@ -208,8 +208,8 @@ pub fn handle_on_type_formatting( pub fn handle_document_symbol( world: WorldSnapshot, - params: req::DocumentSymbolParams, -) -> Result> { + params: lsp_types::DocumentSymbolParams, +) -> Result> { let _p = profile("handle_document_symbol"); let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; @@ -276,7 +276,7 @@ pub fn handle_document_symbol( pub fn handle_workspace_symbol( world: WorldSnapshot, - params: req::WorkspaceSymbolParams, + params: lsp_types::WorkspaceSymbolParams, ) -> Result>> { let _p = profile("handle_workspace_symbol"); let all_symbols = params.query.contains('#'); @@ -320,8 +320,8 @@ pub fn handle_workspace_symbol( pub fn handle_goto_definition( world: WorldSnapshot, - params: req::GotoDefinitionParams, -) -> Result> { + params: lsp_types::GotoDefinitionParams, +) -> Result> { let _p = profile("handle_goto_definition"); let position = from_proto::file_position(&world, params.text_document_position_params)?; let nav_info = match world.analysis().goto_definition(position)? { @@ -338,8 +338,8 @@ pub fn handle_goto_definition( pub fn handle_goto_implementation( world: WorldSnapshot, - params: req::GotoImplementationParams, -) -> Result> { + params: lsp_types::request::GotoImplementationParams, +) -> Result> { let _p = profile("handle_goto_implementation"); let position = from_proto::file_position(&world, params.text_document_position_params)?; let nav_info = match world.analysis().goto_implementation(position)? { @@ -356,8 +356,8 @@ pub fn handle_goto_implementation( pub fn handle_goto_type_definition( world: WorldSnapshot, - params: req::GotoTypeDefinitionParams, -) -> Result> { + params: lsp_types::request::GotoTypeDefinitionParams, +) -> Result> { let _p = profile("handle_goto_type_definition"); let position = from_proto::file_position(&world, params.text_document_position_params)?; let nav_info = match world.analysis().goto_type_definition(position)? { @@ -374,7 +374,7 @@ pub fn handle_goto_type_definition( pub fn handle_parent_module( world: WorldSnapshot, - params: req::TextDocumentPositionParams, + params: lsp_types::TextDocumentPositionParams, ) -> Result> { let _p = profile("handle_parent_module"); let position = from_proto::file_position(&world, params)?; @@ -447,8 +447,8 @@ pub fn handle_runnables( pub fn handle_completion( world: WorldSnapshot, - params: req::CompletionParams, -) -> Result> { + params: lsp_types::CompletionParams, +) -> Result> { let _p = profile("handle_completion"); let position = from_proto::file_position(&world, params.text_document_position)?; let completion_triggered_after_single_colon = { @@ -506,8 +506,8 @@ pub fn handle_folding_range( pub fn handle_signature_help( world: WorldSnapshot, - params: req::SignatureHelpParams, -) -> Result> { + params: lsp_types::SignatureHelpParams, +) -> Result> { let _p = profile("handle_signature_help"); let position = from_proto::file_position(&world, params.text_document_position_params)?; let call_info = match world.analysis().call_info(position)? { @@ -521,14 +521,14 @@ pub fn handle_signature_help( } let sig_info = to_proto::signature_information(call_info.signature, concise); - Ok(Some(req::SignatureHelp { + Ok(Some(lsp_types::SignatureHelp { signatures: vec![sig_info], active_signature: Some(0), active_parameter, })) } -pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result> { +pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result> { let _p = profile("handle_hover"); let position = from_proto::file_position(&world, params.text_document_position_params)?; let info = match world.analysis().hover(position)? { @@ -549,7 +549,7 @@ pub fn handle_hover(world: WorldSnapshot, params: req::HoverParams) -> Result Result> { let _p = profile("handle_prepare_rename"); let position = from_proto::file_position(&world, params)?; @@ -589,7 +589,7 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result Result>> { let _p = profile("handle_references"); let position = from_proto::file_position(&world, params.text_document_position)?; @@ -692,7 +692,7 @@ pub fn handle_formatting( pub fn handle_code_action( world: WorldSnapshot, - params: req::CodeActionParams, + params: lsp_types::CodeActionParams, ) -> Result> { let _p = profile("handle_code_action"); let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; @@ -806,7 +806,7 @@ pub fn handle_code_action( pub fn handle_code_lens( world: WorldSnapshot, - params: req::CodeLensParams, + params: lsp_types::CodeLensParams, ) -> Result>> { let _p = profile("handle_code_lens"); let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; @@ -875,8 +875,8 @@ pub fn handle_code_lens( .map(|it| { let range = to_proto::range(&line_index, it.node_range); let pos = range.start; - let lens_params = req::GotoImplementationParams { - text_document_position_params: req::TextDocumentPositionParams::new( + let lens_params = lsp_types::request::GotoImplementationParams { + text_document_position_params: lsp_types::TextDocumentPositionParams::new( params.text_document.clone(), pos, ), @@ -897,7 +897,7 @@ pub fn handle_code_lens( #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] enum CodeLensResolveData { - Impls(req::GotoImplementationParams), + Impls(lsp_types::request::GotoImplementationParams), } pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result { @@ -908,9 +908,9 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re Some(CodeLensResolveData::Impls(lens_params)) => { let locations: Vec = match handle_goto_implementation(world, lens_params.clone())? { - Some(req::GotoDefinitionResponse::Scalar(loc)) => vec![loc], - Some(req::GotoDefinitionResponse::Array(locs)) => locs, - Some(req::GotoDefinitionResponse::Link(links)) => links + Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], + Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, + Some(lsp_types::GotoDefinitionResponse::Link(links)) => links .into_iter() .map(|link| Location::new(link.target_uri, link.target_selection_range)) .collect(), @@ -947,7 +947,7 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re pub fn handle_document_highlight( world: WorldSnapshot, - params: req::DocumentHighlightParams, + params: lsp_types::DocumentHighlightParams, ) -> Result>> { let _p = profile("handle_document_highlight"); let position = from_proto::file_position(&world, params.text_document_position_params)?; diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs index 0dae6bad4f..aeb29370c0 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/req.rs @@ -1,23 +1,10 @@ //! Defines `rust-analyzer` specific custom messages. +use lsp_types::request::Request; use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; -pub use lsp_types::{ - notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, - CodeLensParams, CompletionParams, CompletionResponse, ConfigurationItem, ConfigurationParams, - DiagnosticTag, DidChangeConfigurationParams, DidChangeWatchedFilesParams, - DidChangeWatchedFilesRegistrationOptions, DocumentHighlightParams, - DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, - FileSystemWatcher, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams, - InitializeResult, MessageType, PartialResultParams, ProgressParams, ProgressParamsValue, - ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, - SelectionRange, SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams, - SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams, - SignatureHelp, SignatureHelpParams, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, - TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams, -}; use std::path::PathBuf; pub enum AnalyzerStatus {} @@ -91,7 +78,7 @@ pub struct FindMatchingBraceParams { pub enum ParentModule {} impl Request for ParentModule { - type Params = TextDocumentPositionParams; + type Params = lsp_types::TextDocumentPositionParams; type Result = Vec; const METHOD: &'static str = "rust-analyzer/parentModule"; } @@ -114,7 +101,7 @@ pub struct JoinLinesParams { pub enum OnEnter {} impl Request for OnEnter { - type Params = TextDocumentPositionParams; + type Params = lsp_types::TextDocumentPositionParams; type Result = Option; const METHOD: &'static str = "rust-analyzer/onEnter"; } @@ -150,8 +137,8 @@ pub struct Runnable { #[serde(rename_all = "camelCase")] pub struct SourceChange { pub label: String, - pub workspace_edit: WorkspaceEdit, - pub cursor_position: Option, + pub workspace_edit: lsp_types::WorkspaceEdit, + pub cursor_position: Option, } pub enum InlayHints {} From 3bec2be9de400dc044ae924cb0ae36faa454d111 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 10 May 2020 19:25:37 +0200 Subject: [PATCH 5/5] req -> lsp_ext --- crates/rust-analyzer/src/lib.rs | 3 +- .../rust-analyzer/src/{req.rs => lsp_ext.rs} | 6 +-- crates/rust-analyzer/src/main_loop.rs | 25 ++++++------ .../rust-analyzer/src/main_loop/handlers.rs | 38 +++++++++++-------- crates/rust-analyzer/src/to_proto.rs | 16 ++++---- .../rust-analyzer/tests/heavy_tests/main.rs | 17 +++++---- .../tests/heavy_tests/support.rs | 6 +-- 7 files changed, 58 insertions(+), 53 deletions(-) rename crates/rust-analyzer/src/{req.rs => lsp_ext.rs} (98%) diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index 6d719108c3..57d0e92188 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs @@ -24,8 +24,7 @@ mod to_proto; mod from_proto; mod main_loop; mod markdown; -// TODO: rename to lsp_ext -pub mod req; +pub mod lsp_ext; pub mod config; mod world; mod diagnostics; diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/lsp_ext.rs similarity index 98% rename from crates/rust-analyzer/src/req.rs rename to crates/rust-analyzer/src/lsp_ext.rs index aeb29370c0..313a8c7697 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -1,12 +1,12 @@ -//! Defines `rust-analyzer` specific custom messages. +//! rust-analyzer extensions to the LSP. + +use std::path::PathBuf; use lsp_types::request::Request; use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; - pub enum AnalyzerStatus {} impl Request for AnalyzerStatus { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 333ecc8598..fa72a9cc64 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -38,12 +38,11 @@ use threadpool::ThreadPool; use crate::{ config::{Config, FilesWatcher}, diagnostics::DiagnosticTask, - from_proto, + from_proto, lsp_ext, main_loop::{ pending_requests::{PendingRequest, PendingRequests}, subscriptions::Subscriptions, }, - req, world::{WorldSnapshot, WorldState}, Result, }; @@ -502,26 +501,27 @@ fn on_request( request_received, }; pool_dispatcher - .on_sync::(|s, ()| Ok(s.collect_garbage()))? - .on_sync::(|s, p| handlers::handle_join_lines(s.snapshot(), p))? - .on_sync::(|s, p| handlers::handle_on_enter(s.snapshot(), p))? + .on_sync::(|s, ()| Ok(s.collect_garbage()))? + .on_sync::(|s, p| handlers::handle_join_lines(s.snapshot(), p))? + .on_sync::(|s, p| handlers::handle_on_enter(s.snapshot(), p))? .on_sync::(|s, p| { handlers::handle_selection_range(s.snapshot(), p) })? - .on_sync::(|s, p| { + .on_sync::(|s, p| { handlers::handle_find_matching_brace(s.snapshot(), p) })? - .on::(handlers::handle_analyzer_status)? - .on::(handlers::handle_syntax_tree)? - .on::(handlers::handle_expand_macro)? + .on::(handlers::handle_analyzer_status)? + .on::(handlers::handle_syntax_tree)? + .on::(handlers::handle_expand_macro)? + .on::(handlers::handle_parent_module)? + .on::(handlers::handle_runnables)? + .on::(handlers::handle_inlay_hints)? .on::(handlers::handle_on_type_formatting)? .on::(handlers::handle_document_symbol)? .on::(handlers::handle_workspace_symbol)? .on::(handlers::handle_goto_definition)? .on::(handlers::handle_goto_implementation)? .on::(handlers::handle_goto_type_definition)? - .on::(handlers::handle_parent_module)? - .on::(handlers::handle_runnables)? .on::(handlers::handle_completion)? .on::(handlers::handle_code_action)? .on::(handlers::handle_code_lens)? @@ -534,7 +534,6 @@ fn on_request( .on::(handlers::handle_references)? .on::(handlers::handle_formatting)? .on::(handlers::handle_document_highlight)? - .on::(handlers::handle_inlay_hints)? .on::(handlers::handle_call_hierarchy_prepare)? .on::( handlers::handle_call_hierarchy_incoming, @@ -546,7 +545,7 @@ fn on_request( .on::( handlers::handle_semantic_tokens_range, )? - .on::(handlers::handle_ssr)? + .on::(handlers::handle_ssr)? .finish(); Ok(()) } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 7aa77d19a4..be8688bc39 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -34,7 +34,7 @@ use crate::{ config::RustfmtConfig, diagnostics::DiagnosticTask, from_json, from_proto, - req::{self, InlayHint, InlayHintsParams}, + lsp_ext::{self, InlayHint, InlayHintsParams}, to_proto, world::WorldSnapshot, LspError, Result, @@ -52,7 +52,10 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { Ok(buf) } -pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) -> Result { +pub fn handle_syntax_tree( + world: WorldSnapshot, + params: lsp_ext::SyntaxTreeParams, +) -> Result { let _p = profile("handle_syntax_tree"); let id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(id)?; @@ -63,8 +66,8 @@ pub fn handle_syntax_tree(world: WorldSnapshot, params: req::SyntaxTreeParams) - pub fn handle_expand_macro( world: WorldSnapshot, - params: req::ExpandMacroParams, -) -> Result> { + params: lsp_ext::ExpandMacroParams, +) -> Result> { let _p = profile("handle_expand_macro"); let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; @@ -74,7 +77,7 @@ pub fn handle_expand_macro( None => Ok(None), Some(offset) => { let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; - Ok(res.map(|it| req::ExpandedMacro { name: it.name, expansion: it.expansion })) + Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) } } } @@ -124,7 +127,7 @@ pub fn handle_selection_range( pub fn handle_find_matching_brace( world: WorldSnapshot, - params: req::FindMatchingBraceParams, + params: lsp_ext::FindMatchingBraceParams, ) -> Result> { let _p = profile("handle_find_matching_brace"); let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; @@ -146,8 +149,8 @@ pub fn handle_find_matching_brace( pub fn handle_join_lines( world: WorldSnapshot, - params: req::JoinLinesParams, -) -> Result { + params: lsp_ext::JoinLinesParams, +) -> Result { let _p = profile("handle_join_lines"); let frange = from_proto::file_range(&world, params.text_document, params.range)?; let source_change = world.analysis().join_lines(frange)?; @@ -157,7 +160,7 @@ pub fn handle_join_lines( pub fn handle_on_enter( world: WorldSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> Result> { let _p = profile("handle_on_enter"); let position = from_proto::file_position(&world, params)?; match world.analysis().on_enter(position)? { @@ -388,8 +391,8 @@ pub fn handle_parent_module( pub fn handle_runnables( world: WorldSnapshot, - params: req::RunnablesParams, -) -> Result> { + params: lsp_ext::RunnablesParams, +) -> Result> { let _p = profile("handle_runnables"); let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; let line_index = world.analysis().file_line_index(file_id)?; @@ -419,7 +422,7 @@ pub fn handle_runnables( match cargo_spec { Some(spec) => { for &cmd in ["check", "test"].iter() { - res.push(req::Runnable { + res.push(lsp_ext::Runnable { range: Default::default(), label: format!("cargo {} -p {}", cmd, spec.package), bin: "cargo".to_string(), @@ -431,7 +434,7 @@ pub fn handle_runnables( } } None => { - res.push(req::Runnable { + res.push(lsp_ext::Runnable { range: Default::default(), label: "cargo check --workspace".to_string(), bin: "cargo".to_string(), @@ -972,7 +975,10 @@ pub fn handle_document_highlight( Ok(Some(res)) } -pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result { +pub fn handle_ssr( + world: WorldSnapshot, + params: lsp_ext::SsrParams, +) -> Result { let _p = profile("handle_ssr"); let source_change = world.analysis().structural_search_replace(¶ms.query, params.parse_only)??; @@ -1003,7 +1009,7 @@ fn to_lsp_runnable( world: &WorldSnapshot, file_id: FileId, runnable: Runnable, -) -> Result { +) -> Result { let spec = CargoTargetSpec::for_file(world, file_id)?; let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; let line_index = world.analysis().file_line_index(file_id)?; @@ -1014,7 +1020,7 @@ fn to_lsp_runnable( RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), RunnableKind::Bin => "run binary".to_string(), }; - Ok(req::Runnable { + Ok(lsp_ext::Runnable { range: to_proto::range(&line_index, runnable.range), label, bin: "cargo".to_string(), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 4157ce8ea1..4500d49827 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -10,7 +10,7 @@ use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_text_edit::{Indel, TextEdit}; use ra_vfs::LineEndings; -use crate::{req, semantic_tokens, world::WorldSnapshot, Result}; +use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { let line_col = line_index.line_col(offset); @@ -215,14 +215,14 @@ pub(crate) fn signature_information( lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) } } -pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> req::InlayHint { - req::InlayHint { +pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ext::InlayHint { + lsp_ext::InlayHint { label: inlay_hint.label.to_string(), range: range(line_index, inlay_hint.range), kind: match inlay_hint.kind { - InlayKind::ParameterHint => req::InlayKind::ParameterHint, - InlayKind::TypeHint => req::InlayKind::TypeHint, - InlayKind::ChainingHint => req::InlayKind::ChainingHint, + InlayKind::ParameterHint => lsp_ext::InlayKind::ParameterHint, + InlayKind::TypeHint => lsp_ext::InlayKind::TypeHint, + InlayKind::ChainingHint => lsp_ext::InlayKind::ChainingHint, }, } } @@ -478,7 +478,7 @@ pub(crate) fn resource_op( pub(crate) fn source_change( world: &WorldSnapshot, source_change: SourceChange, -) -> Result { +) -> Result { let cursor_position = match source_change.cursor_position { None => None, Some(pos) => { @@ -513,7 +513,7 @@ pub(crate) fn source_change( changes: None, document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), }; - Ok(req::SourceChange { label: source_change.label, workspace_edit, cursor_position }) + Ok(lsp_ext::SourceChange { label: source_change.label, workspace_edit, cursor_position }) } pub fn call_hierarchy_item( diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index e459e3a3ce..5011cc2734 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -3,15 +3,16 @@ mod support; use std::{collections::HashMap, path::PathBuf, time::Instant}; use lsp_types::{ - CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, - GotoDefinitionParams, HoverParams, PartialResultParams, Position, Range, TextDocumentItem, - TextDocumentPositionParams, WorkDoneProgressParams, -}; -use rust_analyzer::req::{ - CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, - Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, OnEnter, Runnables, - RunnablesParams, + notification::DidOpenTextDocument, + request::{ + CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, + }, + CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, + DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, + PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, + WorkDoneProgressParams, }; +use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; use serde_json::json; use tempfile::TempDir; use test_utils::skip_slow_tests; diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 8d47ee4f64..8756ad4a3c 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -13,15 +13,15 @@ use lsp_types::{ request::Shutdown, DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress, }; +use lsp_types::{ProgressParams, ProgressParamsValue}; use serde::Serialize; use serde_json::{to_string_pretty, Value}; use tempfile::TempDir; use test_utils::{find_mismatch, parse_fixture}; -use req::{ProgressParams, ProgressParamsValue}; use rust_analyzer::{ config::{ClientCapsConfig, Config}, - main_loop, req, + main_loop, }; pub struct Project<'a> { @@ -206,7 +206,7 @@ impl Server { Message::Notification(n) if n.method == "$/progress" => { match n.clone().extract::("$/progress").unwrap() { ProgressParams { - token: req::ProgressToken::String(ref token), + token: lsp_types::ProgressToken::String(ref token), value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), } if token == "rustAnalyzer/startup" => true, _ => false,