diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 3879da6d03..5b2384a054 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -87,7 +87,7 @@ pub use crate::{ references::ReferenceSearchResult, rename::RenameError, runnables::{Runnable, RunnableKind, TestId}, - static_index::{StaticIndex, StaticIndexedFile}, + static_index::{StaticIndex, StaticIndexedFile, TokenStaticData}, syntax_highlighting::{ tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag}, HlRange, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 806230544f..ab7a829bca 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -2,11 +2,13 @@ //! read-only code browsers and emitting LSIF use hir::{db::HirDatabase, Crate, Module}; -use ide_db::base_db::{FileId, SourceDatabaseExt}; +use ide_db::base_db::{FileId, FileRange, SourceDatabaseExt}; use ide_db::RootDatabase; use rustc_hash::FxHashSet; +use syntax::TextRange; +use syntax::{AstNode, SyntaxKind::*, T}; -use crate::{Analysis, Cancellable, Fold}; +use crate::{Analysis, Cancellable, Fold, HoverConfig, HoverDocFormat, HoverResult}; /// A static representation of fully analyzed source code. /// @@ -15,9 +17,15 @@ pub struct StaticIndex { pub files: Vec, } +pub struct TokenStaticData { + pub range: TextRange, + pub hover: Option, +} + pub struct StaticIndexedFile { pub file_id: FileId, pub folds: Vec, + pub tokens: Vec, } fn all_modules(db: &dyn HirDatabase) -> Vec { @@ -46,17 +54,48 @@ impl StaticIndex { let mut result_files = Vec::::new(); for module in work { let file_id = module.definition_source(db).file_id.original_file(db); - if !visited_files.contains(&file_id) { - //let path = vfs.file_path(file_id); - //let path = path.as_path().unwrap(); - //let doc_id = lsif.add(Element::Vertex(Vertex::Document(Document { - // language_id: Language::Rust, - // uri: lsp_types::Url::from_file_path(path).unwrap(), - //}))); - let folds = analysis.folding_ranges(file_id)?; - result_files.push(StaticIndexedFile { file_id, folds }); - visited_files.insert(file_id); + if visited_files.contains(&file_id) { + continue; } + let folds = analysis.folding_ranges(file_id)?; + // hovers + let sema = hir::Semantics::new(db); + let tokens_or_nodes = sema.parse(file_id).syntax().clone(); + let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|x| match x { + syntax::NodeOrToken::Node(_) => None, + syntax::NodeOrToken::Token(x) => Some(x), + }); + let hover_config = + HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }; + let tokens = tokens + .filter(|token| match token.kind() { + IDENT + | INT_NUMBER + | LIFETIME_IDENT + | T![self] + | T![super] + | T![crate] + | T!['('] + | T![')'] => true, + _ => false, + }) + .map(|token| { + let range = token.text_range(); + let hover = analysis + .hover( + &hover_config, + FileRange { + file_id, + range: TextRange::new(range.start(), range.start()), + }, + )? + .map(|x| x.info); + Ok(TokenStaticData { range, hover }) + }) + .collect::, _>>()?; + result_files.push(StaticIndexedFile { file_id, folds, tokens }); + // mark the file + visited_files.insert(file_id); } Ok(StaticIndex { files: result_files }) } diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 2ba965a15d..12a9919e36 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -2,11 +2,11 @@ use std::env; -use ide::{StaticIndex, StaticIndexedFile}; +use ide::{StaticIndex, StaticIndexedFile, TokenStaticData}; use ide_db::LineIndexDatabase; use ide_db::base_db::salsa::{self, ParallelDatabase}; -use lsp_types::NumberOrString; +use lsp_types::{Hover, HoverContents, NumberOrString}; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; use vfs::AbsPathBuf; @@ -56,6 +56,38 @@ impl LsifManager { fn emit(&self, data: &str) { println!("{}", data); } + + fn add_tokens( + &mut self, + line_index: &LineIndex, + doc_id: Id, + tokens: Vec, + ) { + let tokens_id = tokens + .into_iter() + .map(|token| { + let token_id = self + .add(Element::Vertex(Vertex::Range(to_proto::range(line_index, token.range)))); + if let Some(hover) = token.hover { + let hover_id = self.add(Element::Vertex(Vertex::HoverResult { + result: Hover { + contents: HoverContents::Markup(to_proto::markup_content(hover.markup)), + range: None, + }, + })); + self.add(Element::Edge(Edge::Hover(EdgeData { + in_v: hover_id.into(), + out_v: token_id.into(), + }))); + } + token_id.into() + }) + .collect(); + self.add(Element::Edge(Edge::Contains(EdgeDataMultiIn { + in_vs: tokens_id, + out_v: doc_id.into(), + }))); + } } impl flags::Lsif { @@ -85,7 +117,7 @@ impl flags::Lsif { position_encoding: Encoding::Utf16, tool_info: None, })); - for StaticIndexedFile { file_id, folds } in si.files { + for StaticIndexedFile { file_id, folds, tokens } in si.files { let path = vfs.file_path(file_id); let path = path.as_path().unwrap(); let doc_id = lsif.add(Element::Vertex(Vertex::Document(Document { @@ -94,26 +126,21 @@ impl flags::Lsif { }))); let text = analysis.file_text(file_id)?; let line_index = db.line_index(file_id); + let line_index = LineIndex { + index: line_index.clone(), + encoding: OffsetEncoding::Utf16, + endings: LineEndings::Unix, + }; let result = folds .into_iter() - .map(|it| { - to_proto::folding_range( - &*text, - &LineIndex { - index: line_index.clone(), - encoding: OffsetEncoding::Utf16, - endings: LineEndings::Unix, - }, - false, - it, - ) - }) + .map(|it| to_proto::folding_range(&*text, &line_index, false, it)) .collect(); let folding_id = lsif.add(Element::Vertex(Vertex::FoldingRangeResult { result })); lsif.add(Element::Edge(Edge::FoldingRange(EdgeData { in_v: folding_id.into(), out_v: doc_id.into(), }))); + lsif.add_tokens(&line_index, doc_id, tokens); } Ok(()) } diff --git a/crates/rust-analyzer/src/cli/lsif/lsif_types.rs b/crates/rust-analyzer/src/cli/lsif/lsif_types.rs index 1681840f29..7bb59f6429 100644 --- a/crates/rust-analyzer/src/cli/lsif/lsif_types.rs +++ b/crates/rust-analyzer/src/cli/lsif/lsif_types.rs @@ -1,7 +1,7 @@ //! This module provides LSIF types. This module is a temporary solution //! and it will go to its own repository in future -use lsp_types::FoldingRange; +use lsp_types::{FoldingRange, Hover}; use serde::{Deserialize, Serialize}; pub(crate) type RangeId = lsp_types::NumberOrString; @@ -82,13 +82,16 @@ pub(crate) enum Vertex { FoldingRangeResult { result: Vec, }, + HoverResult { + result: Hover, + } } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(tag = "label")] pub(crate) enum Edge { - Contains(EdgeData), + Contains(EdgeDataMultiIn), RefersTo(EdgeData), Item(Item), @@ -122,6 +125,15 @@ pub(crate) struct EdgeData { pub(crate) out_v: lsp_types::NumberOrString, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct EdgeDataMultiIn { + pub(crate) in_vs: Vec, + pub(crate) out_v: lsp_types::NumberOrString, +} + + + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub(crate) enum DefinitionResultType { @@ -232,25 +244,6 @@ mod tests { assert_eq!(serde_json::from_str::(&text).unwrap(), data); } - #[test] - fn contains() { - let data = Entry { - id: lsp_types::NumberOrString::Number(5), - data: Element::Edge(Edge::Contains(EdgeData { - in_v: lsp_types::NumberOrString::Number(4), - out_v: lsp_types::NumberOrString::Number(1), - })), - }; - - let text = r#"{ "id": 5, "type": "edge", "label": "contains", "outV": 1, "inV": 4}"# - .replace(' ', ""); - - assert_eq!( - serde_json::from_str::(&text).unwrap(), - serde_json::to_value(&data).unwrap() - ); - } - #[test] fn refers_to() { let data = Entry {