diff options
| author | frosty <gabriel@bwaaa.monster> | 2026-08-17 16:32:52 -0400 |
|---|---|---|
| committer | frosty <gabriel@bwaaa.monster> | 2026-08-17 16:32:52 -0400 |
| commit | f9e3b8ba7da220746151179ed10a90c2ac215ffc (patch) | |
| tree | 0d18a6617fefc2b41d9db75f5c79335607954874 /main/src/ui/conversation_content_view/syntax_highlighting.vala | |
| parent | 6d0706bcaa6d54354e416174e3d2654a2bff81fe (diff) | |
Begin implementing codeblocks feature
Diffstat (limited to 'main/src/ui/conversation_content_view/syntax_highlighting.vala')
| -rw-r--r-- | main/src/ui/conversation_content_view/syntax_highlighting.vala | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/main/src/ui/conversation_content_view/syntax_highlighting.vala b/main/src/ui/conversation_content_view/syntax_highlighting.vala new file mode 100644 index 00000000..b45ba81c --- /dev/null +++ b/main/src/ui/conversation_content_view/syntax_highlighting.vala @@ -0,0 +1,136 @@ +using Gee; + +namespace Dino.Ui.CodeSyntax { + +public enum TokenStyle { + COMMENT, + CONSTANT, + FUNCTION, + KEYWORD, + NUMBER, + PREPROCESSOR, + STRING, + TYPE +} + +public class HighlightSpan : Object { + public int start; + public int end; + public TokenStyle style; + + public HighlightSpan(int start, int end, TokenStyle style) { + this.start = start; + this.end = end; + this.style = style; + } +} + +public class HighlightRule : Object { + public TokenStyle style; + private Regex regex; + + public HighlightRule(TokenStyle style, string pattern) { + this.style = style; + try { + regex = new Regex(pattern, RegexCompileFlags.OPTIMIZE); + } catch (RegexError error) { + GLib.error("Invalid syntax highlighting rule: %s", error.message); + } + } + + public bool find(string text, int offset, out int start, out int end) { + MatchInfo match_info; + try { + regex.match_full(text, -1, offset, 0, out match_info); + } catch (RegexError error) { + start = -1; + end = -1; + return false; + } + if (!match_info.matches()) { + start = -1; + end = -1; + return false; + } + match_info.fetch_pos(0, out start, out end); + return true; + } +} + +public class LanguageDefinition : Object { + public string id; + public string name; + public string extension; + public string[] aliases; + public HighlightRule[] rules; + + public LanguageDefinition(string id, string name, string extension, string[] aliases, HighlightRule[] rules) { + this.id = id; + this.name = name; + this.extension = extension; + this.aliases = aliases; + this.rules = rules; + } + + public bool matches(string language) { + string normalized = language.down().strip(); + if (normalized == id) return true; + foreach (string alias in aliases) { + if (normalized == alias) return true; + } + return false; + } + + public ArrayList<HighlightSpan> highlight(string text) { + var spans = new ArrayList<HighlightSpan>(); + int offset = 0; + while (offset < text.length) { + HighlightRule? selected_rule = null; + int selected_start = text.length; + int selected_end = text.length; + foreach (HighlightRule rule in rules) { + int start; + int end; + if (rule.find(text, offset, out start, out end) && start < selected_start) { + selected_rule = rule; + selected_start = start; + selected_end = end; + } + } + if (selected_rule == null || selected_end <= selected_start) break; + spans.add(new HighlightSpan(selected_start, selected_end, selected_rule.style)); + offset = selected_end; + } + return spans; + } +} + +private static LanguageDefinition[]? definitions; + +public static LanguageDefinition? find(string language) { + if (definitions == null) definitions = create_definitions(); + foreach (LanguageDefinition definition in definitions) { + if (definition.matches(language)) return definition; + } + return null; +} + +private static LanguageDefinition[] create_definitions() { + return { create_c_definition() }; +} + +private static LanguageDefinition create_c_definition() { + HighlightRule[] rules = { + new HighlightRule(TokenStyle.COMMENT, "//[^\\r\\n]*|/\\*[\\s\\S]*?\\*/"), + new HighlightRule(TokenStyle.STRING, "\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*'"), + new HighlightRule(TokenStyle.PREPROCESSOR, "(?m)^[\\t ]*#[\\t ]*[A-Za-z_][A-Za-z0-9_]*"), + new HighlightRule(TokenStyle.KEYWORD, "\\b(?:alignas|alignof|asm|auto|break|case|const|constexpr|continue|default|do|else|enum|extern|for|goto|if|inline|register|restrict|return|sizeof|static|static_assert|struct|switch|thread_local|typedef|typeof|typeof_unqual|union|volatile|while|_Alignas|_Alignof|_Atomic|_Generic|_Noreturn|_Static_assert|_Thread_local)\\b"), + new HighlightRule(TokenStyle.TYPE, "\\b(?:bool|char|char8_t|double|float|int|long|short|signed|unsigned|void|_Bool|_Complex|_Decimal32|_Decimal64|_Decimal128|_Imaginary|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|intptr_t|uintptr_t|ptrdiff_t|size_t|ssize_t|wchar_t)\\b"), + new HighlightRule(TokenStyle.CONSTANT, "\\b(?:false|NULL|nullptr|true)\\b"), + new HighlightRule(TokenStyle.NUMBER, "\\b(?:0[xX][0-9A-Fa-f]+|0[bB][01]+|(?:[0-9]+\\.[0-9]*|[0-9]+)(?:[eEpP][+-]?[0-9]+)?)[fFlLuU]*\\b"), + new HighlightRule(TokenStyle.FUNCTION, "\\b[A-Za-z_][A-Za-z0-9_]*(?=[\\t ]*\\()") + }; + return new LanguageDefinition("c", "C", "c", { "h", "c89", "c90", "c99", "c11", "c17", "c18", "c23" }, rules); +} + +} |
