Skip to content

Commit e0954fa

Browse files
Add some basic syntax highlighting for code snippets
Issue #4
1 parent 8c04c06 commit e0954fa

File tree

2 files changed

+71
-18
lines changed

2 files changed

+71
-18
lines changed

src/ui/full_term/problems_ui.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use std::sync::Arc;
4848
use std::sync::MutexGuard;
4949

5050
mod diff;
51+
mod syntax_styling;
5152

5253
pub(super) struct ProblemsUi {
5354
problem_store: ProblemStoreRef,
@@ -538,29 +539,39 @@ fn usage_source_lines(usage: &dyn DisplayUsage) -> Result<Vec<Line<'static>>> {
538539
"{marker}{:gutter_width$}: ",
539540
line_number
540541
))];
541-
if line_number == target_line {
542-
if let Some(column) = source_location.column() {
543-
let column = column as usize - 1;
544-
spans.push(Span::from(line[..column].to_owned()));
545-
spans.push(Span::styled(
546-
line[column..column + 1].to_owned(),
547-
Style::default().add_modifier(Modifier::REVERSED),
548-
));
549-
spans.push(Span::from(line[column + 1..].to_owned()));
550-
} else {
551-
spans.push(Span::styled(
552-
line.to_owned(),
553-
Style::default().add_modifier(Modifier::UNDERLINED),
554-
));
555-
}
556-
} else {
557-
spans.push(Span::from(line.to_owned()));
558-
}
542+
let column = (line_number == target_line)
543+
.then(|| source_location.column())
544+
.flatten();
545+
format_line(&mut spans, column, line);
559546
lines.push(Line::from(spans));
560547
}
561548
Ok(lines)
562549
}
563550

551+
fn format_line(out: &mut Vec<Span>, column: Option<u32>, line: &str) {
552+
let mut rev = false;
553+
let mut offset = 0;
554+
let column_offset = column.map(|c| (c as usize).saturating_sub(1));
555+
for token in rustc_ap_rustc_lexer::tokenize(line) {
556+
let new_offset = offset + token.len;
557+
let token_text = &line[offset..new_offset];
558+
let mut style = Style::default();
559+
if let Some(colour) = syntax_styling::colour_for_token_kind(token.kind, token_text) {
560+
style = style.fg(colour);
561+
}
562+
if column_offset
563+
.map(|c| (offset..new_offset).contains(&c))
564+
.unwrap_or(false)
565+
{
566+
rev = true;
567+
style = style.add_modifier(Modifier::REVERSED);
568+
}
569+
out.push(Span::styled(token_text.to_owned(), style));
570+
offset = new_offset;
571+
}
572+
log::info!("col={column:?} line.len={} rev={rev:?}", line.len());
573+
}
574+
564575
fn render_help(f: &mut Frame<CrosstermBackend<Stdout>>, mode: Option<&Mode>) {
565576
let mut keys = vec![];
566577
let mut title = "Help";
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use ratatui::style::Color;
2+
use rustc_ap_rustc_lexer::LiteralKind;
3+
use rustc_ap_rustc_lexer::TokenKind;
4+
5+
pub(super) fn colour_for_token_kind(kind: TokenKind, token_text: &str) -> Option<Color> {
6+
match kind {
7+
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } => Some(Color::LightGreen),
8+
TokenKind::Ident | TokenKind::RawIdent => {
9+
if is_keyword(token_text) {
10+
Some(Color::Blue)
11+
} else {
12+
Some(Color::LightGreen)
13+
}
14+
}
15+
TokenKind::Literal {
16+
kind:
17+
LiteralKind::Str { .. }
18+
| LiteralKind::ByteStr { .. }
19+
| LiteralKind::RawByteStr { .. }
20+
| LiteralKind::RawStr { .. },
21+
..
22+
} => Some(Color::Yellow),
23+
TokenKind::Lifetime { .. } => Some(Color::Blue),
24+
TokenKind::OpenParen | TokenKind::CloseParen => Some(Color::Blue),
25+
TokenKind::OpenBrace | TokenKind::CloseBrace => Some(Color::Magenta),
26+
TokenKind::OpenBracket | TokenKind::CloseBracket => Some(Color::Magenta),
27+
TokenKind::Question => Some(Color::Yellow),
28+
_ => None,
29+
}
30+
}
31+
32+
const KEYWORDS: &[&str] = &[
33+
"as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for",
34+
"if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return",
35+
"self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe", "use", "where",
36+
"while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro",
37+
"override", "priv", "typeof", "unsized", "virtual", "yield", "try",
38+
];
39+
40+
fn is_keyword(token_text: &str) -> bool {
41+
KEYWORDS.contains(&token_text)
42+
}

0 commit comments

Comments
 (0)