aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/lexer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lexer.c')
-rw-r--r--src/lexer.c71
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);