Feat: comfort features

This commit is contained in:
2026-02-08 00:46:56 +01:00
parent 20c32ce0d8
commit 8ffe2c22c7
31 changed files with 578 additions and 72 deletions

View File

@@ -1,8 +1,13 @@
use std::collections::HashSet;
use std::sync::LazyLock;
use ratatui::style::{Modifier, Style};
use crate::model::{lookup_word, SourceSpan, WordCompile};
use crate::theme;
static EMPTY_SET: LazyLock<HashSet<String>> = LazyLock::new(HashSet::new);
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum TokenKind {
Number,
@@ -20,6 +25,7 @@ pub enum TokenKind {
Emit,
Vary,
Generator,
UserDefined,
Default,
}
@@ -42,6 +48,7 @@ impl TokenKind {
TokenKind::Variable => theme.syntax.variable,
TokenKind::Vary => theme.syntax.vary,
TokenKind::Generator => theme.syntax.generator,
TokenKind::UserDefined => theme.syntax.user_defined,
TokenKind::Default => theme.syntax.default,
};
let style = Style::default().fg(fg).bg(bg);
@@ -114,7 +121,7 @@ const INTERVALS: &[&str] = &[
"M14", "P15",
];
pub fn tokenize_line(line: &str) -> Vec<Token> {
pub fn tokenize_line(line: &str, user_words: &HashSet<String>) -> Vec<Token> {
let mut tokens = Vec::new();
let mut chars = line.char_indices().peekable();
@@ -160,14 +167,14 @@ pub fn tokenize_line(line: &str) -> Vec<Token> {
}
let word = &line[start..end];
let (kind, varargs) = classify_word(word);
let (kind, varargs) = classify_word(word, user_words);
tokens.push(Token { start, end, kind, varargs });
}
tokens
}
fn classify_word(word: &str) -> (TokenKind, bool) {
fn classify_word(word: &str, user_words: &HashSet<String>) -> (TokenKind, bool) {
if word.parse::<f64>().is_ok() || word.parse::<i64>().is_ok() {
return (TokenKind::Number, false);
}
@@ -188,19 +195,24 @@ fn classify_word(word: &str) -> (TokenKind, bool) {
return (TokenKind::Variable, false);
}
if user_words.contains(word) {
return (TokenKind::UserDefined, false);
}
(TokenKind::Default, false)
}
pub fn highlight_line(line: &str) -> Vec<(Style, String)> {
highlight_line_with_runtime(line, &[], &[])
highlight_line_with_runtime(line, &[], &[], &EMPTY_SET)
}
pub fn highlight_line_with_runtime(
line: &str,
executed_spans: &[SourceSpan],
selected_spans: &[SourceSpan],
user_words: &HashSet<String>,
) -> Vec<(Style, String)> {
let tokens = tokenize_line(line);
let tokens = tokenize_line(line, user_words);
let mut result = Vec::new();
let mut last_end = 0;
let gap_style = TokenKind::gap_style();

View File

@@ -90,6 +90,11 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
let is_edit = idx == app.editor_ctx.bank;
let is_playing = banks_with_playback.contains(&idx);
let is_staged = banks_with_staged.contains(&idx);
let is_in_range = is_focused
&& app
.patterns_nav
.bank_selection_range()
.is_some_and(|r| r.contains(&idx));
// Check if any pattern in this bank is muted/soloed (applied)
let has_muted = (0..MAX_PATTERNS).any(|p| app.mute.is_muted(idx, p));
@@ -102,6 +107,8 @@ fn render_banks(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area
let (bg, fg, prefix) = if is_cursor {
(theme.selection.cursor, theme.selection.cursor_fg, "")
} else if is_in_range {
(theme.selection.in_range_bg, theme.selection.in_range_fg, "")
} else if is_playing {
if has_staged_mute_solo {
(theme.list.staged_play_bg, theme.list.staged_play_fg, ">*")
@@ -277,6 +284,11 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
let is_playing = playing_patterns.contains(&idx);
let is_staged_play = staged_to_play.contains(&idx);
let is_staged_stop = staged_to_stop.contains(&idx);
let is_in_range = is_focused
&& app
.patterns_nav
.pattern_selection_range()
.is_some_and(|r| r.contains(&idx));
// Current applied mute/solo state
let is_muted = app.mute.is_muted(bank, idx);
@@ -294,6 +306,8 @@ fn render_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, a
let (bg, fg, prefix) = if is_cursor {
(theme.selection.cursor, theme.selection.cursor_fg, "")
} else if is_in_range {
(theme.selection.in_range_bg, theme.selection.in_range_fg, "")
} else if is_playing {
// Playing patterns
if is_staged_stop {

View File

@@ -1,3 +1,4 @@
use std::collections::HashSet;
use std::time::{Duration, Instant};
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
@@ -14,7 +15,7 @@ use crate::state::{
EditorTarget, EuclideanField, FlashKind, Modal, PanelFocus, PatternField, SidePanel,
};
use crate::theme;
use crate::views::highlight::{self, highlight_line, highlight_line_with_runtime};
use crate::views::highlight::{self, highlight_line_with_runtime};
use crate::widgets::{
ConfirmModal, ModalFrame, NavMinimap, NavTile, SampleBrowser, TextInputModal,
};
@@ -460,6 +461,7 @@ fn render_footer(frame: &mut Frame, app: &App, area: Rect) {
fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term: Rect) -> Option<Rect> {
let theme = theme::get();
let user_words: HashSet<String> = app.dict.lock().keys().cloned().collect();
let inner = match &app.ui.modal {
Modal::None => return None,
Modal::ConfirmQuit { selected } => {
@@ -488,6 +490,16 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
ConfirmModal::new("Confirm", &format!("Reset bank {}?", bank + 1), *selected)
.render_centered(frame, term)
}
Modal::ConfirmResetPatterns {
patterns, selected, ..
} => {
let label = format!("Reset {} patterns?", patterns.len());
ConfirmModal::new("Confirm", &label, *selected).render_centered(frame, term)
}
Modal::ConfirmResetBanks { banks, selected } => {
let label = format!("Reset {} banks?", banks.len());
ConfirmModal::new("Confirm", &label, *selected).render_centered(frame, term)
}
Modal::FileBrowser(state) => {
use crate::state::file_browser::FileBrowserMode;
use crate::widgets::FileBrowserModal;
@@ -621,9 +633,9 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
line_start,
line_str.len(),
);
highlight_line_with_runtime(line_str, &exec, &sel)
highlight_line_with_runtime(line_str, &exec, &sel, &user_words)
} else {
highlight_line(line_str)
highlight_line_with_runtime(line_str, &[], &[], &user_words)
};
line_start += line_str.len() + 1;
let spans: Vec<Span> = tokens
@@ -700,7 +712,7 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term
),
None => (Vec::new(), Vec::new()),
};
highlight::highlight_line_with_runtime(line, &exec, &sel)
highlight::highlight_line_with_runtime(line, &exec, &sel, &user_words)
};
let show_search = app.editor_ctx.editor.search_active()