Skip to content

Commit

Permalink
Implement syntax highlighting for format strings
Browse files Browse the repository at this point in the history
In the syntax highlighting logic, we search for format macro names from std and then do a regex-based search for the format modifiers `{}`.
  • Loading branch information
ltentrup committed Apr 17, 2020
1 parent cce49e2 commit f08c96f
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/ra_ide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ itertools = "0.9.0"
log = "0.4.8"
rustc-hash = "1.1.0"
rand = { version = "0.7.3", features = ["small_rng"] }
regex = "1.3.6"
lazy_static = "1.4.0"

stdx = { path = "../stdx" }

Expand Down
4 changes: 3 additions & 1 deletion crates/ra_ide/src/snapshots/highlight_injection.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
<span class="function">fixture</span>(<span class="string_literal">r#"</span>
<span class="keyword">trait</span> <span class="trait declaration">Foo</span> {
<span class="keyword">fn</span> <span class="function declaration">foo</span>() {
<span class="macro">println!</span>(<span class="string_literal">"2 + 2 = {}"</span>, <span class="numeric_literal">4</span>);
<span class="macro">println!</span>(<span class="string_literal">"2 + </span><span class="attribute">{}</span><span class="string_literal"> = </span><span class="attribute">{}</span><span class="string_literal">"</span>, <span class="numeric_literal">2</span>, <span class="numeric_literal">4</span>);
<span class="macro">format!</span>(<span class="string_literal">"2 + </span><span class="attribute">{:?}</span><span class="string_literal"> }}= </span><span class="attribute">{}</span><span class="string_literal">"</span>, <span class="numeric_literal">2</span>, <span class="numeric_literal">4</span>);
<span class="macro">print!</span>(<span class="string_literal">r"2 + {{ </span><span class="attribute">{:#?}</span><span class="string_literal"> = </span><span class="attribute">{}</span><span class="string_literal"> {{}}"</span>, <span class="numeric_literal">2</span>, <span class="numeric_literal">4</span>);
}
}<span class="string_literal">"#</span>
);
Expand Down
2 changes: 1 addition & 1 deletion crates/ra_ide/src/snapshots/highlighting.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

<span class="comment">// comment</span>
<span class="keyword">fn</span> <span class="function declaration">main</span>() {
<span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
<span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="attribute">{}</span><span class="string_literal">!"</span>, <span class="numeric_literal">92</span>);

<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = Vec::new();
<span class="keyword control">if</span> <span class="keyword">true</span> {
Expand Down
47 changes: 46 additions & 1 deletion crates/ra_ide/src/syntax_highlighting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod html;
mod tests;

use hir::{Name, Semantics};
use lazy_static::lazy_static;
use ra_ide_db::{
defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
RootDatabase,
Expand All @@ -15,8 +16,9 @@ use ra_syntax::{
ast::{self, HasQuotes, HasStringValue},
AstNode, AstToken, Direction, NodeOrToken, SyntaxElement,
SyntaxKind::*,
SyntaxToken, TextRange, WalkEvent, T,
SyntaxToken, TextRange, TextUnit, WalkEvent, T,
};
use regex::Regex;
use rustc_hash::FxHashMap;

use crate::{call_info::call_info_for_token, Analysis, FileId};
Expand Down Expand Up @@ -134,6 +136,7 @@ pub(crate) fn highlight(
let mut stack = HighlightedRangeStack::new();

let mut current_macro_call: Option<ast::MacroCall> = None;
let mut format_string: Option<SyntaxElement> = None;

// Walk all nodes, keeping track of whether we are inside a macro or not.
// If in macro, expand it first and highlight the expanded code.
Expand Down Expand Up @@ -169,6 +172,7 @@ pub(crate) fn highlight(
WalkEvent::Leave(Some(mc)) => {
assert!(current_macro_call == Some(mc));
current_macro_call = None;
format_string = None;
continue;
}
_ => (),
Expand All @@ -189,6 +193,23 @@ pub(crate) fn highlight(
};
let token = sema.descend_into_macros(token.clone());
let parent = token.parent();

// Check if macro takes a format string and remember the string
if let Some(name) = current_macro_call
.as_ref()
.and_then(|m| m.path())
.and_then(|p| p.segment())
.and_then(|s| s.name_ref())
{
if ["format", "println", "print", "eprint", "eprintln", "write", "writeln"]
.contains(&name.text().as_str())
{
format_string = parent.children_with_tokens().nth(1).filter(|e| {
ast::String::can_cast(e.kind()) || ast::RawString::can_cast(e.kind())
});
}
}

// We only care Name and Name_ref
match (token.kind(), parent.kind()) {
(IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
Expand All @@ -205,10 +226,34 @@ pub(crate) fn highlight(
}
}

let is_format_string =
format_string.as_ref().map(|fs| fs == &element_to_highlight).unwrap_or_default();

if let Some((highlight, binding_hash)) =
highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight)
{
stack.add(HighlightedRange { range, highlight, binding_hash });
if is_format_string {
stack.push();
lazy_static! {
static ref RE: Regex =
Regex::new(r"[^\{](?P<format>\{(?:\}|[^\{].*?\}))[^\}]").unwrap();
}
for (start, end) in RE
.captures_iter(&format_string.as_ref().unwrap().to_string())
.flat_map(|matches| matches.name("format").map(|m| (m.start(), m.end())))
{
stack.add(HighlightedRange {
range: TextRange::from_to(
range.start() + TextUnit::from_usize(start),
range.start() + TextUnit::from_usize(end),
),
highlight: HighlightTag::Attribute.into(),
binding_hash: None,
});
}
stack.pop();
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion crates/ra_ide/src/syntax_highlighting/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ fn main() {
fixture(r#"
trait Foo {
fn foo() {
println!("2 + 2 = {}", 4);
println!("2 + {} = {}", 2, 4);
format!("2 + {:?} }}= {}", 2, 4);
print!(r"2 + {{ {:#?} = {} {{}}", 2, 4);
}
}"#
);
Expand Down

0 comments on commit f08c96f

Please sign in to comment.