mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 17:58:16 +00:00
Merge #4876
4876: Syntactic highlighting of NAME_REF for injections r=matklad a=ltentrup This commit adds a function that tries to determine the syntax highlighting class of NAME_REFs based on the usage. It is used for highlighting injections (such as highlighting of doctests) as the semantic logic will most of the time result in unresolved references. It also adds a color to unresolved references in HTML encoding. Follow up of #4683. Fixes #4809. Co-authored-by: Leander Tentrup <leander.tentrup@gmail.com>
This commit is contained in:
commit
4342b71715
12 changed files with 102 additions and 16 deletions
|
@ -440,12 +440,14 @@ impl Analysis {
|
|||
|
||||
/// Computes syntax highlighting for the given file
|
||||
pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
|
||||
self.with_db(|db| syntax_highlighting::highlight(db, file_id, None))
|
||||
self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
|
||||
}
|
||||
|
||||
/// Computes syntax highlighting for the given file range.
|
||||
pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> {
|
||||
self.with_db(|db| syntax_highlighting::highlight(db, frange.file_id, Some(frange.range)))
|
||||
self.with_db(|db| {
|
||||
syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes syntax highlighting for the given file.
|
||||
|
|
|
@ -7,6 +7,6 @@ use crate::{FileId, RootDatabase};
|
|||
|
||||
pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) {
|
||||
for file in files {
|
||||
let _ = crate::syntax_highlighting::highlight(db, file, None);
|
||||
let _ = crate::syntax_highlighting::highlight(db, file, None, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,22 +25,29 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.unresolved_reference { color: #FC5555; }
|
||||
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.keyword.unsafe { color: #BC8383; font-weight: bold; }
|
||||
.control { font-style: italic; }
|
||||
</style>
|
||||
<pre><code><span class="keyword">impl</span> <span class="unresolved_reference">Foo</span> {
|
||||
<pre><code><span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
|
||||
<span class="field declaration">bar</span>: <span class="builtin_type">bool</span>,
|
||||
}
|
||||
|
||||
<span class="keyword">impl</span> <span class="struct">Foo</span> {
|
||||
<span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration">bar</span>: <span class="builtin_type">bool</span> = <span class="bool_literal">true</span>;
|
||||
|
||||
<span class="comment">/// Constructs a new `Foo`.</span>
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// # Examples</span>
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// ```</span>
|
||||
<span class="comment">/// #</span> <span class="attribute">#![</span><span class="function attribute">allow</span><span class="attribute">(unused_mut)]</span>
|
||||
<span class="comment">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="unresolved_reference">Foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>();
|
||||
<span class="comment">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="struct">Foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>();
|
||||
<span class="comment">/// ```</span>
|
||||
<span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -> <span class="unresolved_reference">Foo</span> {
|
||||
<span class="unresolved_reference">Foo</span> { }
|
||||
<span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -> <span class="struct">Foo</span> {
|
||||
<span class="struct">Foo</span> { <span class="field">bar</span>: <span class="bool_literal">true</span> }
|
||||
}
|
||||
|
||||
<span class="comment">/// `bar` method on `Foo`.</span>
|
||||
|
@ -48,11 +55,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
<span class="comment">/// # Examples</span>
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// ```</span>
|
||||
<span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>();
|
||||
<span class="comment">/// </span><span class="keyword">use</span> <span class="module">x</span>::<span class="module">y</span>;
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>();
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// </span><span class="comment">// calls bar on foo</span>
|
||||
<span class="comment">/// </span><span class="macro">assert!</span>(foo.bar());
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">bar</span> = <span class="variable">foo</span>.<span class="field">bar</span> || <span class="struct">Foo</span>::<span class="constant">bar</span>;
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// </span><span class="comment">/* multi-line
|
||||
</span><span class="comment">/// </span><span class="comment"> comment */</span>
|
||||
<span class="comment">///</span>
|
||||
|
@ -63,7 +74,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
<span class="comment">/// ```</span>
|
||||
<span class="comment">///</span>
|
||||
<span class="comment">/// ```</span>
|
||||
<span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>().<span class="unresolved_reference">bar</span>();
|
||||
<span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="struct">Foo</span>::<span class="function">new</span>().<span class="function">bar</span>();
|
||||
<span class="comment">/// ```</span>
|
||||
<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -> <span class="builtin_type">bool</span> {
|
||||
<span class="bool_literal">true</span>
|
||||
|
|
|
@ -25,6 +25,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.unresolved_reference { color: #FC5555; }
|
||||
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.keyword.unsafe { color: #BC8383; font-weight: bold; }
|
||||
|
|
|
@ -25,6 +25,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.unresolved_reference { color: #FC5555; }
|
||||
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.keyword.unsafe { color: #BC8383; font-weight: bold; }
|
||||
|
|
|
@ -25,6 +25,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.unresolved_reference { color: #FC5555; }
|
||||
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.keyword.unsafe { color: #BC8383; font-weight: bold; }
|
||||
|
|
|
@ -25,6 +25,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.unresolved_reference { color: #FC5555; }
|
||||
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.keyword.unsafe { color: #BC8383; font-weight: bold; }
|
||||
|
|
|
@ -25,6 +25,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.unresolved_reference { color: #FC5555; }
|
||||
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.keyword.unsafe { color: #BC8383; font-weight: bold; }
|
||||
|
|
|
@ -44,6 +44,7 @@ pub(crate) fn highlight(
|
|||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
range_to_highlight: Option<TextRange>,
|
||||
syntactic_name_ref_highlighting: bool,
|
||||
) -> Vec<HighlightedRange> {
|
||||
let _p = profile("highlight");
|
||||
let sema = Semantics::new(db);
|
||||
|
@ -104,6 +105,7 @@ pub(crate) fn highlight(
|
|||
if let Some((highlight, binding_hash)) = highlight_element(
|
||||
&sema,
|
||||
&mut bindings_shadow_count,
|
||||
syntactic_name_ref_highlighting,
|
||||
name.syntax().clone().into(),
|
||||
) {
|
||||
stack.add(HighlightedRange {
|
||||
|
@ -200,9 +202,12 @@ pub(crate) fn highlight(
|
|||
|
||||
let is_format_string = format_string.as_ref() == Some(&element_to_highlight);
|
||||
|
||||
if let Some((highlight, binding_hash)) =
|
||||
highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone())
|
||||
{
|
||||
if let Some((highlight, binding_hash)) = highlight_element(
|
||||
&sema,
|
||||
&mut bindings_shadow_count,
|
||||
syntactic_name_ref_highlighting,
|
||||
element_to_highlight.clone(),
|
||||
) {
|
||||
stack.add(HighlightedRange { range, highlight, binding_hash });
|
||||
if let Some(string) =
|
||||
element_to_highlight.as_token().cloned().and_then(ast::String::cast)
|
||||
|
@ -410,6 +415,7 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
|
|||
fn highlight_element(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
bindings_shadow_count: &mut FxHashMap<Name, u32>,
|
||||
syntactic_name_ref_highlighting: bool,
|
||||
element: SyntaxElement,
|
||||
) -> Option<(Highlight, Option<u64>)> {
|
||||
let db = sema.db;
|
||||
|
@ -463,6 +469,7 @@ fn highlight_element(
|
|||
}
|
||||
NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
|
||||
},
|
||||
None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref),
|
||||
None => HighlightTag::UnresolvedReference.into(),
|
||||
}
|
||||
}
|
||||
|
@ -614,3 +621,53 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
|
|||
|
||||
tag.into()
|
||||
}
|
||||
|
||||
fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
|
||||
let default = HighlightTag::UnresolvedReference;
|
||||
|
||||
let parent = match name.syntax().parent() {
|
||||
Some(it) => it,
|
||||
_ => return default.into(),
|
||||
};
|
||||
|
||||
let tag = match parent.kind() {
|
||||
METHOD_CALL_EXPR => HighlightTag::Function,
|
||||
FIELD_EXPR => HighlightTag::Field,
|
||||
PATH_SEGMENT => {
|
||||
let path = match parent.parent().and_then(ast::Path::cast) {
|
||||
Some(it) => it,
|
||||
_ => return default.into(),
|
||||
};
|
||||
let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
|
||||
Some(it) => it,
|
||||
_ => {
|
||||
// within path, decide whether it is module or adt by checking for uppercase name
|
||||
return if name.text().chars().next().unwrap_or_default().is_uppercase() {
|
||||
HighlightTag::Struct
|
||||
} else {
|
||||
HighlightTag::Module
|
||||
}
|
||||
.into();
|
||||
}
|
||||
};
|
||||
let parent = match expr.syntax().parent() {
|
||||
Some(it) => it,
|
||||
None => return default.into(),
|
||||
};
|
||||
|
||||
match parent.kind() {
|
||||
CALL_EXPR => HighlightTag::Function,
|
||||
_ => {
|
||||
if name.text().chars().next().unwrap_or_default().is_uppercase() {
|
||||
HighlightTag::Struct
|
||||
} else {
|
||||
HighlightTag::Constant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => default,
|
||||
};
|
||||
|
||||
tag.into()
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
|
|||
)
|
||||
}
|
||||
|
||||
let ranges = highlight(db, file_id, None);
|
||||
let ranges = highlight(db, file_id, None, false);
|
||||
let text = parse.tree().syntax().to_string();
|
||||
let mut prev_pos = TextSize::from(0);
|
||||
let mut buf = String::new();
|
||||
|
@ -84,6 +84,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.unresolved_reference { color: #FC5555; }
|
||||
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.keyword.unsafe { color: #BC8383; font-weight: bold; }
|
||||
|
|
|
@ -137,7 +137,7 @@ pub(super) fn highlight_doc_comment(
|
|||
let (analysis, tmp_file_id) = Analysis::from_single_file(text);
|
||||
|
||||
stack.push();
|
||||
for mut h in analysis.highlight(tmp_file_id).unwrap() {
|
||||
for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
|
||||
// Determine start offset and end offset in case of multi-line ranges
|
||||
let mut start_offset = None;
|
||||
let mut end_offset = None;
|
||||
|
|
|
@ -287,7 +287,13 @@ fn main() {
|
|||
fn test_highlight_doctest() {
|
||||
check_highlighting(
|
||||
r#"
|
||||
struct Foo {
|
||||
bar: bool,
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
pub const bar: bool = true;
|
||||
|
||||
/// Constructs a new `Foo`.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -297,7 +303,7 @@ impl Foo {
|
|||
/// let mut foo: Foo = Foo::new();
|
||||
/// ```
|
||||
pub const fn new() -> Foo {
|
||||
Foo { }
|
||||
Foo { bar: true }
|
||||
}
|
||||
|
||||
/// `bar` method on `Foo`.
|
||||
|
@ -305,11 +311,15 @@ impl Foo {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use x::y;
|
||||
///
|
||||
/// let foo = Foo::new();
|
||||
///
|
||||
/// // calls bar on foo
|
||||
/// assert!(foo.bar());
|
||||
///
|
||||
/// let bar = foo.bar || Foo::bar;
|
||||
///
|
||||
/// /* multi-line
|
||||
/// comment */
|
||||
///
|
||||
|
@ -330,7 +340,7 @@ impl Foo {
|
|||
.trim(),
|
||||
"crates/ra_ide/src/snapshots/highlight_doctest.html",
|
||||
false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// Highlights the code given by the `ra_fixture` argument, renders the
|
||||
|
|
Loading…
Reference in a new issue