diff options
Diffstat (limited to 'src/lexer.c')
| -rw-r--r-- | src/lexer.c | 71 |
1 files changed, 59 insertions, 12 deletions
diff --git a/src/lexer.c b/src/lexer.c index 5e6202d..25ea8f2 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include "sciteco.h" #include "view.h" #include "parser.h" +#include "core-commands.h" #include "lexer.h" static teco_style_t @@ -37,16 +38,21 @@ teco_lexer_getstyle(teco_view_t *view, teco_machine_main_t *machine, /* * FIXME: At least this special workaround for numbers might be * unnecessary once we get a special parser state for parsing numbers. - * - * FIXME: What about ^* and ^/? - * They are currently highlighted as commands. */ if (machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START && chr <= 0xFF) { if (g_ascii_isdigit(chr)) style = SCE_SCITECO_NUMBER; - else if (strchr("+-*/#&", chr)) + else if (strchr(",+-*/#&()", chr)) style = SCE_SCITECO_OPERATOR; + } else if (machine->parent.current == &teco_state_control) { + /* + * Two-character operators must always begin with caret + * They get a separate style, so we can extend it back to + * the caret in teco_lexter_step. + */ + if (strchr("*/#", chr)) + style = SCE_SCITECO_OPERATOR2; } /* @@ -126,13 +132,40 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine, machine->macro_pc = g_utf8_next_char(macro+machine->macro_pc) - macro; gunichar escape_char = machine->expectstring.machine.escape_char; + guint fold_level = SC_FOLDLEVELBASE+machine->expectstring.nesting-1+ + (escape_char == '{' ? 1 : 0); + style = teco_lexer_getstyle(view, machine, chr); /* + * Apply folding. This currently folds only {...} string arguments + * and all its embedded braces. + * We could fold loops and IF-statements as well, but that would + * require manually keeping track of the nesting in parse-only mode, + * which should better be in the parser itself. + * + * FIXME: You cannot practically disable folding via properties. + */ + if (teco_view_ssm(view, SCI_GETPROPERTYINT, (uptr_t)"fold", TRUE)) { + guint next_fold_level = SC_FOLDLEVELBASE+machine->expectstring.nesting-1+ + (machine->expectstring.machine.escape_char == '{' ? 1 : 0); + + if (next_fold_level > fold_level) + /* `chr` opened a {...} string argument */ + teco_view_ssm(view, SCI_SETFOLDLEVEL, *cur_line, + fold_level | SC_FOLDLEVELHEADERFLAG); + else if (!*cur_col) + teco_view_ssm(view, SCI_SETFOLDLEVEL, *cur_line, fold_level); + } + + /* * Optionally style @^Uq{ ... } contents like macro definitions. * The curly braces will be styled like regular commands. * - * FIXME: This will not work with nested macro definitions. + * FIXME: This works only for top-level macro definitions, + * not for nested definitions. + * FIXME: The macrodef_machine's end-of-macro callback could be used + * to detect and highlight an error on the closing `}`. * FIXME: This cannot currently be disabled, not even with SCI_SETPROPERTY. * We could only map it to an ED flag or * rewrite the lexer against the ILexer5 interface, which requires C++. @@ -147,8 +180,9 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine, /* * True comments begin with `!*` or `!!`, but only the second character gets * the correct style by default, so we extend it backwards. + * The same is true for two-letter operators. */ - if (style == SCE_SCITECO_COMMENT) + if (style == SCE_SCITECO_COMMENT || style == SCE_SCITECO_OPERATOR2) old_pc--; teco_view_ssm(view, SCI_STARTSTYLING, start+old_pc, 0); @@ -185,22 +219,35 @@ teco_lexer_style(teco_view_t *view, gsize end) gsize start = teco_view_ssm(view, SCI_GETENDSTYLED, 0, 0); guint start_line = teco_view_ssm(view, SCI_LINEFROMPOSITION, start, 0); - gint start_col = 0; /* * The line state stores the laster character (column) in bytes, * that starts from a fresh parser state. * It's -1 if the line does not have a clean parser state. - * Therefore we search for the first line before `start` that has a - * known clean parser state. + * If the cached position on start_line does not fit our needs, + * we backtrack and search in previous lines + * for a known clean parser state. + * + * NOTE: It's crucial to consider the line state of the first possible + * line since we might be styling for a single-line command line view. + * + * FIXME: During rubout of regular commands we will frequently have the + * situation that the cached line state points after the last styled position + * forcing us to restyle the entire command line macro. + * If this turns out to be problematic, we might detect that + * view == teco_cmdline.view and inspect teco_cmdline.machine. */ - if (start_line > 0) { + gint start_col = teco_view_ssm(view, SCI_GETLINESTATE, start_line, 0); + if (start_col > start - teco_view_ssm(view, SCI_POSITIONFROMLINE, start_line, 0)) + /* we are asked to style __before__ the last known start state */ + start_col = -1; + if (start_col < 0 && start_line > 0) { do start_line--; while ((start_col = teco_view_ssm(view, SCI_GETLINESTATE, start_line, 0)) < 0 && start_line > 0); - start_col = MAX(start_col, 0); } + start_col = MAX(start_col, 0); start = teco_view_ssm(view, SCI_POSITIONFROMLINE, start_line, 0) + start_col; g_assert(end > start); |
