]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/syntax_highlighting/format.rs
Fix format string highlighting in `panic!`
[rust.git] / crates / ide / src / syntax_highlighting / format.rs
1 //! Syntax highlighting for format macro strings.
2 use ide_db::SymbolKind;
3 use syntax::{
4     ast::{self, FormatSpecifier, HasFormatSpecifier},
5     AstNode, AstToken, TextRange,
6 };
7
8 use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag};
9
10 pub(super) fn highlight_format_string(
11     stack: &mut Highlights,
12     string: &ast::String,
13     range: TextRange,
14 ) {
15     if is_format_string(string).is_none() {
16         return;
17     }
18
19     string.lex_format_specifier(|piece_range, kind| {
20         if let Some(highlight) = highlight_format_specifier(kind) {
21             stack.add(HlRange {
22                 range: piece_range + range.start(),
23                 highlight: highlight.into(),
24                 binding_hash: None,
25             });
26         }
27     });
28 }
29
30 fn is_format_string(string: &ast::String) -> Option<()> {
31     let parent = string.syntax().parent()?;
32
33     let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
34     if !matches!(
35         name.text().as_str(),
36         "format_args" | "format_args_nl" | "const_format_args" | "panic_2015" | "panic_2021"
37     ) {
38         return None;
39     }
40
41     let first_literal = parent
42         .children_with_tokens()
43         .find_map(|it| it.as_token().cloned().and_then(ast::String::cast))?;
44     if &first_literal != string {
45         return None;
46     }
47
48     Some(())
49 }
50
51 fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HlTag> {
52     Some(match kind {
53         FormatSpecifier::Open
54         | FormatSpecifier::Close
55         | FormatSpecifier::Colon
56         | FormatSpecifier::Fill
57         | FormatSpecifier::Align
58         | FormatSpecifier::Sign
59         | FormatSpecifier::NumberSign
60         | FormatSpecifier::DollarSign
61         | FormatSpecifier::Dot
62         | FormatSpecifier::Asterisk
63         | FormatSpecifier::QuestionMark => HlTag::FormatSpecifier,
64
65         FormatSpecifier::Integer | FormatSpecifier::Zero => HlTag::NumericLiteral,
66
67         FormatSpecifier::Identifier => HlTag::Symbol(SymbolKind::Local),
68     })
69 }