Merge pull request #3349 from matklad/yo-dawg

Put syntax highlighting into syntax highlighting
This commit is contained in:
Aleksey Kladov 2020-02-27 16:48:06 +01:00 committed by GitHub
commit de492d439f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 120 additions and 23 deletions

View file

@ -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 {

View file

@ -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<Cal
let file = file.syntax();
let token = file.token_at_offset(position.offset).next()?;
let token = sema.descend_into_macros(token);
call_info_for_token(&sema, token)
}
pub(crate) fn call_info_for_token(
sema: &Semantics<RootDatabase>,
token: SyntaxToken,
) -> Option<CallInfo> {
// 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<Cal
match callable_def {
hir::CallableDef::FunctionId(it) => {
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(&macro_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<Cal
let num_args_at_callsite = arg_list.args().count();
let arg_list_range = arg_list.syntax().text_range();
if !arg_list_range.contains_inclusive(position.offset) {
if !arg_list_range.contains_inclusive(token.text_range().start()) {
tested_by!(call_info_bad_offset);
return None;
}
@ -70,7 +78,9 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
num_args_at_callsite,
arg_list
.args()
.take_while(|arg| arg.syntax().text_range().end() < position.offset)
.take_while(|arg| {
arg.syntax().text_range().end() < token.text_range().start()
})
.count(),
);
@ -100,7 +110,13 @@ impl FnCallNode {
match_ast! {
match node {
ast::CallExpr(it) => { 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 },
}

View file

@ -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)
}

View file

@ -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<HighlightedRange>,
sema: &Semantics<RootDatabase>,
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(())
}

View file

@ -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<TextRange> {
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<TextRange> {
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<TextRange> {
// 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<TextRange> {