aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/conversation_content_view/syntax_highlighting.vala
blob: b45ba81cb83773a5276479e6ac012526486944b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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);
}

}