Basic DocumentHighlightKind support for assignments

This commit is contained in:
Jeremy Kolb 2020-01-04 17:46:01 -05:00 committed by kjeremy
parent 19eb7fa1db
commit d993f329a0
5 changed files with 107 additions and 17 deletions

View File

@ -75,7 +75,7 @@ pub use crate::{
inlay_hints::{InlayHint, InlayKind}, inlay_hints::{InlayHint, InlayKind},
line_index::{LineCol, LineIndex}, line_index::{LineCol, LineIndex},
line_index_utils::translate_offset_with_edit, line_index_utils::translate_offset_with_edit,
references::{Reference, ReferenceKind, ReferenceSearchResult, SearchScope}, references::{Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope},
runnables::{Runnable, RunnableKind}, runnables::{Runnable, RunnableKind},
source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
syntax_highlighting::HighlightedRange, syntax_highlighting::HighlightedRange,

View File

@ -19,8 +19,8 @@ use once_cell::unsync::Lazy;
use ra_db::{SourceDatabase, SourceDatabaseExt}; use ra_db::{SourceDatabase, SourceDatabaseExt};
use ra_prof::profile; use ra_prof::profile;
use ra_syntax::{ use ra_syntax::{
algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextUnit, algo::find_node_at_offset, ast, match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode,
TokenAtOffset, TextUnit, TokenAtOffset,
}; };
use crate::{ use crate::{
@ -46,6 +46,7 @@ pub struct ReferenceSearchResult {
pub struct Reference { pub struct Reference {
pub file_range: FileRange, pub file_range: FileRange,
pub kind: ReferenceKind, pub kind: ReferenceKind,
pub access: Option<ReferenceAccess>,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -54,6 +55,12 @@ pub enum ReferenceKind {
Other, Other,
} }
#[derive(Debug, Clone, PartialEq)]
pub enum ReferenceAccess {
Read,
Write,
}
impl ReferenceSearchResult { impl ReferenceSearchResult {
pub fn declaration(&self) -> &NavigationTarget { pub fn declaration(&self) -> &NavigationTarget {
&self.declaration &self.declaration
@ -72,7 +79,7 @@ impl ReferenceSearchResult {
} }
// allow turning ReferenceSearchResult into an iterator // allow turning ReferenceSearchResult into an iterator
// over FileRanges // over References
impl IntoIterator for ReferenceSearchResult { impl IntoIterator for ReferenceSearchResult {
type Item = Reference; type Item = Reference;
type IntoIter = std::vec::IntoIter<Reference>; type IntoIter = std::vec::IntoIter<Reference>;
@ -85,6 +92,7 @@ impl IntoIterator for ReferenceSearchResult {
range: self.declaration.range(), range: self.declaration.range(),
}, },
kind: self.declaration_kind, kind: self.declaration_kind,
access: None,
}); });
v.append(&mut self.references); v.append(&mut self.references);
v.into_iter() v.into_iter()
@ -201,7 +209,13 @@ fn process_definition(
} else { } else {
ReferenceKind::Other ReferenceKind::Other
}; };
refs.push(Reference { file_range: FileRange { file_id, range }, kind }); let access = access_mode(d.kind, &name_ref);
refs.push(Reference {
file_range: FileRange { file_id, range },
kind,
access,
});
} }
} }
} }
@ -210,11 +224,46 @@ fn process_definition(
refs refs
} }
fn access_mode(kind: NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
match kind {
NameKind::Local(_) | NameKind::Field(_) => {
//LetExpr or BinExpr
name_ref.syntax().ancestors().find_map(|node| {
match_ast! {
match (node) {
ast::BinExpr(expr) => {
match expr.op_kind() {
Some(kind) if kind.is_assignment() => {
if let Some(lhs) = expr.lhs() {
if lhs.syntax().text_range() == name_ref.syntax().text_range() {
return Some(ReferenceAccess::Write);
}
}
if let Some(rhs) = expr.rhs() {
if rhs.syntax().text_range().is_subrange(&name_ref.syntax().text_range()) {
return Some(ReferenceAccess::Read);
}
}
},
_ => { return Some(ReferenceAccess::Read) },
}
None
},
_ => {None}
}
}
})
}
_ => None,
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis},
Reference, ReferenceKind, ReferenceSearchResult, SearchScope, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope,
}; };
#[test] #[test]
@ -515,6 +564,20 @@ mod tests {
); );
} }
#[test]
fn test_basic_highlight_read() {
let code = r#"
fn foo() {
let i<|> = 0;
i = i + 1;
}"#;
let refs = get_all_refs(code);
assert_eq!(refs.len(), 3);
assert_eq!(refs.references[0].access, Some(ReferenceAccess::Write));
assert_eq!(refs.references[1].access, Some(ReferenceAccess::Read));
}
fn get_all_refs(text: &str) -> ReferenceSearchResult { fn get_all_refs(text: &str) -> ReferenceSearchResult {
let (analysis, position) = single_file_with_position(text); let (analysis, position) = single_file_with_position(text);
analysis.find_all_refs(position, None).unwrap().unwrap() analysis.find_all_refs(position, None).unwrap().unwrap()

View File

@ -9,7 +9,7 @@ use lsp_types::{
use ra_ide::{ use ra_ide::{
translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex,
NavigationTarget, RangeInfo, Severity, SourceChange, SourceFileEdit, NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit,
}; };
use ra_syntax::{SyntaxKind, TextRange, TextUnit}; use ra_syntax::{SyntaxKind, TextRange, TextUnit};
use ra_text_edit::{AtomTextEdit, TextEdit}; use ra_text_edit::{AtomTextEdit, TextEdit};
@ -53,6 +53,18 @@ impl Conv for SyntaxKind {
} }
} }
impl Conv for ReferenceAccess {
type Output = ::lsp_types::DocumentHighlightKind;
fn conv(self) -> Self::Output {
use lsp_types::DocumentHighlightKind::*;
match self {
ReferenceAccess::Read => Read,
ReferenceAccess::Write => Write,
}
}
}
impl Conv for CompletionItemKind { impl Conv for CompletionItemKind {
type Output = ::lsp_types::CompletionItemKind; type Output = ::lsp_types::CompletionItemKind;

View File

@ -536,18 +536,32 @@ pub fn handle_references(
let locations = if params.context.include_declaration { let locations = if params.context.include_declaration {
refs.into_iter() refs.into_iter()
.filter_map(|r| { .filter_map(|reference| {
let line_index = world.analysis().file_line_index(r.file_range.file_id).ok()?; let line_index =
to_location(r.file_range.file_id, r.file_range.range, &world, &line_index).ok() 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()
}) })
.collect() .collect()
} else { } else {
// Only iterate over the references if include_declaration was false // Only iterate over the references if include_declaration was false
refs.references() refs.references()
.iter() .iter()
.filter_map(|r| { .filter_map(|reference| {
let line_index = world.analysis().file_line_index(r.file_range.file_id).ok()?; let line_index =
to_location(r.file_range.file_id, r.file_range.range, &world, &line_index).ok() 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()
}) })
.collect() .collect()
}; };
@ -836,10 +850,10 @@ pub fn handle_document_highlight(
Ok(Some( Ok(Some(
refs.into_iter() refs.into_iter()
.filter(|r| r.file_range.file_id == file_id) .filter(|reference| reference.file_range.file_id == file_id)
.map(|r| DocumentHighlight { .map(|reference| DocumentHighlight {
range: r.file_range.range.conv_with(&line_index), range: reference.file_range.range.conv_with(&line_index),
kind: None, kind: reference.access.map(|it| it.conv()),
}) })
.collect(), .collect(),
)) ))

View File

@ -144,6 +144,7 @@ impl BinOp {
} }
} }
} }
impl ast::BinExpr { impl ast::BinExpr {
pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> { pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| { self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {