From c6247f74c72857de3619a080698237d58ff9e960 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 27 Feb 2020 16:05:35 +0100 Subject: Basic injections --- crates/ra_hir_ty/src/tests.rs | 4 +-- crates/ra_ide/src/call_info.rs | 34 +++++++++++++----- crates/ra_ide/src/mock_analysis.rs | 16 ++++----- crates/ra_ide/src/syntax_highlighting.rs | 59 +++++++++++++++++++++++++++++--- crates/ra_syntax/src/ast/tokens.rs | 30 ++++++++++++++++ 5 files changed, 120 insertions(+), 23 deletions(-) diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 240cc03a2..087edcc92 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -51,8 +51,8 @@ fn type_at(content: &str) -> String { type_at_pos(&db, file_pos) } -fn infer(content: &str) -> String { - infer_with_mismatches(content, false) +fn infer(ra_fixture: &str) -> String { + infer_with_mismatches(ra_fixture, false) } fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 9a1fc0d35..2b35a3803 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs @@ -3,7 +3,7 @@ use hir::Semantics; use ra_ide_db::RootDatabase; use ra_syntax::{ ast::{self, ArgListOwner}, - match_ast, AstNode, SyntaxNode, + match_ast, AstNode, SyntaxNode, SyntaxToken, }; use test_utils::tested_by; @@ -16,7 +16,13 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option, + token: SyntaxToken, +) -> Option { // Find the calling expression and it's NameRef let calling_node = FnCallNode::with_node(&token.parent())?; @@ -27,21 +33,23 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { let fn_def = it.into(); - (CallInfo::with_fn(db, fn_def), fn_def.has_self_param(db)) + (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db)) + } + hir::CallableDef::StructId(it) => { + (CallInfo::with_struct(sema.db, it.into())?, false) } - hir::CallableDef::StructId(it) => (CallInfo::with_struct(db, it.into())?, false), hir::CallableDef::EnumVariantId(it) => { - (CallInfo::with_enum_variant(db, it.into())?, false) + (CallInfo::with_enum_variant(sema.db, it.into())?, false) } } } FnCallNode::MethodCallExpr(method_call) => { let function = sema.resolve_method_call(&method_call)?; - (CallInfo::with_fn(db, function), function.has_self_param(db)) + (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db)) } FnCallNode::MacroCallExpr(macro_call) => { let macro_def = sema.resolve_macro_call(¯o_call)?; - (CallInfo::with_macro(db, macro_def)?, false) + (CallInfo::with_macro(sema.db, macro_def)?, false) } }; @@ -61,7 +69,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option Option { Some(FnCallNode::CallExpr(it)) }, - ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) }, + ast::MethodCallExpr(it) => { + let arg_list = it.arg_list()?; + if !syntax.text_range().is_subrange(&arg_list.syntax().text_range()) { + return None; + } + Some(FnCallNode::MethodCallExpr(it)) + }, ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) }, _ => { None }, } diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 081aaee8c..f4cd6deb7 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs @@ -124,28 +124,28 @@ impl MockAnalysis { } /// Creates analysis from a multi-file fixture, returns positions marked with <|>. -pub fn analysis_and_position(fixture: &str) -> (Analysis, FilePosition) { - let (mock, position) = MockAnalysis::with_files_and_position(fixture); +pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) { + let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); (mock.analysis(), position) } /// Creates analysis for a single file. -pub fn single_file(code: &str) -> (Analysis, FileId) { +pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) { let mut mock = MockAnalysis::new(); - let file_id = mock.add_file("/main.rs", code); + let file_id = mock.add_file("/main.rs", ra_fixture); (mock.analysis(), file_id) } /// Creates analysis for a single file, returns position marked with <|>. -pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) { +pub fn single_file_with_position(ra_fixture: &str) -> (Analysis, FilePosition) { let mut mock = MockAnalysis::new(); - let pos = mock.add_file_with_position("/main.rs", code); + let pos = mock.add_file_with_position("/main.rs", ra_fixture); (mock.analysis(), pos) } /// Creates analysis for a single file, returns range marked with a pair of <|>. -pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) { +pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) { let mut mock = MockAnalysis::new(); - let pos = mock.add_file_with_range("/main.rs", code); + let pos = mock.add_file_with_range("/main.rs", ra_fixture); (mock.analysis(), pos) } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 796f0e545..3a5cbee9b 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -12,11 +12,12 @@ use ra_ide_db::{ }; use ra_prof::profile; use ra_syntax::{ - ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, TextRange, WalkEvent, T, + ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, SyntaxToken, + TextRange, WalkEvent, T, }; use rustc_hash::FxHashMap; -use crate::{references::classify_name_ref, FileId}; +use crate::{call_info::call_info_for_token, references::classify_name_ref, Analysis, FileId}; pub(crate) use html::highlight_as_html; pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; @@ -94,11 +95,12 @@ pub(crate) fn highlight( WalkEvent::Enter(it) => it, WalkEvent::Leave(_) => continue, }; + let range = element.text_range(); let element_to_highlight = if current_macro_call.is_some() { // Inside a macro -- expand it first - let token = match element.into_token() { + let token = match element.clone().into_token() { Some(it) if it.parent().kind() == TOKEN_TREE => it, _ => continue, }; @@ -110,9 +112,17 @@ pub(crate) fn highlight( _ => token.into(), } } else { - element + element.clone() }; + if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { + let expanded = element_to_highlight.as_token().unwrap().clone(); + if highlight_injection(&mut res, &sema, token, expanded).is_some() { + eprintln!("res = {:?}", res); + continue; + } + } + if let Some((highlight, binding_hash)) = highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight) { @@ -281,3 +291,44 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { _ => default, } } + +fn highlight_injection( + acc: &mut Vec, + sema: &Semantics, + literal: ast::RawString, + expanded: SyntaxToken, +) -> Option<()> { + let call_info = call_info_for_token(&sema, expanded)?; + let idx = call_info.active_parameter?; + let name = call_info.signature.parameter_names.get(idx)?; + if name != "ra_fixture" { + return None; + } + let value = literal.value()?; + let (analysis, tmp_file_id) = Analysis::from_single_file(value); + + if let Some(range) = literal.open_quote_text_range() { + acc.push(HighlightedRange { + range, + highlight: HighlightTag::LiteralString.into(), + binding_hash: None, + }) + } + + for mut h in analysis.highlight(tmp_file_id).unwrap() { + if let Some(r) = literal.map_range_up(h.range) { + h.range = r; + acc.push(h) + } + } + + if let Some(range) = literal.close_quote_text_range() { + acc.push(HighlightedRange { + range, + highlight: HighlightTag::LiteralString.into(), + binding_hash: None, + }) + } + + Some(()) +} diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index ed8661faf..693b35feb 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs @@ -171,6 +171,36 @@ impl RawString { let inside_str = &text[start_of_inside..end_of_inside]; Some(inside_str.to_string()) } + + pub fn open_quote_text_range(&self) -> Option { + let text = self.text().as_str(); + let usual_string_range = find_usual_string_range(text)?; + + let start = self.syntax().text_range().start(); + let len = usual_string_range.start() + TextUnit::of_char('"'); + Some(TextRange::offset_len(start, len)) + } + + pub fn close_quote_text_range(&self) -> Option { + let text = self.text().as_str(); + let usual_string_range = find_usual_string_range(text)?; + + let end = self.syntax().text_range().end(); + let len = TextUnit::of_str(text) - usual_string_range.end(); + Some(TextRange::from_to(end - len, end)) + } + + pub fn map_range_up(&self, range: TextRange) -> Option { + // FIXME: handle escapes here properly + let text = self.text().as_str(); + let usual_string_range = find_usual_string_range(text)?; + Some( + range + + self.syntax().text_range().start() + + TextUnit::of_char('"') + + usual_string_range.start(), + ) + } } fn find_usual_string_range(s: &str) -> Option { -- cgit v1.2.3