Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inlay hints backend into the server #1549

Merged
merged 12 commits into from
Jul 23, 2019
110 changes: 110 additions & 0 deletions crates/ra_ide_api/src/inlay_hints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use ra_syntax::{
algo::visit::{visitor, Visitor},
ast::{self, PatKind, TypeAscriptionOwner},
AstNode, SmolStr, SourceFile, SyntaxNode, TextRange,
};

#[derive(Debug, PartialEq, Eq)]
pub enum InlayKind {
LetBinding,
ClosureParameter,
}

#[derive(Debug)]
pub struct InlayHint {
pub range: TextRange,
pub text: SmolStr,
pub inlay_kind: InlayKind,
}

pub(crate) fn inlay_hints(file: &SourceFile) -> Vec<InlayHint> {
file.syntax().descendants().map(|node| get_inlay_hints(&node)).flatten().collect()
}

fn get_inlay_hints(node: &SyntaxNode) -> Vec<InlayHint> {
visitor()
.visit(|let_statement: ast::LetStmt| {
let let_syntax = let_statement.syntax();

if let_statement.ascribed_type().is_some() {
return Vec::new();
}

let pat_range = match let_statement.pat().map(|pat| pat.kind()) {
Some(PatKind::BindPat(bind_pat)) => bind_pat.syntax().text_range(),
Some(PatKind::TuplePat(tuple_pat)) => tuple_pat.syntax().text_range(),
_ => return Vec::new(),
};

vec![InlayHint {
range: pat_range,
text: let_syntax.text().to_smol_string(),
inlay_kind: InlayKind::LetBinding,
}]
})
.visit(|closure_parameter: ast::LambdaExpr| {
if let Some(param_list) = closure_parameter.param_list() {
param_list
.params()
.filter(|closure_param| closure_param.ascribed_type().is_none())
.map(|closure_param| {
let closure_param_syntax = closure_param.syntax();
InlayHint {
range: closure_param_syntax.text_range(),
text: closure_param_syntax.text().to_smol_string(),
inlay_kind: InlayKind::ClosureParameter,
}
})
.collect()
} else {
Vec::new()
}
})
.accept(&node)
.unwrap_or_else(Vec::new)
}

#[cfg(test)]
mod tests {
use super::*;
use insta::assert_debug_snapshot_matches;

#[test]
fn test_inlay_hints() {
let file = SourceFile::parse(
r#"
struct OuterStruct {}

fn main() {
struct InnerStruct {}

let test = 54;
let test = InnerStruct {};
let test = OuterStruct {};
let test = vec![222];
let mut test = Vec::new();
test.push(333);
let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>();
let mut test = 33;
let _ = 22;
let test: Vec<_> = (0..3).collect();

let _ = (0..23).map(|i: u32| {
let i_squared = i * i;
i_squared
});

let test: i32 = 33;

let (x, c) = (42, 'a');
let test = (42, 'a');
}

"#,
)
.ok()
.unwrap();
let hints = inlay_hints(&file);
assert_debug_snapshot_matches!("inlay_hints", hints);
}
}
7 changes: 7 additions & 0 deletions crates/ra_ide_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mod join_lines;
mod typing;
mod matching_brace;
mod display;
mod inlay_hints;

#[cfg(test)]
mod marks;
Expand All @@ -64,6 +65,7 @@ pub use crate::{
display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
folding_ranges::{Fold, FoldKind},
hover::HoverResult,
inlay_hints::{InlayHint, InlayKind},
line_index::{LineCol, LineIndex},
line_index_utils::translate_offset_with_edit,
references::ReferenceSearchResult,
Expand Down Expand Up @@ -396,6 +398,11 @@ impl Analysis {
file_structure(&parse.tree())
}

/// Returns a list of the places in the file where type hints can be displayed.
pub fn inlay_hints(&self, file_id: FileId) -> Vec<InlayHint> {
inlay_hints::inlay_hints(&self.db.parse(file_id).tree())
}

/// Returns the set of folding ranges.
pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
let parse = self.db.parse(file_id);
Expand Down
63 changes: 63 additions & 0 deletions crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
created: "2019-07-20T20:13:53.385368Z"
creator: insta@0.8.1
source: crates/ra_ide_api/src/inlay_hints.rs
expression: hints
---
[
InlayHint {
range: [71; 75),
text: "let test = 54;",
inlay_kind: LetBinding,
},
InlayHint {
range: [90; 94),
text: "let test = InnerStruct {};",
inlay_kind: LetBinding,
},
InlayHint {
range: [121; 125),
text: "let test = OuterStruct {};",
inlay_kind: LetBinding,
},
InlayHint {
range: [152; 156),
text: "let test = vec![222];",
inlay_kind: LetBinding,
},
InlayHint {
range: [178; 186),
text: "let mut test = Vec::new();",
inlay_kind: LetBinding,
},
InlayHint {
range: [229; 233),
text: "let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>();",
inlay_kind: LetBinding,
},
InlayHint {
range: [258; 259),
text: "i",
inlay_kind: ClosureParameter,
},
InlayHint {
range: [297; 305),
text: "let mut test = 33;",
inlay_kind: LetBinding,
},
InlayHint {
range: [417; 426),
text: "let i_squared = i * i;",
inlay_kind: LetBinding,
},
InlayHint {
range: [500; 506),
text: "let (x, c) = (42, \'a\');",
inlay_kind: LetBinding,
},
InlayHint {
range: [528; 532),
text: "let test = (42, \'a\');",
inlay_kind: LetBinding,
},
]
34 changes: 29 additions & 5 deletions crates/ra_lsp_server/src/main_loop/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use lsp_types::{
TextDocumentIdentifier, TextEdit, WorkspaceEdit,
};
use ra_ide_api::{
AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity,
AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, InlayKind, Query,
RunnableKind, Severity,
};
use ra_prof::profile;
use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
Expand Down Expand Up @@ -685,13 +686,14 @@ pub fn handle_code_lens(
params: req::CodeLensParams,
) -> Result<Option<Vec<CodeLens>>> {
let file_id = params.text_document.try_conv_with(&world)?;
let line_index = world.analysis().file_line_index(file_id);
let analysis = world.analysis();
let line_index = analysis.file_line_index(file_id);

let mut lenses: Vec<CodeLens> = Default::default();
let workspace_root = world.workspace_root_for(file_id);

// Gather runnables
for runnable in world.analysis().runnables(file_id)? {
for runnable in analysis.runnables(file_id)? {
let title = match &runnable.kind {
RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => Some("▶️Run Test"),
RunnableKind::Bench { .. } => Some("Run Bench"),
Expand Down Expand Up @@ -728,8 +730,7 @@ pub fn handle_code_lens(

// Handle impls
lenses.extend(
world
.analysis()
analysis
.file_structure(file_id)
.into_iter()
.filter(|it| match it.kind {
Expand All @@ -749,6 +750,29 @@ pub fn handle_code_lens(
}),
);

lenses.extend(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be moved eventually into a separate LSP requestion right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, removed now to avoid confusion.
I use it currently to do some sort of a final integration testing.

analysis
.inlay_hints(file_id)
.into_iter()
.filter(|hint| hint.inlay_kind == InlayKind::LetBinding)
.filter_map(|inlay_hint| {
let resolved_type = analysis
.type_of(FileRange { range: inlay_hint.range, file_id })
.ok()
.and_then(std::convert::identity)
.filter(|resolved_type| "{unknown}" != resolved_type);
resolved_type.map(|resolved_type| (resolved_type, inlay_hint.range))
})
.map(|(resolved_type, range)| CodeLens {
range: range.conv_with(&line_index),
command: Some(Command {
title: resolved_type,
command: String::new(),
arguments: None,
}),
data: None,
}),
);
Ok(Some(lenses))
}

Expand Down