This commit is contained in:
2026-01-22 10:08:05 +01:00
parent 409e815414
commit 88b6f64a72
10 changed files with 43268 additions and 388 deletions

View File

@@ -13,6 +13,9 @@ pub enum TokenKind {
Sound,
Param,
Context,
Note,
Interval,
Variable,
Default,
}
@@ -28,6 +31,9 @@ impl TokenKind {
TokenKind::Sound => Style::default().fg(Color::Rgb(100, 220, 200)),
TokenKind::Param => Style::default().fg(Color::Rgb(180, 150, 220)),
TokenKind::Context => Style::default().fg(Color::Rgb(220, 180, 120)),
TokenKind::Note => Style::default().fg(Color::Rgb(120, 200, 160)),
TokenKind::Interval => Style::default().fg(Color::Rgb(160, 200, 120)),
TokenKind::Variable => Style::default().fg(Color::Rgb(200, 140, 180)),
TokenKind::Default => Style::default().fg(Color::Rgb(200, 200, 200)),
}
}
@@ -39,18 +45,21 @@ pub struct Token {
pub kind: TokenKind,
}
const STACK_OPS: &[&str] = &["dup", "drop", "swap", "over", "rot", "nip", "tuck"];
const STACK_OPS: &[&str] = &["dup", "dupn", "drop", "swap", "over", "rot", "nip", "tuck"];
const OPERATORS: &[&str] = &[
"+", "-", "*", "/", "mod", "neg", "abs", "min", "max", "=", "<>", "<", ">", "<=", ">=", "and",
"or", "not",
"or", "not", "ceil", "floor", "round", "mtof", "ftom",
];
const KEYWORDS: &[&str] = &[
"if", "else", "then", "emit", "get", "set", "rand", "rrand", "seed", "cycle", "choose",
"chance", "[", "]",
"if", "else", "then", "emit", "rand", "rrand", "seed", "cycle", "choose", "chance", "[", "]",
"zoom", "scale!", "stack", "echo", "necho", "for", "div", "each", "at", "pop", "adsr", "ad",
"?", "!?", "<<", ">>", "|", "@", "!", "pcycle", "tempo!", "prob", "sometimes", "often",
"rarely", "almostAlways", "almostNever", "always", "never", "coin", "fill", "iter", "every",
"gt", "lt",
];
const SOUND: &[&str] = &["sound", "s"];
const CONTEXT: &[&str] = &[
"step", "beat", "bank", "pattern", "tempo", "phase", "slot", "runs",
"step", "beat", "bank", "pattern", "tempo", "phase", "slot", "runs", "stepdur",
];
const PARAMS: &[&str] = &[
"time",
@@ -160,6 +169,28 @@ const PARAMS: &[&str] = &[
"cut",
"reset",
];
const INTERVALS: &[&str] = &[
"P1", "unison", "m2", "M2", "m3", "M3", "P4", "aug4", "dim5", "tritone", "P5", "m6", "M6",
"m7", "M7", "P8", "oct", "m9", "M9", "m10", "M10", "P11", "aug11", "P12", "m13", "M13", "m14",
"M14", "P15",
];
fn is_note(word: &str) -> bool {
let bytes = word.as_bytes();
if bytes.len() < 2 {
return false;
}
if !matches!(bytes[0], b'a'..=b'g' | b'A'..=b'G') {
return false;
}
let rest = &bytes[1..];
let digits_start = if rest.first().is_some_and(|&b| b == b'#' || b == b's' || b == b'b') {
1
} else {
0
};
rest[digits_start..].iter().all(|&b| b.is_ascii_digit()) && digits_start < rest.len()
}
pub fn tokenize_line(line: &str) -> Vec<Token> {
let mut tokens = Vec::new();
@@ -252,6 +283,18 @@ fn classify_word(word: &str) -> TokenKind {
return TokenKind::Param;
}
if INTERVALS.contains(&word) {
return TokenKind::Interval;
}
if is_note(word) {
return TokenKind::Note;
}
if word.len() > 1 && (word.starts_with('@') || word.starts_with('!')) {
return TokenKind::Variable;
}
TokenKind::Default
}

View File

@@ -180,7 +180,7 @@ fn render_step_preview(frame: &mut Frame, app: &App, snapshot: &SequencerSnapsho
}
let runtime_spans = if app.ui.runtime_highlight && app.playback.playing {
snapshot.get_trace(app.editor_ctx.bank, app.editor_ctx.pattern)
snapshot.get_trace(app.editor_ctx.bank, app.editor_ctx.pattern, step_idx)
} else {
None
};

View File

@@ -362,7 +362,7 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
let (cursor_row, cursor_col) = app.editor_ctx.text.cursor();
let runtime_spans = if app.ui.runtime_highlight && app.playback.playing {
snapshot.get_trace(app.editor_ctx.bank, app.editor_ctx.pattern)
snapshot.get_trace(app.editor_ctx.bank, app.editor_ctx.pattern, app.editor_ctx.step)
} else {
None
};