tenseleyflow/fackr / be98840

Browse files

feat: add syntax highlighting module

Add syntax highlighting infrastructure with support for multiple languages.
Includes token-based highlighting with configurable colors for keywords,
strings, comments, numbers, and other language constructs.
Authored by espadonne
SHA
be9884003e828352c63d9fa85d0d1b508b418bd4
Parents
b8f7ffd
Tree
1d7feea

3 changed files

StatusFile+-
A src/syntax/highlight.rs 642 0
A src/syntax/languages.rs 1618 0
A src/syntax/mod.rs 6 0
src/syntax/highlight.rsadded
@@ -0,0 +1,642 @@
1
+//! Core syntax highlighting engine
2
+
3
+#![allow(dead_code)]
4
+
5
+use super::languages::{Language, LanguageDef};
6
+use crossterm::style::Color;
7
+
8
+/// Token types for syntax highlighting
9
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10
+pub enum TokenType {
11
+    Plain,
12
+    Keyword,
13
+    String,
14
+    Number,
15
+    Comment,
16
+    Operator,
17
+    Type,
18
+    Function,
19
+    Preprocessor,
20
+    Attribute,
21
+    Punctuation,
22
+}
23
+
24
+impl TokenType {
25
+    /// Get the foreground color for this token type
26
+    pub fn color(&self) -> Color {
27
+        match self {
28
+            TokenType::Plain => Color::Reset,
29
+            TokenType::Keyword => Color::Blue,
30
+            TokenType::String => Color::Green,
31
+            TokenType::Number => Color::Magenta,
32
+            TokenType::Comment => Color::DarkGrey,
33
+            TokenType::Operator => Color::Yellow,
34
+            TokenType::Type => Color::Cyan,
35
+            TokenType::Function => Color::Cyan,
36
+            TokenType::Preprocessor => Color::Magenta,
37
+            TokenType::Attribute => Color::Yellow,
38
+            TokenType::Punctuation => Color::DarkGrey,
39
+        }
40
+    }
41
+
42
+    /// Whether this token type should be bold
43
+    pub fn bold(&self) -> bool {
44
+        matches!(self, TokenType::Keyword | TokenType::Function)
45
+    }
46
+}
47
+
48
+/// A token in a line of text
49
+#[derive(Debug, Clone)]
50
+pub struct Token {
51
+    /// Token type
52
+    pub token_type: TokenType,
53
+    /// Start column (character index, not byte)
54
+    pub start: usize,
55
+    /// End column (exclusive, character index)
56
+    pub end: usize,
57
+}
58
+
59
+/// State for multiline constructs (comments, strings)
60
+#[derive(Debug, Clone, Default)]
61
+pub struct HighlightState {
62
+    /// Currently in a multiline comment
63
+    pub in_block_comment: bool,
64
+    /// Currently in a multiline string (stores delimiter for matching)
65
+    pub in_multiline_string: Option<String>,
66
+}
67
+
68
+/// Syntax highlighter for a specific language
69
+#[derive(Debug)]
70
+pub struct Highlighter {
71
+    /// Current language definition
72
+    language: Option<LanguageDef>,
73
+    /// State for multiline constructs
74
+    state: HighlightState,
75
+}
76
+
77
+impl Default for Highlighter {
78
+    fn default() -> Self {
79
+        Self::new()
80
+    }
81
+}
82
+
83
+impl Highlighter {
84
+    /// Create a new highlighter with no language
85
+    pub fn new() -> Self {
86
+        Self {
87
+            language: None,
88
+            state: HighlightState::default(),
89
+        }
90
+    }
91
+
92
+    /// Detect and set language based on filename
93
+    pub fn detect_language(&mut self, filename: &str) {
94
+        self.language = Language::detect(filename).map(|l| l.definition());
95
+        self.state = HighlightState::default();
96
+    }
97
+
98
+    /// Set language explicitly
99
+    pub fn set_language(&mut self, lang: Language) {
100
+        self.language = Some(lang.definition());
101
+        self.state = HighlightState::default();
102
+    }
103
+
104
+    /// Clear language (disable highlighting)
105
+    pub fn clear_language(&mut self) {
106
+        self.language = None;
107
+        self.state = HighlightState::default();
108
+    }
109
+
110
+    /// Check if highlighting is enabled
111
+    pub fn is_enabled(&self) -> bool {
112
+        self.language.is_some()
113
+    }
114
+
115
+    /// Get current language name
116
+    pub fn language_name(&self) -> Option<&str> {
117
+        self.language.as_ref().map(|l| l.name)
118
+    }
119
+
120
+    /// Reset multiline state (call when buffer changes significantly)
121
+    pub fn reset_state(&mut self) {
122
+        self.state = HighlightState::default();
123
+    }
124
+
125
+    /// Tokenize a single line, returning tokens and updated state
126
+    /// The state should be passed from the previous line for correct multiline handling
127
+    pub fn tokenize_line(&self, line: &str, state: &mut HighlightState) -> Vec<Token> {
128
+        let lang = match &self.language {
129
+            Some(l) => l,
130
+            None => return vec![],
131
+        };
132
+
133
+        let mut tokens = Vec::new();
134
+        let chars: Vec<char> = line.chars().collect();
135
+        let mut i = 0;
136
+
137
+        while i < chars.len() {
138
+            // Handle continuing multiline comment
139
+            if state.in_block_comment {
140
+                if let Some((end_start, end_len)) = self.find_block_comment_end(lang, &chars, i) {
141
+                    tokens.push(Token {
142
+                        token_type: TokenType::Comment,
143
+                        start: i,
144
+                        end: end_start + end_len,
145
+                    });
146
+                    i = end_start + end_len;
147
+                    state.in_block_comment = false;
148
+                    continue;
149
+                } else {
150
+                    // Rest of line is comment
151
+                    tokens.push(Token {
152
+                        token_type: TokenType::Comment,
153
+                        start: i,
154
+                        end: chars.len(),
155
+                    });
156
+                    break;
157
+                }
158
+            }
159
+
160
+            // Handle continuing multiline string
161
+            if let Some(ref delim) = state.in_multiline_string.clone() {
162
+                if let Some(end_pos) = self.find_string_end(&chars, i, delim) {
163
+                    tokens.push(Token {
164
+                        token_type: TokenType::String,
165
+                        start: i,
166
+                        end: end_pos,
167
+                    });
168
+                    i = end_pos;
169
+                    state.in_multiline_string = None;
170
+                    continue;
171
+                } else {
172
+                    // Rest of line is string
173
+                    tokens.push(Token {
174
+                        token_type: TokenType::String,
175
+                        start: i,
176
+                        end: chars.len(),
177
+                    });
178
+                    break;
179
+                }
180
+            }
181
+
182
+            // Skip whitespace
183
+            if chars[i].is_whitespace() {
184
+                i += 1;
185
+                continue;
186
+            }
187
+
188
+            // Check for line comment
189
+            if let Some(ref comment) = lang.line_comment {
190
+                if self.matches_at(&chars, i, comment) {
191
+                    tokens.push(Token {
192
+                        token_type: TokenType::Comment,
193
+                        start: i,
194
+                        end: chars.len(),
195
+                    });
196
+                    break;
197
+                }
198
+            }
199
+
200
+            // Check for block comment start
201
+            if let (Some(ref start), Some(_)) = (&lang.block_comment_start, &lang.block_comment_end) {
202
+                if self.matches_at(&chars, i, start) {
203
+                    let comment_start = i;
204
+                    i += start.chars().count();
205
+
206
+                    if let Some((end_start, end_len)) = self.find_block_comment_end(lang, &chars, i) {
207
+                        tokens.push(Token {
208
+                            token_type: TokenType::Comment,
209
+                            start: comment_start,
210
+                            end: end_start + end_len,
211
+                        });
212
+                        i = end_start + end_len;
213
+                    } else {
214
+                        // Multiline comment continues
215
+                        tokens.push(Token {
216
+                            token_type: TokenType::Comment,
217
+                            start: comment_start,
218
+                            end: chars.len(),
219
+                        });
220
+                        state.in_block_comment = true;
221
+                        break;
222
+                    }
223
+                    continue;
224
+                }
225
+            }
226
+
227
+            // Check for strings
228
+            if let Some((token, new_i, multiline_delim)) = self.try_parse_string(lang, &chars, i) {
229
+                tokens.push(token);
230
+                i = new_i;
231
+                if let Some(delim) = multiline_delim {
232
+                    state.in_multiline_string = Some(delim);
233
+                    break;
234
+                }
235
+                continue;
236
+            }
237
+
238
+            // Check for numbers
239
+            if let Some((token, new_i)) = self.try_parse_number(&chars, i) {
240
+                tokens.push(token);
241
+                i = new_i;
242
+                continue;
243
+            }
244
+
245
+            // Check for preprocessor directives
246
+            if lang.has_preprocessor && chars[i] == '#' && self.is_line_start(&chars, i) {
247
+                tokens.push(Token {
248
+                    token_type: TokenType::Preprocessor,
249
+                    start: i,
250
+                    end: chars.len(),
251
+                });
252
+                break;
253
+            }
254
+
255
+            // Check for attributes (Rust #[], Python @)
256
+            if let Some((token, new_i)) = self.try_parse_attribute(lang, &chars, i) {
257
+                tokens.push(token);
258
+                i = new_i;
259
+                continue;
260
+            }
261
+
262
+            // Check for identifiers (keywords, types, functions)
263
+            if chars[i].is_alphabetic() || chars[i] == '_' {
264
+                let start = i;
265
+                while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
266
+                    i += 1;
267
+                }
268
+                let word: String = chars[start..i].iter().collect();
269
+
270
+                let token_type = if lang.keywords.contains(&word.as_str()) {
271
+                    TokenType::Keyword
272
+                } else if lang.types.contains(&word.as_str()) {
273
+                    TokenType::Type
274
+                } else if i < chars.len() && chars[i] == '(' {
275
+                    TokenType::Function
276
+                } else {
277
+                    TokenType::Plain
278
+                };
279
+
280
+                if token_type != TokenType::Plain {
281
+                    tokens.push(Token {
282
+                        token_type,
283
+                        start,
284
+                        end: i,
285
+                    });
286
+                }
287
+                continue;
288
+            }
289
+
290
+            // Check for operators
291
+            if let Some((token, new_i)) = self.try_parse_operator(lang, &chars, i) {
292
+                tokens.push(token);
293
+                i = new_i;
294
+                continue;
295
+            }
296
+
297
+            // Check for punctuation
298
+            if lang.punctuation.contains(&chars[i]) {
299
+                tokens.push(Token {
300
+                    token_type: TokenType::Punctuation,
301
+                    start: i,
302
+                    end: i + 1,
303
+                });
304
+                i += 1;
305
+                continue;
306
+            }
307
+
308
+            // Skip unknown character
309
+            i += 1;
310
+        }
311
+
312
+        tokens
313
+    }
314
+
315
+    fn matches_at(&self, chars: &[char], pos: usize, pattern: &str) -> bool {
316
+        let pattern_chars: Vec<char> = pattern.chars().collect();
317
+        if pos + pattern_chars.len() > chars.len() {
318
+            return false;
319
+        }
320
+        for (i, &pc) in pattern_chars.iter().enumerate() {
321
+            if chars[pos + i] != pc {
322
+                return false;
323
+            }
324
+        }
325
+        true
326
+    }
327
+
328
+    fn find_block_comment_end(&self, lang: &LanguageDef, chars: &[char], start: usize) -> Option<(usize, usize)> {
329
+        let end_pattern = lang.block_comment_end.as_ref()?;
330
+        let end_chars: Vec<char> = end_pattern.chars().collect();
331
+
332
+        for i in start..chars.len() {
333
+            if self.matches_at(chars, i, end_pattern) {
334
+                return Some((i, end_chars.len()));
335
+            }
336
+        }
337
+        None
338
+    }
339
+
340
+    fn try_parse_string(&self, lang: &LanguageDef, chars: &[char], start: usize) -> Option<(Token, usize, Option<String>)> {
341
+        let c = chars[start];
342
+
343
+        // Check for string delimiters
344
+        if !lang.string_delimiters.contains(&c) {
345
+            return None;
346
+        }
347
+
348
+        // Check for triple-quoted strings (Python, etc.)
349
+        if lang.multiline_strings {
350
+            let triple: String = std::iter::repeat(c).take(3).collect();
351
+            if self.matches_at(chars, start, &triple) {
352
+                let delim_len = 3;
353
+                let mut i = start + delim_len;
354
+
355
+                while i < chars.len() {
356
+                    if self.matches_at(chars, i, &triple) {
357
+                        return Some((
358
+                            Token {
359
+                                token_type: TokenType::String,
360
+                                start,
361
+                                end: i + delim_len,
362
+                            },
363
+                            i + delim_len,
364
+                            None,
365
+                        ));
366
+                    }
367
+                    if chars[i] == '\\' && i + 1 < chars.len() {
368
+                        i += 2;
369
+                    } else {
370
+                        i += 1;
371
+                    }
372
+                }
373
+
374
+                // String continues on next line
375
+                return Some((
376
+                    Token {
377
+                        token_type: TokenType::String,
378
+                        start,
379
+                        end: chars.len(),
380
+                    },
381
+                    chars.len(),
382
+                    Some(triple),
383
+                ));
384
+            }
385
+        }
386
+
387
+        // Regular string
388
+        let mut i = start + 1;
389
+        while i < chars.len() {
390
+            if chars[i] == c {
391
+                return Some((
392
+                    Token {
393
+                        token_type: TokenType::String,
394
+                        start,
395
+                        end: i + 1,
396
+                    },
397
+                    i + 1,
398
+                    None,
399
+                ));
400
+            }
401
+            if chars[i] == '\\' && i + 1 < chars.len() {
402
+                i += 2;
403
+            } else {
404
+                i += 1;
405
+            }
406
+        }
407
+
408
+        // Unterminated string - highlight to end of line
409
+        Some((
410
+            Token {
411
+                token_type: TokenType::String,
412
+                start,
413
+                end: chars.len(),
414
+            },
415
+            chars.len(),
416
+            None,
417
+        ))
418
+    }
419
+
420
+    fn find_string_end(&self, chars: &[char], start: usize, delim: &str) -> Option<usize> {
421
+        let mut i = start;
422
+        while i < chars.len() {
423
+            if self.matches_at(chars, i, delim) {
424
+                return Some(i + delim.chars().count());
425
+            }
426
+            if chars[i] == '\\' && i + 1 < chars.len() {
427
+                i += 2;
428
+            } else {
429
+                i += 1;
430
+            }
431
+        }
432
+        None
433
+    }
434
+
435
+    fn try_parse_number(&self, chars: &[char], start: usize) -> Option<(Token, usize)> {
436
+        let c = chars[start];
437
+
438
+        // Must start with digit, or . followed by digit
439
+        if !c.is_ascii_digit() {
440
+            if c == '.' && start + 1 < chars.len() && chars[start + 1].is_ascii_digit() {
441
+                // .5 style float
442
+            } else {
443
+                return None;
444
+            }
445
+        }
446
+
447
+        let mut i = start;
448
+        let mut has_dot = c == '.';
449
+        let mut has_exp = false;
450
+
451
+        // Handle hex, octal, binary
452
+        if c == '0' && i + 1 < chars.len() {
453
+            match chars[i + 1] {
454
+                'x' | 'X' => {
455
+                    i += 2;
456
+                    while i < chars.len() && (chars[i].is_ascii_hexdigit() || chars[i] == '_') {
457
+                        i += 1;
458
+                    }
459
+                    return Some((Token { token_type: TokenType::Number, start, end: i }, i));
460
+                }
461
+                'o' | 'O' => {
462
+                    i += 2;
463
+                    while i < chars.len() && (chars[i].is_digit(8) || chars[i] == '_') {
464
+                        i += 1;
465
+                    }
466
+                    return Some((Token { token_type: TokenType::Number, start, end: i }, i));
467
+                }
468
+                'b' | 'B' => {
469
+                    i += 2;
470
+                    while i < chars.len() && (chars[i] == '0' || chars[i] == '1' || chars[i] == '_') {
471
+                        i += 1;
472
+                    }
473
+                    return Some((Token { token_type: TokenType::Number, start, end: i }, i));
474
+                }
475
+                _ => {}
476
+            }
477
+        }
478
+
479
+        // Decimal number (possibly float)
480
+        while i < chars.len() {
481
+            let ch = chars[i];
482
+            if ch.is_ascii_digit() || ch == '_' {
483
+                i += 1;
484
+            } else if ch == '.' && !has_dot && !has_exp {
485
+                // Check it's not a method call like 5.to_string()
486
+                if i + 1 < chars.len() && chars[i + 1].is_ascii_digit() {
487
+                    has_dot = true;
488
+                    i += 1;
489
+                } else if i + 1 >= chars.len() {
490
+                    has_dot = true;
491
+                    i += 1;
492
+                } else {
493
+                    break;
494
+                }
495
+            } else if (ch == 'e' || ch == 'E') && !has_exp {
496
+                has_exp = true;
497
+                i += 1;
498
+                if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
499
+                    i += 1;
500
+                }
501
+            } else {
502
+                break;
503
+            }
504
+        }
505
+
506
+        // Handle type suffixes (f32, i64, etc.)
507
+        if i < chars.len() && chars[i].is_alphabetic() {
508
+            let suffix_start = i;
509
+            while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
510
+                i += 1;
511
+            }
512
+            // Common numeric suffixes
513
+            let suffix: String = chars[suffix_start..i].iter().collect();
514
+            let valid_suffixes = ["f32", "f64", "i8", "i16", "i32", "i64", "i128", "isize",
515
+                                  "u8", "u16", "u32", "u64", "u128", "usize", "f", "d", "l", "L"];
516
+            if !valid_suffixes.contains(&suffix.as_str()) {
517
+                i = suffix_start; // Not a valid suffix, rollback
518
+            }
519
+        }
520
+
521
+        if i > start {
522
+            Some((Token { token_type: TokenType::Number, start, end: i }, i))
523
+        } else {
524
+            None
525
+        }
526
+    }
527
+
528
+    fn try_parse_operator(&self, lang: &LanguageDef, chars: &[char], start: usize) -> Option<(Token, usize)> {
529
+        // Try longer operators first
530
+        for &op in &lang.operators {
531
+            if self.matches_at(chars, start, op) {
532
+                let len = op.chars().count();
533
+                return Some((
534
+                    Token {
535
+                        token_type: TokenType::Operator,
536
+                        start,
537
+                        end: start + len,
538
+                    },
539
+                    start + len,
540
+                ));
541
+            }
542
+        }
543
+        None
544
+    }
545
+
546
+    fn try_parse_attribute(&self, lang: &LanguageDef, chars: &[char], start: usize) -> Option<(Token, usize)> {
547
+        // Rust attributes: #[...] or #![...]
548
+        if lang.name == "Rust" && chars[start] == '#' {
549
+            let mut i = start + 1;
550
+            if i < chars.len() && chars[i] == '!' {
551
+                i += 1;
552
+            }
553
+            if i < chars.len() && chars[i] == '[' {
554
+                let attr_start = start;
555
+                let mut bracket_depth = 1;
556
+                i += 1;
557
+                while i < chars.len() && bracket_depth > 0 {
558
+                    match chars[i] {
559
+                        '[' => bracket_depth += 1,
560
+                        ']' => bracket_depth -= 1,
561
+                        _ => {}
562
+                    }
563
+                    i += 1;
564
+                }
565
+                return Some((
566
+                    Token {
567
+                        token_type: TokenType::Attribute,
568
+                        start: attr_start,
569
+                        end: i,
570
+                    },
571
+                    i,
572
+                ));
573
+            }
574
+        }
575
+
576
+        // Python decorators: @name
577
+        if lang.name == "Python" && chars[start] == '@' {
578
+            let mut i = start + 1;
579
+            while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == '.') {
580
+                i += 1;
581
+            }
582
+            if i > start + 1 {
583
+                return Some((
584
+                    Token {
585
+                        token_type: TokenType::Attribute,
586
+                        start,
587
+                        end: i,
588
+                    },
589
+                    i,
590
+                ));
591
+            }
592
+        }
593
+
594
+        None
595
+    }
596
+
597
+    fn is_line_start(&self, chars: &[char], pos: usize) -> bool {
598
+        for i in 0..pos {
599
+            if !chars[i].is_whitespace() {
600
+                return false;
601
+            }
602
+        }
603
+        true
604
+    }
605
+}
606
+
607
+#[cfg(test)]
608
+mod tests {
609
+    use super::*;
610
+
611
+    #[test]
612
+    fn test_rust_keywords() {
613
+        let mut hl = Highlighter::new();
614
+        hl.set_language(Language::Rust);
615
+        let mut state = HighlightState::default();
616
+
617
+        let tokens = hl.tokenize_line("let x = 42;", &mut state);
618
+        assert!(tokens.iter().any(|t| t.token_type == TokenType::Keyword)); // let
619
+        assert!(tokens.iter().any(|t| t.token_type == TokenType::Number));  // 42
620
+    }
621
+
622
+    #[test]
623
+    fn test_string_parsing() {
624
+        let mut hl = Highlighter::new();
625
+        hl.set_language(Language::Rust);
626
+        let mut state = HighlightState::default();
627
+
628
+        let tokens = hl.tokenize_line(r#"let s = "hello";"#, &mut state);
629
+        assert!(tokens.iter().any(|t| t.token_type == TokenType::String));
630
+    }
631
+
632
+    #[test]
633
+    fn test_comment_parsing() {
634
+        let mut hl = Highlighter::new();
635
+        hl.set_language(Language::Rust);
636
+        let mut state = HighlightState::default();
637
+
638
+        let tokens = hl.tokenize_line("// this is a comment", &mut state);
639
+        assert_eq!(tokens.len(), 1);
640
+        assert_eq!(tokens[0].token_type, TokenType::Comment);
641
+    }
642
+}
src/syntax/languages.rsadded
1618 lines changed — click to load
@@ -0,0 +1,1618 @@
1
+//! Language definitions for syntax highlighting
2
+
3
+#![allow(dead_code)]
4
+
5
+use std::collections::HashSet;
6
+
7
+/// Supported programming languages
8
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9
+pub enum Language {
10
+    Rust,
11
+    Python,
12
+    JavaScript,
13
+    TypeScript,
14
+    C,
15
+    Cpp,
16
+    Go,
17
+    Java,
18
+    Kotlin,
19
+    Swift,
20
+    Ruby,
21
+    Php,
22
+    CSharp,
23
+    Scala,
24
+    Haskell,
25
+    Lua,
26
+    Perl,
27
+    R,
28
+    Julia,
29
+    Elixir,
30
+    Erlang,
31
+    Clojure,
32
+    Fortran,
33
+    Zig,
34
+    Nim,
35
+    Odin,
36
+    V,
37
+    D,
38
+    Bash,
39
+    Fish,
40
+    Zsh,
41
+    PowerShell,
42
+    Sql,
43
+    Html,
44
+    Css,
45
+    Json,
46
+    Yaml,
47
+    Toml,
48
+    Xml,
49
+    Markdown,
50
+    Makefile,
51
+    Dockerfile,
52
+    Terraform,
53
+    Nix,
54
+    Ocaml,
55
+    Fsharp,
56
+    Dart,
57
+    Groovy,
58
+}
59
+
60
+impl Language {
61
+    /// Detect language from filename/extension
62
+    pub fn detect(filename: &str) -> Option<Language> {
63
+        let lower = filename.to_lowercase();
64
+
65
+        // Check full filename first (for files like Makefile, Dockerfile)
66
+        let basename = lower.rsplit('/').next().unwrap_or(&lower);
67
+
68
+        match basename {
69
+            "makefile" | "gnumakefile" => return Some(Language::Makefile),
70
+            "dockerfile" => return Some(Language::Dockerfile),
71
+            "cmakelists.txt" => return Some(Language::Makefile),
72
+            ".bashrc" | ".bash_profile" | ".profile" => return Some(Language::Bash),
73
+            ".zshrc" | ".zprofile" => return Some(Language::Zsh),
74
+            "cargo.toml" | "pyproject.toml" => return Some(Language::Toml),
75
+            "package.json" | "tsconfig.json" => return Some(Language::Json),
76
+            _ => {}
77
+        }
78
+
79
+        // Check extension
80
+        let ext = lower.rsplit('.').next()?;
81
+
82
+        match ext {
83
+            // Rust
84
+            "rs" => Some(Language::Rust),
85
+
86
+            // Python
87
+            "py" | "pyw" | "pyi" | "pyx" => Some(Language::Python),
88
+
89
+            // JavaScript / TypeScript
90
+            "js" | "mjs" | "cjs" | "jsx" => Some(Language::JavaScript),
91
+            "ts" | "mts" | "cts" | "tsx" => Some(Language::TypeScript),
92
+
93
+            // C / C++
94
+            "c" | "h" => Some(Language::C),
95
+            "cpp" | "cc" | "cxx" | "c++" | "hpp" | "hh" | "hxx" | "h++" | "ipp" => Some(Language::Cpp),
96
+
97
+            // Go
98
+            "go" => Some(Language::Go),
99
+
100
+            // Java / JVM languages
101
+            "java" => Some(Language::Java),
102
+            "kt" | "kts" => Some(Language::Kotlin),
103
+            "scala" | "sc" => Some(Language::Scala),
104
+            "groovy" | "gvy" | "gy" | "gsh" => Some(Language::Groovy),
105
+            "clj" | "cljs" | "cljc" | "edn" => Some(Language::Clojure),
106
+
107
+            // Apple / Swift
108
+            "swift" => Some(Language::Swift),
109
+
110
+            // Ruby
111
+            "rb" | "rake" | "gemspec" => Some(Language::Ruby),
112
+
113
+            // PHP
114
+            "php" | "php3" | "php4" | "php5" | "phtml" => Some(Language::Php),
115
+
116
+            // C# / .NET
117
+            "cs" => Some(Language::CSharp),
118
+            "fs" | "fsx" | "fsi" => Some(Language::Fsharp),
119
+
120
+            // Functional languages
121
+            "hs" | "lhs" => Some(Language::Haskell),
122
+            "ml" | "mli" => Some(Language::Ocaml),
123
+            "ex" | "exs" => Some(Language::Elixir),
124
+            "erl" | "hrl" => Some(Language::Erlang),
125
+
126
+            // Scripting
127
+            "lua" => Some(Language::Lua),
128
+            "pl" | "pm" | "t" => Some(Language::Perl),
129
+            "r" | "R" => Some(Language::R),
130
+            "jl" => Some(Language::Julia),
131
+
132
+            // System languages
133
+            "zig" => Some(Language::Zig),
134
+            "nim" | "nims" => Some(Language::Nim),
135
+            "odin" => Some(Language::Odin),
136
+            "v" => Some(Language::V),
137
+            "d" => Some(Language::D),
138
+            "f90" | "f95" | "f03" | "f08" | "f18" | "f" | "for" => Some(Language::Fortran),
139
+
140
+            // Shell
141
+            "sh" | "bash" => Some(Language::Bash),
142
+            "fish" => Some(Language::Fish),
143
+            "zsh" => Some(Language::Zsh),
144
+            "ps1" | "psm1" | "psd1" => Some(Language::PowerShell),
145
+
146
+            // Data / Config
147
+            "sql" => Some(Language::Sql),
148
+            "json" | "jsonc" | "json5" => Some(Language::Json),
149
+            "yaml" | "yml" => Some(Language::Yaml),
150
+            "toml" => Some(Language::Toml),
151
+            "xml" | "svg" | "xsl" | "xslt" => Some(Language::Xml),
152
+
153
+            // Web
154
+            "html" | "htm" | "xhtml" => Some(Language::Html),
155
+            "css" | "scss" | "sass" | "less" => Some(Language::Css),
156
+
157
+            // Documentation
158
+            "md" | "markdown" | "mdown" | "mkd" => Some(Language::Markdown),
159
+
160
+            // DevOps / Infrastructure
161
+            "tf" | "tfvars" => Some(Language::Terraform),
162
+            "nix" => Some(Language::Nix),
163
+
164
+            // Flutter / Dart
165
+            "dart" => Some(Language::Dart),
166
+
167
+            _ => None,
168
+        }
169
+    }
170
+
171
+    /// Get the language definition
172
+    pub fn definition(&self) -> LanguageDef {
173
+        match self {
174
+            Language::Rust => rust_def(),
175
+            Language::Python => python_def(),
176
+            Language::JavaScript => javascript_def(),
177
+            Language::TypeScript => typescript_def(),
178
+            Language::C => c_def(),
179
+            Language::Cpp => cpp_def(),
180
+            Language::Go => go_def(),
181
+            Language::Java => java_def(),
182
+            Language::Kotlin => kotlin_def(),
183
+            Language::Swift => swift_def(),
184
+            Language::Ruby => ruby_def(),
185
+            Language::Php => php_def(),
186
+            Language::CSharp => csharp_def(),
187
+            Language::Scala => scala_def(),
188
+            Language::Haskell => haskell_def(),
189
+            Language::Lua => lua_def(),
190
+            Language::Perl => perl_def(),
191
+            Language::R => r_def(),
192
+            Language::Julia => julia_def(),
193
+            Language::Elixir => elixir_def(),
194
+            Language::Erlang => erlang_def(),
195
+            Language::Clojure => clojure_def(),
196
+            Language::Fortran => fortran_def(),
197
+            Language::Zig => zig_def(),
198
+            Language::Nim => nim_def(),
199
+            Language::Odin => odin_def(),
200
+            Language::V => v_def(),
201
+            Language::D => d_def(),
202
+            Language::Bash => bash_def(),
203
+            Language::Fish => fish_def(),
204
+            Language::Zsh => zsh_def(),
205
+            Language::PowerShell => powershell_def(),
206
+            Language::Sql => sql_def(),
207
+            Language::Html => html_def(),
208
+            Language::Css => css_def(),
209
+            Language::Json => json_def(),
210
+            Language::Yaml => yaml_def(),
211
+            Language::Toml => toml_def(),
212
+            Language::Xml => xml_def(),
213
+            Language::Markdown => markdown_def(),
214
+            Language::Makefile => makefile_def(),
215
+            Language::Dockerfile => dockerfile_def(),
216
+            Language::Terraform => terraform_def(),
217
+            Language::Nix => nix_def(),
218
+            Language::Ocaml => ocaml_def(),
219
+            Language::Fsharp => fsharp_def(),
220
+            Language::Dart => dart_def(),
221
+            Language::Groovy => groovy_def(),
222
+        }
223
+    }
224
+
225
+    /// Get display name
226
+    pub fn name(&self) -> &'static str {
227
+        match self {
228
+            Language::Rust => "Rust",
229
+            Language::Python => "Python",
230
+            Language::JavaScript => "JavaScript",
231
+            Language::TypeScript => "TypeScript",
232
+            Language::C => "C",
233
+            Language::Cpp => "C++",
234
+            Language::Go => "Go",
235
+            Language::Java => "Java",
236
+            Language::Kotlin => "Kotlin",
237
+            Language::Swift => "Swift",
238
+            Language::Ruby => "Ruby",
239
+            Language::Php => "PHP",
240
+            Language::CSharp => "C#",
241
+            Language::Scala => "Scala",
242
+            Language::Haskell => "Haskell",
243
+            Language::Lua => "Lua",
244
+            Language::Perl => "Perl",
245
+            Language::R => "R",
246
+            Language::Julia => "Julia",
247
+            Language::Elixir => "Elixir",
248
+            Language::Erlang => "Erlang",
249
+            Language::Clojure => "Clojure",
250
+            Language::Fortran => "Fortran",
251
+            Language::Zig => "Zig",
252
+            Language::Nim => "Nim",
253
+            Language::Odin => "Odin",
254
+            Language::V => "V",
255
+            Language::D => "D",
256
+            Language::Bash => "Bash",
257
+            Language::Fish => "Fish",
258
+            Language::Zsh => "Zsh",
259
+            Language::PowerShell => "PowerShell",
260
+            Language::Sql => "SQL",
261
+            Language::Html => "HTML",
262
+            Language::Css => "CSS",
263
+            Language::Json => "JSON",
264
+            Language::Yaml => "YAML",
265
+            Language::Toml => "TOML",
266
+            Language::Xml => "XML",
267
+            Language::Markdown => "Markdown",
268
+            Language::Makefile => "Makefile",
269
+            Language::Dockerfile => "Dockerfile",
270
+            Language::Terraform => "Terraform",
271
+            Language::Nix => "Nix",
272
+            Language::Ocaml => "OCaml",
273
+            Language::Fsharp => "F#",
274
+            Language::Dart => "Dart",
275
+            Language::Groovy => "Groovy",
276
+        }
277
+    }
278
+}
279
+
280
+/// Language definition for syntax highlighting
281
+#[derive(Debug, Clone)]
282
+pub struct LanguageDef {
283
+    pub name: &'static str,
284
+    pub keywords: HashSet<&'static str>,
285
+    pub types: HashSet<&'static str>,
286
+    pub line_comment: Option<&'static str>,
287
+    pub block_comment_start: Option<&'static str>,
288
+    pub block_comment_end: Option<&'static str>,
289
+    pub string_delimiters: Vec<char>,
290
+    pub multiline_strings: bool,
291
+    pub operators: Vec<&'static str>,
292
+    pub punctuation: Vec<char>,
293
+    pub has_preprocessor: bool,
294
+    pub case_sensitive: bool,
295
+}
296
+
297
+impl Default for LanguageDef {
298
+    fn default() -> Self {
299
+        Self {
300
+            name: "Plain",
301
+            keywords: HashSet::new(),
302
+            types: HashSet::new(),
303
+            line_comment: None,
304
+            block_comment_start: None,
305
+            block_comment_end: None,
306
+            string_delimiters: vec!['"', '\''],
307
+            multiline_strings: false,
308
+            operators: vec![],
309
+            punctuation: vec![],
310
+            has_preprocessor: false,
311
+            case_sensitive: true,
312
+        }
313
+    }
314
+}
315
+
316
+// Common operators used by C-like languages
317
+const C_OPERATORS: &[&str] = &[
318
+    "->", "++", "--", "<<", ">>", "<=", ">=", "==", "!=", "&&", "||",
319
+    "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=",
320
+    "+", "-", "*", "/", "%", "&", "|", "^", "~", "!", "<", ">", "=", "?", ":",
321
+];
322
+
323
+const C_PUNCTUATION: &[char] = &['{', '}', '(', ')', '[', ']', ';', ',', '.'];
324
+
325
+// ============================================================================
326
+// Language Definitions
327
+// ============================================================================
328
+
329
+fn rust_def() -> LanguageDef {
330
+    LanguageDef {
331
+        name: "Rust",
332
+        keywords: [
333
+            "as", "async", "await", "break", "const", "continue", "crate", "dyn",
334
+            "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in",
335
+            "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return",
336
+            "self", "Self", "static", "struct", "super", "trait", "true", "type",
337
+            "unsafe", "use", "where", "while", "yield",
338
+        ].into_iter().collect(),
339
+        types: [
340
+            "bool", "char", "str", "u8", "u16", "u32", "u64", "u128", "usize",
341
+            "i8", "i16", "i32", "i64", "i128", "isize", "f32", "f64",
342
+            "String", "Vec", "Box", "Rc", "Arc", "Cell", "RefCell", "Option",
343
+            "Result", "Ok", "Err", "Some", "None", "HashMap", "HashSet",
344
+            "BTreeMap", "BTreeSet", "VecDeque", "LinkedList", "BinaryHeap",
345
+        ].into_iter().collect(),
346
+        line_comment: Some("//"),
347
+        block_comment_start: Some("/*"),
348
+        block_comment_end: Some("*/"),
349
+        string_delimiters: vec!['"'],
350
+        multiline_strings: false,
351
+        operators: C_OPERATORS.to_vec(),
352
+        punctuation: C_PUNCTUATION.to_vec(),
353
+        has_preprocessor: false,
354
+        case_sensitive: true,
355
+    }
356
+}
357
+
358
+fn python_def() -> LanguageDef {
359
+    LanguageDef {
360
+        name: "Python",
361
+        keywords: [
362
+            "False", "None", "True", "and", "as", "assert", "async", "await",
363
+            "break", "class", "continue", "def", "del", "elif", "else", "except",
364
+            "finally", "for", "from", "global", "if", "import", "in", "is",
365
+            "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try",
366
+            "while", "with", "yield", "match", "case", "type",
367
+        ].into_iter().collect(),
368
+        types: [
369
+            "int", "float", "str", "bool", "list", "dict", "set", "tuple",
370
+            "bytes", "bytearray", "complex", "frozenset", "object", "type",
371
+            "None", "Callable", "Iterator", "Generator", "Coroutine",
372
+            "Optional", "Union", "Any", "List", "Dict", "Set", "Tuple",
373
+        ].into_iter().collect(),
374
+        line_comment: Some("#"),
375
+        block_comment_start: None,
376
+        block_comment_end: None,
377
+        string_delimiters: vec!['"', '\''],
378
+        multiline_strings: true, // """...""" and '''...'''
379
+        operators: vec![
380
+            "**", "//", "<<", ">>", "<=", ">=", "==", "!=", "->",
381
+            "+=", "-=", "*=", "/=", "//=", "%=", "**=", "&=", "|=", "^=", ">>=", "<<=",
382
+            "+", "-", "*", "/", "%", "&", "|", "^", "~", "<", ">", "=", "@",
383
+        ],
384
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ':', ',', '.', ';'],
385
+        has_preprocessor: false,
386
+        case_sensitive: true,
387
+    }
388
+}
389
+
390
+fn javascript_def() -> LanguageDef {
391
+    LanguageDef {
392
+        name: "JavaScript",
393
+        keywords: [
394
+            "async", "await", "break", "case", "catch", "class", "const",
395
+            "continue", "debugger", "default", "delete", "do", "else", "export",
396
+            "extends", "false", "finally", "for", "function", "if", "import",
397
+            "in", "instanceof", "let", "new", "null", "of", "return", "static",
398
+            "super", "switch", "this", "throw", "true", "try", "typeof", "var",
399
+            "void", "while", "with", "yield", "undefined", "NaN", "Infinity",
400
+        ].into_iter().collect(),
401
+        types: [
402
+            "Array", "Boolean", "Date", "Error", "Function", "JSON", "Map",
403
+            "Math", "Number", "Object", "Promise", "Proxy", "RegExp", "Set",
404
+            "String", "Symbol", "WeakMap", "WeakSet", "BigInt", "ArrayBuffer",
405
+            "DataView", "Float32Array", "Float64Array", "Int8Array", "Int16Array",
406
+            "Int32Array", "Uint8Array", "Uint16Array", "Uint32Array",
407
+        ].into_iter().collect(),
408
+        line_comment: Some("//"),
409
+        block_comment_start: Some("/*"),
410
+        block_comment_end: Some("*/"),
411
+        string_delimiters: vec!['"', '\'', '`'],
412
+        multiline_strings: true, // template literals
413
+        operators: C_OPERATORS.to_vec(),
414
+        punctuation: C_PUNCTUATION.to_vec(),
415
+        has_preprocessor: false,
416
+        case_sensitive: true,
417
+    }
418
+}
419
+
420
+fn typescript_def() -> LanguageDef {
421
+    let mut def = javascript_def();
422
+    def.name = "TypeScript";
423
+    def.keywords.extend([
424
+        "abstract", "as", "asserts", "declare", "enum", "get", "implements",
425
+        "interface", "is", "keyof", "module", "namespace", "never", "override",
426
+        "private", "protected", "public", "readonly", "require", "set", "type",
427
+        "infer", "satisfies",
428
+    ]);
429
+    def.types.extend([
430
+        "any", "boolean", "never", "null", "number", "object", "string",
431
+        "symbol", "undefined", "unknown", "void", "Partial", "Required",
432
+        "Readonly", "Record", "Pick", "Omit", "Exclude", "Extract",
433
+        "NonNullable", "Parameters", "ReturnType", "InstanceType",
434
+    ]);
435
+    def
436
+}
437
+
438
+fn c_def() -> LanguageDef {
439
+    LanguageDef {
440
+        name: "C",
441
+        keywords: [
442
+            "auto", "break", "case", "const", "continue", "default", "do",
443
+            "else", "enum", "extern", "for", "goto", "if", "inline", "register",
444
+            "restrict", "return", "sizeof", "static", "struct", "switch",
445
+            "typedef", "union", "volatile", "while", "_Alignas", "_Alignof",
446
+            "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary",
447
+            "_Noreturn", "_Static_assert", "_Thread_local",
448
+        ].into_iter().collect(),
449
+        types: [
450
+            "char", "double", "float", "int", "long", "short", "signed",
451
+            "unsigned", "void", "size_t", "ssize_t", "ptrdiff_t", "intptr_t",
452
+            "uintptr_t", "int8_t", "int16_t", "int32_t", "int64_t",
453
+            "uint8_t", "uint16_t", "uint32_t", "uint64_t", "bool", "FILE",
454
+        ].into_iter().collect(),
455
+        line_comment: Some("//"),
456
+        block_comment_start: Some("/*"),
457
+        block_comment_end: Some("*/"),
458
+        string_delimiters: vec!['"', '\''],
459
+        multiline_strings: false,
460
+        operators: C_OPERATORS.to_vec(),
461
+        punctuation: C_PUNCTUATION.to_vec(),
462
+        has_preprocessor: true,
463
+        case_sensitive: true,
464
+    }
465
+}
466
+
467
+fn cpp_def() -> LanguageDef {
468
+    let mut def = c_def();
469
+    def.name = "C++";
470
+    def.keywords.extend([
471
+        "alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor",
472
+        "catch", "class", "compl", "concept", "consteval", "constexpr",
473
+        "constinit", "const_cast", "co_await", "co_return", "co_yield",
474
+        "decltype", "delete", "dynamic_cast", "explicit", "export", "false",
475
+        "friend", "module", "mutable", "namespace", "new", "noexcept", "not",
476
+        "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected",
477
+        "public", "reinterpret_cast", "requires", "static_assert", "static_cast",
478
+        "template", "this", "throw", "true", "try", "typeid", "typename",
479
+        "using", "virtual", "xor", "xor_eq", "override", "final",
480
+    ]);
481
+    def.types.extend([
482
+        "auto", "wchar_t", "char8_t", "char16_t", "char32_t", "string",
483
+        "wstring", "string_view", "vector", "map", "unordered_map", "set",
484
+        "unordered_set", "list", "deque", "array", "pair", "tuple",
485
+        "optional", "variant", "any", "span", "unique_ptr", "shared_ptr",
486
+        "weak_ptr", "function", "thread", "mutex", "atomic",
487
+    ]);
488
+    def
489
+}
490
+
491
+fn go_def() -> LanguageDef {
492
+    LanguageDef {
493
+        name: "Go",
494
+        keywords: [
495
+            "break", "case", "chan", "const", "continue", "default", "defer",
496
+            "else", "fallthrough", "for", "func", "go", "goto", "if", "import",
497
+            "interface", "map", "package", "range", "return", "select", "struct",
498
+            "switch", "type", "var", "true", "false", "nil", "iota",
499
+        ].into_iter().collect(),
500
+        types: [
501
+            "bool", "byte", "complex64", "complex128", "error", "float32",
502
+            "float64", "int", "int8", "int16", "int32", "int64", "rune",
503
+            "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr",
504
+            "any", "comparable",
505
+        ].into_iter().collect(),
506
+        line_comment: Some("//"),
507
+        block_comment_start: Some("/*"),
508
+        block_comment_end: Some("*/"),
509
+        string_delimiters: vec!['"', '\'', '`'],
510
+        multiline_strings: true,
511
+        operators: vec![
512
+            ":=", "...", "++", "--", "<<", ">>", "&^", "<=", ">=", "==", "!=",
513
+            "&&", "||", "<-", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=",
514
+            "<<=", ">>=", "&^=",
515
+            "+", "-", "*", "/", "%", "&", "|", "^", "<", ">", "=", "!",
516
+        ],
517
+        punctuation: C_PUNCTUATION.to_vec(),
518
+        has_preprocessor: false,
519
+        case_sensitive: true,
520
+    }
521
+}
522
+
523
+fn java_def() -> LanguageDef {
524
+    LanguageDef {
525
+        name: "Java",
526
+        keywords: [
527
+            "abstract", "assert", "boolean", "break", "byte", "case", "catch",
528
+            "char", "class", "const", "continue", "default", "do", "double",
529
+            "else", "enum", "extends", "final", "finally", "float", "for",
530
+            "goto", "if", "implements", "import", "instanceof", "int",
531
+            "interface", "long", "native", "new", "package", "private",
532
+            "protected", "public", "return", "short", "static", "strictfp",
533
+            "super", "switch", "synchronized", "this", "throw", "throws",
534
+            "transient", "try", "void", "volatile", "while", "true", "false",
535
+            "null", "var", "yield", "record", "sealed", "non-sealed", "permits",
536
+        ].into_iter().collect(),
537
+        types: [
538
+            "Boolean", "Byte", "Character", "Class", "Double", "Enum", "Float",
539
+            "Integer", "Long", "Number", "Object", "Short", "String", "Void",
540
+            "List", "Map", "Set", "Collection", "ArrayList", "HashMap", "HashSet",
541
+            "LinkedList", "TreeMap", "TreeSet", "Optional", "Stream", "Thread",
542
+        ].into_iter().collect(),
543
+        line_comment: Some("//"),
544
+        block_comment_start: Some("/*"),
545
+        block_comment_end: Some("*/"),
546
+        string_delimiters: vec!['"', '\''],
547
+        multiline_strings: true, // text blocks with """
548
+        operators: C_OPERATORS.to_vec(),
549
+        punctuation: C_PUNCTUATION.to_vec(),
550
+        has_preprocessor: false,
551
+        case_sensitive: true,
552
+    }
553
+}
554
+
555
+fn kotlin_def() -> LanguageDef {
556
+    LanguageDef {
557
+        name: "Kotlin",
558
+        keywords: [
559
+            "as", "break", "by", "catch", "class", "companion", "const",
560
+            "constructor", "continue", "crossinline", "data", "do", "else",
561
+            "enum", "external", "false", "final", "finally", "for", "fun",
562
+            "get", "if", "import", "in", "infix", "init", "inline", "inner",
563
+            "interface", "internal", "is", "lateinit", "noinline", "null",
564
+            "object", "open", "operator", "out", "override", "package",
565
+            "private", "protected", "public", "reified", "return", "sealed",
566
+            "set", "super", "suspend", "tailrec", "this", "throw", "true",
567
+            "try", "typealias", "typeof", "val", "var", "vararg", "when",
568
+            "where", "while",
569
+        ].into_iter().collect(),
570
+        types: [
571
+            "Any", "Boolean", "Byte", "Char", "Double", "Float", "Int", "Long",
572
+            "Nothing", "Number", "Short", "String", "Unit", "Array", "List",
573
+            "Map", "Set", "MutableList", "MutableMap", "MutableSet", "Pair",
574
+            "Triple", "Sequence", "Lazy",
575
+        ].into_iter().collect(),
576
+        line_comment: Some("//"),
577
+        block_comment_start: Some("/*"),
578
+        block_comment_end: Some("*/"),
579
+        string_delimiters: vec!['"', '\''],
580
+        multiline_strings: true,
581
+        operators: C_OPERATORS.to_vec(),
582
+        punctuation: C_PUNCTUATION.to_vec(),
583
+        has_preprocessor: false,
584
+        case_sensitive: true,
585
+    }
586
+}
587
+
588
+fn swift_def() -> LanguageDef {
589
+    LanguageDef {
590
+        name: "Swift",
591
+        keywords: [
592
+            "actor", "any", "as", "associatedtype", "async", "await", "break",
593
+            "case", "catch", "class", "continue", "convenience", "default",
594
+            "defer", "deinit", "didSet", "do", "dynamic", "else", "enum",
595
+            "extension", "fallthrough", "false", "fileprivate", "final", "for",
596
+            "func", "get", "guard", "if", "import", "in", "indirect", "infix",
597
+            "init", "inout", "internal", "is", "isolated", "lazy", "let",
598
+            "mutating", "nil", "nonisolated", "nonmutating", "open", "operator",
599
+            "optional", "override", "postfix", "precedencegroup", "prefix",
600
+            "private", "protocol", "public", "repeat", "required", "rethrows",
601
+            "return", "self", "Self", "set", "some", "static", "struct",
602
+            "subscript", "super", "switch", "throw", "throws", "true", "try",
603
+            "typealias", "unowned", "var", "weak", "where", "while", "willSet",
604
+        ].into_iter().collect(),
605
+        types: [
606
+            "Any", "AnyObject", "Array", "Bool", "Character", "Dictionary",
607
+            "Double", "Float", "Int", "Int8", "Int16", "Int32", "Int64",
608
+            "Optional", "Result", "Set", "String", "UInt", "UInt8", "UInt16",
609
+            "UInt32", "UInt64", "Void", "Never",
610
+        ].into_iter().collect(),
611
+        line_comment: Some("//"),
612
+        block_comment_start: Some("/*"),
613
+        block_comment_end: Some("*/"),
614
+        string_delimiters: vec!['"'],
615
+        multiline_strings: true,
616
+        operators: C_OPERATORS.to_vec(),
617
+        punctuation: C_PUNCTUATION.to_vec(),
618
+        has_preprocessor: false,
619
+        case_sensitive: true,
620
+    }
621
+}
622
+
623
+fn ruby_def() -> LanguageDef {
624
+    LanguageDef {
625
+        name: "Ruby",
626
+        keywords: [
627
+            "BEGIN", "END", "alias", "and", "begin", "break", "case", "class",
628
+            "def", "defined?", "do", "else", "elsif", "end", "ensure", "false",
629
+            "for", "if", "in", "module", "next", "nil", "not", "or", "redo",
630
+            "rescue", "retry", "return", "self", "super", "then", "true",
631
+            "undef", "unless", "until", "when", "while", "yield", "__FILE__",
632
+            "__LINE__", "__ENCODING__", "lambda", "proc", "raise", "require",
633
+            "require_relative", "attr_accessor", "attr_reader", "attr_writer",
634
+            "private", "protected", "public",
635
+        ].into_iter().collect(),
636
+        types: [
637
+            "Array", "Bignum", "Binding", "Class", "Continuation", "Dir",
638
+            "Exception", "FalseClass", "File", "Fixnum", "Float", "Hash",
639
+            "Integer", "IO", "MatchData", "Method", "Module", "NilClass",
640
+            "Numeric", "Object", "Proc", "Range", "Regexp", "String", "Struct",
641
+            "Symbol", "Thread", "Time", "TrueClass",
642
+        ].into_iter().collect(),
643
+        line_comment: Some("#"),
644
+        block_comment_start: Some("=begin"),
645
+        block_comment_end: Some("=end"),
646
+        string_delimiters: vec!['"', '\'', '`'],
647
+        multiline_strings: true,
648
+        operators: vec![
649
+            "**", "..", "...", "<<", ">>", "<=>", "<=", ">=", "==", "===",
650
+            "!=", "=~", "!~", "&&", "||", "+=", "-=", "*=", "/=", "%=",
651
+            "**=", "&=", "|=", "^=", "<<=", ">>=", "&&=", "||=",
652
+            "+", "-", "*", "/", "%", "&", "|", "^", "~", "<", ">", "=", "!",
653
+        ],
654
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':', '@', '$'],
655
+        has_preprocessor: false,
656
+        case_sensitive: true,
657
+    }
658
+}
659
+
660
+fn php_def() -> LanguageDef {
661
+    LanguageDef {
662
+        name: "PHP",
663
+        keywords: [
664
+            "abstract", "and", "array", "as", "break", "callable", "case",
665
+            "catch", "class", "clone", "const", "continue", "declare", "default",
666
+            "die", "do", "echo", "else", "elseif", "empty", "enddeclare",
667
+            "endfor", "endforeach", "endif", "endswitch", "endwhile", "eval",
668
+            "exit", "extends", "final", "finally", "fn", "for", "foreach",
669
+            "function", "global", "goto", "if", "implements", "include",
670
+            "include_once", "instanceof", "insteadof", "interface", "isset",
671
+            "list", "match", "namespace", "new", "or", "print", "private",
672
+            "protected", "public", "readonly", "require", "require_once",
673
+            "return", "static", "switch", "throw", "trait", "try", "unset",
674
+            "use", "var", "while", "xor", "yield", "true", "false", "null",
675
+        ].into_iter().collect(),
676
+        types: [
677
+            "bool", "boolean", "int", "integer", "float", "double", "string",
678
+            "array", "object", "callable", "iterable", "void", "mixed", "never",
679
+            "null", "self", "parent", "static",
680
+        ].into_iter().collect(),
681
+        line_comment: Some("//"),
682
+        block_comment_start: Some("/*"),
683
+        block_comment_end: Some("*/"),
684
+        string_delimiters: vec!['"', '\''],
685
+        multiline_strings: true,
686
+        operators: C_OPERATORS.to_vec(),
687
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':', '$', '@'],
688
+        has_preprocessor: false,
689
+        case_sensitive: false,
690
+    }
691
+}
692
+
693
+fn csharp_def() -> LanguageDef {
694
+    LanguageDef {
695
+        name: "C#",
696
+        keywords: [
697
+            "abstract", "as", "base", "bool", "break", "byte", "case", "catch",
698
+            "char", "checked", "class", "const", "continue", "decimal", "default",
699
+            "delegate", "do", "double", "else", "enum", "event", "explicit",
700
+            "extern", "false", "finally", "fixed", "float", "for", "foreach",
701
+            "goto", "if", "implicit", "in", "int", "interface", "internal",
702
+            "is", "lock", "long", "namespace", "new", "null", "object",
703
+            "operator", "out", "override", "params", "private", "protected",
704
+            "public", "readonly", "ref", "return", "sbyte", "sealed", "short",
705
+            "sizeof", "stackalloc", "static", "string", "struct", "switch",
706
+            "this", "throw", "true", "try", "typeof", "uint", "ulong",
707
+            "unchecked", "unsafe", "ushort", "using", "var", "virtual", "void",
708
+            "volatile", "while", "async", "await", "dynamic", "nameof", "when",
709
+            "record", "init", "with", "required", "file", "scoped",
710
+        ].into_iter().collect(),
711
+        types: [
712
+            "Boolean", "Byte", "Char", "DateTime", "Decimal", "Double", "Guid",
713
+            "Int16", "Int32", "Int64", "Object", "SByte", "Single", "String",
714
+            "TimeSpan", "UInt16", "UInt32", "UInt64", "List", "Dictionary",
715
+            "HashSet", "Queue", "Stack", "Array", "Task", "Func", "Action",
716
+            "IEnumerable", "IList", "IDictionary", "ICollection",
717
+        ].into_iter().collect(),
718
+        line_comment: Some("//"),
719
+        block_comment_start: Some("/*"),
720
+        block_comment_end: Some("*/"),
721
+        string_delimiters: vec!['"', '\''],
722
+        multiline_strings: true,
723
+        operators: C_OPERATORS.to_vec(),
724
+        punctuation: C_PUNCTUATION.to_vec(),
725
+        has_preprocessor: true,
726
+        case_sensitive: true,
727
+    }
728
+}
729
+
730
+fn scala_def() -> LanguageDef {
731
+    LanguageDef {
732
+        name: "Scala",
733
+        keywords: [
734
+            "abstract", "case", "catch", "class", "def", "do", "else", "enum",
735
+            "export", "extends", "extension", "false", "final", "finally",
736
+            "for", "forSome", "given", "if", "implicit", "import", "inline",
737
+            "lazy", "match", "new", "null", "object", "opaque", "open",
738
+            "override", "package", "private", "protected", "return", "sealed",
739
+            "super", "then", "this", "throw", "trait", "transparent", "true",
740
+            "try", "type", "using", "val", "var", "while", "with", "yield",
741
+        ].into_iter().collect(),
742
+        types: [
743
+            "Any", "AnyRef", "AnyVal", "Array", "BigDecimal", "BigInt", "Boolean",
744
+            "Byte", "Char", "Double", "Float", "Int", "List", "Long", "Map",
745
+            "Nothing", "Null", "Option", "Seq", "Set", "Short", "Some", "String",
746
+            "Unit", "Vector",
747
+        ].into_iter().collect(),
748
+        line_comment: Some("//"),
749
+        block_comment_start: Some("/*"),
750
+        block_comment_end: Some("*/"),
751
+        string_delimiters: vec!['"', '\''],
752
+        multiline_strings: true,
753
+        operators: C_OPERATORS.to_vec(),
754
+        punctuation: C_PUNCTUATION.to_vec(),
755
+        has_preprocessor: false,
756
+        case_sensitive: true,
757
+    }
758
+}
759
+
760
+fn haskell_def() -> LanguageDef {
761
+    LanguageDef {
762
+        name: "Haskell",
763
+        keywords: [
764
+            "as", "case", "class", "data", "default", "deriving", "do", "else",
765
+            "forall", "foreign", "hiding", "if", "import", "in", "infix",
766
+            "infixl", "infixr", "instance", "let", "mdo", "module", "newtype",
767
+            "of", "proc", "qualified", "rec", "then", "type", "where",
768
+        ].into_iter().collect(),
769
+        types: [
770
+            "Bool", "Char", "Double", "Either", "Float", "IO", "Int", "Integer",
771
+            "Maybe", "Ordering", "String", "Word",
772
+        ].into_iter().collect(),
773
+        line_comment: Some("--"),
774
+        block_comment_start: Some("{-"),
775
+        block_comment_end: Some("-}"),
776
+        string_delimiters: vec!['"', '\''],
777
+        multiline_strings: false,
778
+        operators: vec![
779
+            "->", "<-", "=>", "::", "++", ">>", ">>=", "<$>", "<*>", "<|>",
780
+            ".", "$", "=", "<", ">", "+", "-", "*", "/", "^", "&", "|",
781
+        ],
782
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '`'],
783
+        has_preprocessor: false,
784
+        case_sensitive: true,
785
+    }
786
+}
787
+
788
+fn lua_def() -> LanguageDef {
789
+    LanguageDef {
790
+        name: "Lua",
791
+        keywords: [
792
+            "and", "break", "do", "else", "elseif", "end", "false", "for",
793
+            "function", "goto", "if", "in", "local", "nil", "not", "or",
794
+            "repeat", "return", "then", "true", "until", "while",
795
+        ].into_iter().collect(),
796
+        types: HashSet::new(),
797
+        line_comment: Some("--"),
798
+        block_comment_start: Some("--[["),
799
+        block_comment_end: Some("]]"),
800
+        string_delimiters: vec!['"', '\''],
801
+        multiline_strings: true,
802
+        operators: vec![
803
+            "..", "...", "==", "~=", "<=", ">=", "<<", ">>", "//",
804
+            "+", "-", "*", "/", "%", "^", "#", "&", "|", "~", "<", ">", "=",
805
+        ],
806
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':'],
807
+        has_preprocessor: false,
808
+        case_sensitive: true,
809
+    }
810
+}
811
+
812
+fn perl_def() -> LanguageDef {
813
+    LanguageDef {
814
+        name: "Perl",
815
+        keywords: [
816
+            "and", "cmp", "continue", "do", "else", "elsif", "eq", "for",
817
+            "foreach", "ge", "goto", "gt", "if", "last", "le", "lt", "my",
818
+            "ne", "next", "no", "or", "our", "package", "redo", "require",
819
+            "return", "sub", "unless", "until", "use", "while", "xor",
820
+        ].into_iter().collect(),
821
+        types: HashSet::new(),
822
+        line_comment: Some("#"),
823
+        block_comment_start: Some("=pod"),
824
+        block_comment_end: Some("=cut"),
825
+        string_delimiters: vec!['"', '\''],
826
+        multiline_strings: true,
827
+        operators: vec![
828
+            "->", "++", "--", "**", "=~", "!~", "<=", ">=", "==", "!=",
829
+            "<=>", "&&", "||", "..", "...",
830
+            "+", "-", "*", "/", "%", ".", "<", ">", "&", "|", "^", "~", "!",
831
+        ],
832
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', ':', '$', '@', '%'],
833
+        has_preprocessor: false,
834
+        case_sensitive: true,
835
+    }
836
+}
837
+
838
+fn r_def() -> LanguageDef {
839
+    LanguageDef {
840
+        name: "R",
841
+        keywords: [
842
+            "break", "else", "for", "function", "if", "in", "next", "repeat",
843
+            "return", "while", "TRUE", "FALSE", "NULL", "NA", "NA_integer_",
844
+            "NA_real_", "NA_complex_", "NA_character_", "Inf", "NaN",
845
+        ].into_iter().collect(),
846
+        types: [
847
+            "character", "complex", "double", "expression", "integer", "list",
848
+            "logical", "numeric", "raw", "vector", "matrix", "array",
849
+            "data.frame", "factor",
850
+        ].into_iter().collect(),
851
+        line_comment: Some("#"),
852
+        block_comment_start: None,
853
+        block_comment_end: None,
854
+        string_delimiters: vec!['"', '\''],
855
+        multiline_strings: false,
856
+        operators: vec![
857
+            "<-", "->", "<<-", "->>", "%%", "%/%", "%*%", "%in%", "%o%", "%x%",
858
+            "<=", ">=", "==", "!=", "&&", "||", "!", "&", "|",
859
+            "+", "-", "*", "/", "^", ":", "$", "@", "~",
860
+        ],
861
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ','],
862
+        has_preprocessor: false,
863
+        case_sensitive: true,
864
+    }
865
+}
866
+
867
+fn julia_def() -> LanguageDef {
868
+    LanguageDef {
869
+        name: "Julia",
870
+        keywords: [
871
+            "abstract", "baremodule", "begin", "break", "catch", "const",
872
+            "continue", "do", "else", "elseif", "end", "export", "false",
873
+            "finally", "for", "function", "global", "if", "import", "in",
874
+            "let", "local", "macro", "module", "mutable", "primitive", "quote",
875
+            "return", "struct", "true", "try", "type", "using", "where", "while",
876
+        ].into_iter().collect(),
877
+        types: [
878
+            "Any", "Bool", "Char", "Complex", "Float16", "Float32", "Float64",
879
+            "Int", "Int8", "Int16", "Int32", "Int64", "Int128", "Integer",
880
+            "Nothing", "Number", "Rational", "Real", "String", "Symbol",
881
+            "UInt", "UInt8", "UInt16", "UInt32", "UInt64", "UInt128",
882
+            "Array", "Dict", "Set", "Tuple", "Vector", "Matrix",
883
+        ].into_iter().collect(),
884
+        line_comment: Some("#"),
885
+        block_comment_start: Some("#="),
886
+        block_comment_end: Some("=#"),
887
+        string_delimiters: vec!['"', '\''],
888
+        multiline_strings: true,
889
+        operators: vec![
890
+            "->", "=>", "::", "...", "..", "<=", ">=", "==", "!=", "===", "!==",
891
+            "&&", "||", "⊻", "<<", ">>", ">>>", "+=", "-=", "*=", "/=",
892
+            "+", "-", "*", "/", "\\", "^", "%", "&", "|", "~", "<", ">", "!",
893
+        ],
894
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':'],
895
+        has_preprocessor: false,
896
+        case_sensitive: true,
897
+    }
898
+}
899
+
900
+fn elixir_def() -> LanguageDef {
901
+    LanguageDef {
902
+        name: "Elixir",
903
+        keywords: [
904
+            "after", "alias", "and", "case", "catch", "cond", "def", "defp",
905
+            "defcallback", "defdelegate", "defexception", "defguard",
906
+            "defguardp", "defimpl", "defmacro", "defmacrop", "defmodule",
907
+            "defoverridable", "defprotocol", "defstruct", "do", "else", "end",
908
+            "false", "fn", "for", "if", "import", "in", "nil", "not", "or",
909
+            "quote", "raise", "receive", "require", "rescue", "true", "try",
910
+            "unless", "unquote", "unquote_splicing", "use", "when", "with",
911
+        ].into_iter().collect(),
912
+        types: [
913
+            "Atom", "BitString", "Float", "Function", "Integer", "List", "Map",
914
+            "PID", "Port", "Reference", "Tuple", "String", "Keyword",
915
+        ].into_iter().collect(),
916
+        line_comment: Some("#"),
917
+        block_comment_start: None,
918
+        block_comment_end: None,
919
+        string_delimiters: vec!['"', '\''],
920
+        multiline_strings: true,
921
+        operators: vec![
922
+            "->", "<-", "|>", "++", "--", "<>", "..", "<=", ">=", "==", "!=",
923
+            "===", "!==", "&&", "||", "and", "or", "not", "in",
924
+            "+", "-", "*", "/", "^", "&", "|", "~", "<", ">", "=", "!",
925
+        ],
926
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':', '@', '%'],
927
+        has_preprocessor: false,
928
+        case_sensitive: true,
929
+    }
930
+}
931
+
932
+fn erlang_def() -> LanguageDef {
933
+    LanguageDef {
934
+        name: "Erlang",
935
+        keywords: [
936
+            "after", "and", "andalso", "band", "begin", "bnot", "bor", "bsl",
937
+            "bsr", "bxor", "case", "catch", "cond", "div", "end", "fun", "if",
938
+            "let", "not", "of", "or", "orelse", "receive", "rem", "try", "when",
939
+            "xor",
940
+        ].into_iter().collect(),
941
+        types: HashSet::new(),
942
+        line_comment: Some("%"),
943
+        block_comment_start: None,
944
+        block_comment_end: None,
945
+        string_delimiters: vec!['"', '\''],
946
+        multiline_strings: false,
947
+        operators: vec![
948
+            "->", "<-", "++", "--", "==", "/=", "=<", ">=", "=:=", "=/=",
949
+            "||", "!", "+", "-", "*", "/", "<", ">", "=",
950
+        ],
951
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':', '|'],
952
+        has_preprocessor: false,
953
+        case_sensitive: true,
954
+    }
955
+}
956
+
957
+fn clojure_def() -> LanguageDef {
958
+    LanguageDef {
959
+        name: "Clojure",
960
+        keywords: [
961
+            "def", "defn", "defn-", "defmacro", "defmulti", "defmethod",
962
+            "defprotocol", "defrecord", "defstruct", "deftype", "fn", "if",
963
+            "do", "let", "loop", "recur", "throw", "try", "catch", "finally",
964
+            "quote", "var", "import", "use", "require", "ns", "in-ns", "new",
965
+            "set!", "nil", "true", "false",
966
+        ].into_iter().collect(),
967
+        types: HashSet::new(),
968
+        line_comment: Some(";"),
969
+        block_comment_start: None,
970
+        block_comment_end: None,
971
+        string_delimiters: vec!['"'],
972
+        multiline_strings: true,
973
+        operators: vec![
974
+            "->", "->>", "=>", "@", "^", "`", "~", "~@", "#",
975
+        ],
976
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ':', '\''],
977
+        has_preprocessor: false,
978
+        case_sensitive: true,
979
+    }
980
+}
981
+
982
+fn fortran_def() -> LanguageDef {
983
+    LanguageDef {
984
+        name: "Fortran",
985
+        keywords: [
986
+            "allocatable", "allocate", "assign", "associate", "asynchronous",
987
+            "backspace", "block", "call", "case", "character", "class", "close",
988
+            "codimension", "common", "complex", "concurrent", "contains",
989
+            "contiguous", "continue", "critical", "cycle", "data", "deallocate",
990
+            "default", "deferred", "dimension", "do", "double", "elemental",
991
+            "else", "elseif", "elsewhere", "end", "endfile", "endif", "entry",
992
+            "enum", "enumerator", "equivalence", "error", "exit", "extends",
993
+            "external", "final", "flush", "forall", "format", "function",
994
+            "generic", "go", "goto", "if", "images", "implicit", "import",
995
+            "include", "inquire", "intent", "interface", "intrinsic", "kind",
996
+            "len", "lock", "logical", "module", "namelist", "none", "nopass",
997
+            "nullify", "only", "open", "operator", "optional", "out", "parameter",
998
+            "pass", "pause", "pointer", "precision", "print", "private",
999
+            "procedure", "program", "protected", "public", "pure", "read",
1000
+            "real", "recursive", "result", "return", "rewind", "save", "select",
1001
+            "sequence", "stop", "submodule", "subroutine", "sync", "target",
1002
+            "then", "to", "type", "unlock", "use", "value", "volatile", "wait",
1003
+            "where", "while", "write",
1004
+        ].into_iter().collect(),
1005
+        types: [
1006
+            "integer", "real", "complex", "character", "logical", "double",
1007
+            "precision", "type", "class",
1008
+        ].into_iter().collect(),
1009
+        line_comment: Some("!"),
1010
+        block_comment_start: None,
1011
+        block_comment_end: None,
1012
+        string_delimiters: vec!['"', '\''],
1013
+        multiline_strings: false,
1014
+        operators: vec![
1015
+            "==", "/=", "<=", ">=", "**", "//", ".eq.", ".ne.", ".lt.", ".le.",
1016
+            ".gt.", ".ge.", ".and.", ".or.", ".not.", ".eqv.", ".neqv.",
1017
+            "+", "-", "*", "/", "<", ">", "=",
1018
+        ],
1019
+        punctuation: vec!['(', ')', '[', ']', ',', ':', '%'],
1020
+        has_preprocessor: false,
1021
+        case_sensitive: false,
1022
+    }
1023
+}
1024
+
1025
+fn zig_def() -> LanguageDef {
1026
+    LanguageDef {
1027
+        name: "Zig",
1028
+        keywords: [
1029
+            "addrspace", "align", "allowzero", "and", "anyframe", "anytype",
1030
+            "asm", "async", "await", "break", "callconv", "catch", "comptime",
1031
+            "const", "continue", "defer", "else", "enum", "errdefer", "error",
1032
+            "export", "extern", "fn", "for", "if", "inline", "linksection",
1033
+            "noalias", "noinline", "nosuspend", "opaque", "or", "orelse",
1034
+            "packed", "pub", "resume", "return", "struct", "suspend", "switch",
1035
+            "test", "threadlocal", "try", "union", "unreachable", "usingnamespace",
1036
+            "var", "volatile", "while", "null", "undefined", "true", "false",
1037
+        ].into_iter().collect(),
1038
+        types: [
1039
+            "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "i128", "u128",
1040
+            "isize", "usize", "f16", "f32", "f64", "f80", "f128", "bool", "void",
1041
+            "anyerror", "anyopaque", "comptime_int", "comptime_float", "type",
1042
+            "noreturn",
1043
+        ].into_iter().collect(),
1044
+        line_comment: Some("//"),
1045
+        block_comment_start: None,
1046
+        block_comment_end: None,
1047
+        string_delimiters: vec!['"'],
1048
+        multiline_strings: false,
1049
+        operators: C_OPERATORS.to_vec(),
1050
+        punctuation: C_PUNCTUATION.to_vec(),
1051
+        has_preprocessor: false,
1052
+        case_sensitive: true,
1053
+    }
1054
+}
1055
+
1056
+fn nim_def() -> LanguageDef {
1057
+    LanguageDef {
1058
+        name: "Nim",
1059
+        keywords: [
1060
+            "addr", "and", "as", "asm", "bind", "block", "break", "case", "cast",
1061
+            "concept", "const", "continue", "converter", "defer", "discard",
1062
+            "distinct", "div", "do", "elif", "else", "end", "enum", "except",
1063
+            "export", "finally", "for", "from", "func", "if", "import", "in",
1064
+            "include", "interface", "is", "isnot", "iterator", "let", "macro",
1065
+            "method", "mixin", "mod", "nil", "not", "notin", "object", "of",
1066
+            "or", "out", "proc", "ptr", "raise", "ref", "return", "shl", "shr",
1067
+            "static", "template", "try", "tuple", "type", "using", "var", "when",
1068
+            "while", "xor", "yield", "true", "false",
1069
+        ].into_iter().collect(),
1070
+        types: [
1071
+            "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16",
1072
+            "uint32", "uint64", "float", "float32", "float64", "bool", "char",
1073
+            "string", "cstring", "pointer", "seq", "array", "set", "tuple",
1074
+            "object", "ref", "ptr", "proc", "iterator", "void",
1075
+        ].into_iter().collect(),
1076
+        line_comment: Some("#"),
1077
+        block_comment_start: Some("#["),
1078
+        block_comment_end: Some("]#"),
1079
+        string_delimiters: vec!['"', '\''],
1080
+        multiline_strings: true,
1081
+        operators: vec![
1082
+            "==", "!=", "<=", ">=", "<", ">", "+=", "-=", "*=", "/=", "&=",
1083
+            "and", "or", "not", "xor", "shl", "shr", "div", "mod",
1084
+            "+", "-", "*", "/", "&", "|", "^", "~", "@", "$",
1085
+        ],
1086
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':'],
1087
+        has_preprocessor: false,
1088
+        case_sensitive: true,
1089
+    }
1090
+}
1091
+
1092
+fn odin_def() -> LanguageDef {
1093
+    LanguageDef {
1094
+        name: "Odin",
1095
+        keywords: [
1096
+            "align_of", "auto_cast", "bit_set", "break", "case", "cast",
1097
+            "context", "continue", "defer", "distinct", "do", "dynamic",
1098
+            "else", "enum", "fallthrough", "false", "for", "foreign", "if",
1099
+            "import", "in", "map", "matrix", "nil", "not_in", "offset_of",
1100
+            "or_else", "or_return", "package", "proc", "return", "size_of",
1101
+            "struct", "switch", "transmute", "true", "type_of", "typeid",
1102
+            "union", "using", "when", "where",
1103
+        ].into_iter().collect(),
1104
+        types: [
1105
+            "bool", "b8", "b16", "b32", "b64", "int", "i8", "i16", "i32", "i64",
1106
+            "i128", "uint", "u8", "u16", "u32", "u64", "u128", "uintptr",
1107
+            "f16", "f32", "f64", "complex32", "complex64", "complex128",
1108
+            "quaternion64", "quaternion128", "quaternion256", "string", "cstring",
1109
+            "rune", "rawptr", "typeid", "any",
1110
+        ].into_iter().collect(),
1111
+        line_comment: Some("//"),
1112
+        block_comment_start: Some("/*"),
1113
+        block_comment_end: Some("*/"),
1114
+        string_delimiters: vec!['"', '\'', '`'],
1115
+        multiline_strings: true,
1116
+        operators: C_OPERATORS.to_vec(),
1117
+        punctuation: C_PUNCTUATION.to_vec(),
1118
+        has_preprocessor: false,
1119
+        case_sensitive: true,
1120
+    }
1121
+}
1122
+
1123
+fn v_def() -> LanguageDef {
1124
+    LanguageDef {
1125
+        name: "V",
1126
+        keywords: [
1127
+            "as", "asm", "assert", "atomic", "break", "const", "continue",
1128
+            "defer", "else", "enum", "false", "fn", "for", "go", "goto", "if",
1129
+            "import", "in", "interface", "is", "isreftype", "lock", "match",
1130
+            "module", "mut", "none", "or", "pub", "return", "rlock", "select",
1131
+            "shared", "sizeof", "spawn", "static", "struct", "true", "type",
1132
+            "typeof", "union", "unsafe", "volatile", "__offsetof",
1133
+        ].into_iter().collect(),
1134
+        types: [
1135
+            "bool", "string", "i8", "i16", "int", "i64", "i128", "u8", "u16",
1136
+            "u32", "u64", "u128", "rune", "f32", "f64", "isize", "usize",
1137
+            "voidptr", "any", "thread",
1138
+        ].into_iter().collect(),
1139
+        line_comment: Some("//"),
1140
+        block_comment_start: Some("/*"),
1141
+        block_comment_end: Some("*/"),
1142
+        string_delimiters: vec!['"', '\'', '`'],
1143
+        multiline_strings: true,
1144
+        operators: C_OPERATORS.to_vec(),
1145
+        punctuation: C_PUNCTUATION.to_vec(),
1146
+        has_preprocessor: false,
1147
+        case_sensitive: true,
1148
+    }
1149
+}
1150
+
1151
+fn d_def() -> LanguageDef {
1152
+    LanguageDef {
1153
+        name: "D",
1154
+        keywords: [
1155
+            "abstract", "alias", "align", "asm", "assert", "auto", "body",
1156
+            "bool", "break", "case", "cast", "catch", "class", "const",
1157
+            "continue", "debug", "default", "delegate", "delete", "deprecated",
1158
+            "do", "else", "enum", "export", "extern", "false", "final",
1159
+            "finally", "for", "foreach", "foreach_reverse", "function", "goto",
1160
+            "if", "immutable", "import", "in", "inout", "interface", "invariant",
1161
+            "is", "lazy", "mixin", "module", "new", "nothrow", "null", "out",
1162
+            "override", "package", "pragma", "private", "protected", "public",
1163
+            "pure", "ref", "return", "scope", "shared", "static", "struct",
1164
+            "super", "switch", "synchronized", "template", "this", "throw",
1165
+            "true", "try", "typeid", "typeof", "union", "unittest", "version",
1166
+            "while", "with", "__FILE__", "__LINE__", "__gshared", "__traits",
1167
+        ].into_iter().collect(),
1168
+        types: [
1169
+            "void", "bool", "byte", "ubyte", "short", "ushort", "int", "uint",
1170
+            "long", "ulong", "cent", "ucent", "float", "double", "real",
1171
+            "ifloat", "idouble", "ireal", "cfloat", "cdouble", "creal", "char",
1172
+            "wchar", "dchar", "string", "wstring", "dstring", "size_t", "ptrdiff_t",
1173
+        ].into_iter().collect(),
1174
+        line_comment: Some("//"),
1175
+        block_comment_start: Some("/*"),
1176
+        block_comment_end: Some("*/"),
1177
+        string_delimiters: vec!['"', '\'', '`'],
1178
+        multiline_strings: true,
1179
+        operators: C_OPERATORS.to_vec(),
1180
+        punctuation: C_PUNCTUATION.to_vec(),
1181
+        has_preprocessor: false,
1182
+        case_sensitive: true,
1183
+    }
1184
+}
1185
+
1186
+fn bash_def() -> LanguageDef {
1187
+    LanguageDef {
1188
+        name: "Bash",
1189
+        keywords: [
1190
+            "if", "then", "else", "elif", "fi", "case", "esac", "for", "while",
1191
+            "until", "do", "done", "in", "function", "select", "time", "coproc",
1192
+            "break", "continue", "return", "exit", "export", "readonly", "local",
1193
+            "declare", "typeset", "unset", "shift", "source", "alias", "unalias",
1194
+            "set", "shopt", "trap", "eval", "exec", "true", "false",
1195
+        ].into_iter().collect(),
1196
+        types: HashSet::new(),
1197
+        line_comment: Some("#"),
1198
+        block_comment_start: None,
1199
+        block_comment_end: None,
1200
+        string_delimiters: vec!['"', '\''],
1201
+        multiline_strings: false,
1202
+        operators: vec![
1203
+            "||", "&&", ";;", ";&", ";;&", "|&", "<<", ">>", "<&", ">&", "<>",
1204
+            "==", "!=", "<=", ">=", "-eq", "-ne", "-lt", "-le", "-gt", "-ge",
1205
+            "-z", "-n", "-e", "-f", "-d", "-r", "-w", "-x",
1206
+            "|", "&", ";", "<", ">", "=",
1207
+        ],
1208
+        punctuation: vec!['{', '}', '(', ')', '[', ']', '$', '`'],
1209
+        has_preprocessor: false,
1210
+        case_sensitive: true,
1211
+    }
1212
+}
1213
+
1214
+fn fish_def() -> LanguageDef {
1215
+    LanguageDef {
1216
+        name: "Fish",
1217
+        keywords: [
1218
+            "and", "begin", "break", "builtin", "case", "command", "continue",
1219
+            "else", "end", "exec", "for", "function", "if", "in", "not", "or",
1220
+            "return", "set", "status", "switch", "test", "while",
1221
+        ].into_iter().collect(),
1222
+        types: HashSet::new(),
1223
+        line_comment: Some("#"),
1224
+        block_comment_start: None,
1225
+        block_comment_end: None,
1226
+        string_delimiters: vec!['"', '\''],
1227
+        multiline_strings: false,
1228
+        operators: vec![
1229
+            "||", "&&", ";", "|", "&", "<", ">", "=",
1230
+        ],
1231
+        punctuation: vec!['{', '}', '(', ')', '[', ']', '$'],
1232
+        has_preprocessor: false,
1233
+        case_sensitive: true,
1234
+    }
1235
+}
1236
+
1237
+fn zsh_def() -> LanguageDef {
1238
+    let mut def = bash_def();
1239
+    def.name = "Zsh";
1240
+    def.keywords.extend([
1241
+        "autoload", "bindkey", "compdef", "compadd", "compinit", "emulate",
1242
+        "noglob", "zle", "zmodload", "zstyle",
1243
+    ]);
1244
+    def
1245
+}
1246
+
1247
+fn powershell_def() -> LanguageDef {
1248
+    LanguageDef {
1249
+        name: "PowerShell",
1250
+        keywords: [
1251
+            "begin", "break", "catch", "class", "continue", "data", "define",
1252
+            "do", "dynamicparam", "else", "elseif", "end", "enum", "exit",
1253
+            "filter", "finally", "for", "foreach", "from", "function", "hidden",
1254
+            "if", "in", "inlinescript", "param", "process", "return", "static",
1255
+            "switch", "throw", "trap", "try", "until", "using", "var", "while",
1256
+            "workflow", "parallel", "sequence",
1257
+        ].into_iter().collect(),
1258
+        types: [
1259
+            "bool", "byte", "char", "datetime", "decimal", "double", "float",
1260
+            "int", "long", "object", "sbyte", "short", "single", "string",
1261
+            "uint", "ulong", "ushort", "void", "array", "hashtable", "xml",
1262
+        ].into_iter().collect(),
1263
+        line_comment: Some("#"),
1264
+        block_comment_start: Some("<#"),
1265
+        block_comment_end: Some("#>"),
1266
+        string_delimiters: vec!['"', '\''],
1267
+        multiline_strings: true,
1268
+        operators: vec![
1269
+            "-eq", "-ne", "-gt", "-ge", "-lt", "-le", "-like", "-notlike",
1270
+            "-match", "-notmatch", "-contains", "-notcontains", "-in", "-notin",
1271
+            "-replace", "-split", "-join", "-and", "-or", "-xor", "-not", "-band",
1272
+            "-bor", "-bxor", "-bnot", "-shl", "-shr",
1273
+            "=", "+=", "-=", "*=", "/=", "%=", "++", "--",
1274
+            "|", "&", ";", "<", ">",
1275
+        ],
1276
+        punctuation: vec!['{', '}', '(', ')', '[', ']', '$', '@', ',', '.'],
1277
+        has_preprocessor: false,
1278
+        case_sensitive: false,
1279
+    }
1280
+}
1281
+
1282
+fn sql_def() -> LanguageDef {
1283
+    LanguageDef {
1284
+        name: "SQL",
1285
+        keywords: [
1286
+            "add", "all", "alter", "and", "any", "as", "asc", "backup", "between",
1287
+            "by", "case", "check", "column", "constraint", "create", "database",
1288
+            "default", "delete", "desc", "distinct", "drop", "exec", "exists",
1289
+            "foreign", "from", "full", "group", "having", "in", "index", "inner",
1290
+            "insert", "into", "is", "join", "key", "left", "like", "limit",
1291
+            "not", "null", "on", "or", "order", "outer", "primary", "procedure",
1292
+            "right", "rownum", "select", "set", "table", "top", "truncate",
1293
+            "union", "unique", "update", "values", "view", "where", "with",
1294
+            "true", "false", "begin", "end", "declare", "if", "else", "while",
1295
+            "return", "commit", "rollback", "transaction",
1296
+        ].into_iter().collect(),
1297
+        types: [
1298
+            "int", "integer", "smallint", "bigint", "decimal", "numeric", "float",
1299
+            "real", "double", "precision", "char", "varchar", "text", "nchar",
1300
+            "nvarchar", "ntext", "binary", "varbinary", "image", "date", "time",
1301
+            "datetime", "timestamp", "boolean", "bool", "bit", "money", "xml",
1302
+            "json", "uuid", "serial", "bytea", "array",
1303
+        ].into_iter().collect(),
1304
+        line_comment: Some("--"),
1305
+        block_comment_start: Some("/*"),
1306
+        block_comment_end: Some("*/"),
1307
+        string_delimiters: vec!['\''],
1308
+        multiline_strings: false,
1309
+        operators: vec![
1310
+            "<=", ">=", "<>", "!=", "||", "::", "->", "->>",
1311
+            "+", "-", "*", "/", "%", "&", "|", "^", "~", "<", ">", "=",
1312
+        ],
1313
+        punctuation: vec!['(', ')', ',', '.', ';', ':'],
1314
+        has_preprocessor: false,
1315
+        case_sensitive: false,
1316
+    }
1317
+}
1318
+
1319
+fn html_def() -> LanguageDef {
1320
+    LanguageDef {
1321
+        name: "HTML",
1322
+        keywords: HashSet::new(),
1323
+        types: HashSet::new(),
1324
+        line_comment: None,
1325
+        block_comment_start: Some("<!--"),
1326
+        block_comment_end: Some("-->"),
1327
+        string_delimiters: vec!['"', '\''],
1328
+        multiline_strings: false,
1329
+        operators: vec!["="],
1330
+        punctuation: vec!['<', '>', '/', '!'],
1331
+        has_preprocessor: false,
1332
+        case_sensitive: false,
1333
+    }
1334
+}
1335
+
1336
+fn css_def() -> LanguageDef {
1337
+    LanguageDef {
1338
+        name: "CSS",
1339
+        keywords: [
1340
+            "!important", "@charset", "@font-face", "@import", "@keyframes",
1341
+            "@media", "@namespace", "@page", "@supports", "@viewport",
1342
+        ].into_iter().collect(),
1343
+        types: HashSet::new(),
1344
+        line_comment: None,
1345
+        block_comment_start: Some("/*"),
1346
+        block_comment_end: Some("*/"),
1347
+        string_delimiters: vec!['"', '\''],
1348
+        multiline_strings: false,
1349
+        operators: vec![":", ";", ",", "+", ">", "~", "*"],
1350
+        punctuation: vec!['{', '}', '(', ')', '[', ']', '.', '#'],
1351
+        has_preprocessor: false,
1352
+        case_sensitive: false,
1353
+    }
1354
+}
1355
+
1356
+fn json_def() -> LanguageDef {
1357
+    LanguageDef {
1358
+        name: "JSON",
1359
+        keywords: ["true", "false", "null"].into_iter().collect(),
1360
+        types: HashSet::new(),
1361
+        line_comment: None,
1362
+        block_comment_start: None,
1363
+        block_comment_end: None,
1364
+        string_delimiters: vec!['"'],
1365
+        multiline_strings: false,
1366
+        operators: vec![":"],
1367
+        punctuation: vec!['{', '}', '[', ']', ','],
1368
+        has_preprocessor: false,
1369
+        case_sensitive: true,
1370
+    }
1371
+}
1372
+
1373
+fn yaml_def() -> LanguageDef {
1374
+    LanguageDef {
1375
+        name: "YAML",
1376
+        keywords: [
1377
+            "true", "false", "null", "yes", "no", "on", "off", "~",
1378
+        ].into_iter().collect(),
1379
+        types: HashSet::new(),
1380
+        line_comment: Some("#"),
1381
+        block_comment_start: None,
1382
+        block_comment_end: None,
1383
+        string_delimiters: vec!['"', '\''],
1384
+        multiline_strings: true,
1385
+        operators: vec![":", "-", ">", "|", "&", "*", "!"],
1386
+        punctuation: vec!['{', '}', '[', ']', ','],
1387
+        has_preprocessor: false,
1388
+        case_sensitive: true,
1389
+    }
1390
+}
1391
+
1392
+fn toml_def() -> LanguageDef {
1393
+    LanguageDef {
1394
+        name: "TOML",
1395
+        keywords: ["true", "false"].into_iter().collect(),
1396
+        types: HashSet::new(),
1397
+        line_comment: Some("#"),
1398
+        block_comment_start: None,
1399
+        block_comment_end: None,
1400
+        string_delimiters: vec!['"', '\''],
1401
+        multiline_strings: true,
1402
+        operators: vec!["="],
1403
+        punctuation: vec!['{', '}', '[', ']', ',', '.'],
1404
+        has_preprocessor: false,
1405
+        case_sensitive: true,
1406
+    }
1407
+}
1408
+
1409
+fn xml_def() -> LanguageDef {
1410
+    LanguageDef {
1411
+        name: "XML",
1412
+        keywords: HashSet::new(),
1413
+        types: HashSet::new(),
1414
+        line_comment: None,
1415
+        block_comment_start: Some("<!--"),
1416
+        block_comment_end: Some("-->"),
1417
+        string_delimiters: vec!['"', '\''],
1418
+        multiline_strings: false,
1419
+        operators: vec!["="],
1420
+        punctuation: vec!['<', '>', '/', '?', '!'],
1421
+        has_preprocessor: true, // <?xml ... ?>
1422
+        case_sensitive: true,
1423
+    }
1424
+}
1425
+
1426
+fn markdown_def() -> LanguageDef {
1427
+    LanguageDef {
1428
+        name: "Markdown",
1429
+        keywords: HashSet::new(),
1430
+        types: HashSet::new(),
1431
+        line_comment: None,
1432
+        block_comment_start: None,
1433
+        block_comment_end: None,
1434
+        string_delimiters: vec![],
1435
+        multiline_strings: false,
1436
+        operators: vec!["#", "*", "-", "+", ">", "`", "~", "_", "[", "]", "(", ")"],
1437
+        punctuation: vec![],
1438
+        has_preprocessor: false,
1439
+        case_sensitive: true,
1440
+    }
1441
+}
1442
+
1443
+fn makefile_def() -> LanguageDef {
1444
+    LanguageDef {
1445
+        name: "Makefile",
1446
+        keywords: [
1447
+            "ifeq", "ifneq", "ifdef", "ifndef", "else", "endif", "define",
1448
+            "endef", "export", "unexport", "override", "include", "-include",
1449
+            "sinclude", "vpath", ".PHONY", ".SUFFIXES", ".DEFAULT", ".PRECIOUS",
1450
+            ".INTERMEDIATE", ".SECONDARY", ".SECONDEXPANSION", ".DELETE_ON_ERROR",
1451
+            ".IGNORE", ".LOW_RESOLUTION_TIME", ".SILENT", ".EXPORT_ALL_VARIABLES",
1452
+            ".NOTPARALLEL", ".ONESHELL", ".POSIX",
1453
+        ].into_iter().collect(),
1454
+        types: HashSet::new(),
1455
+        line_comment: Some("#"),
1456
+        block_comment_start: None,
1457
+        block_comment_end: None,
1458
+        string_delimiters: vec!['"', '\''],
1459
+        multiline_strings: false,
1460
+        operators: vec!["=", ":=", "?=", "+=", "::=", "!=", ":", "|", ";", "@", "-"],
1461
+        punctuation: vec!['$', '(', ')', '{', '}', '%', '*', '?', '<', '>'],
1462
+        has_preprocessor: false,
1463
+        case_sensitive: true,
1464
+    }
1465
+}
1466
+
1467
+fn dockerfile_def() -> LanguageDef {
1468
+    LanguageDef {
1469
+        name: "Dockerfile",
1470
+        keywords: [
1471
+            "ADD", "ARG", "CMD", "COPY", "ENTRYPOINT", "ENV", "EXPOSE", "FROM",
1472
+            "HEALTHCHECK", "LABEL", "MAINTAINER", "ONBUILD", "RUN", "SHELL",
1473
+            "STOPSIGNAL", "USER", "VOLUME", "WORKDIR", "AS",
1474
+        ].into_iter().collect(),
1475
+        types: HashSet::new(),
1476
+        line_comment: Some("#"),
1477
+        block_comment_start: None,
1478
+        block_comment_end: None,
1479
+        string_delimiters: vec!['"', '\''],
1480
+        multiline_strings: false,
1481
+        operators: vec!["=", "\\"],
1482
+        punctuation: vec!['[', ']', '{', '}', '$'],
1483
+        has_preprocessor: false,
1484
+        case_sensitive: false,
1485
+    }
1486
+}
1487
+
1488
+fn terraform_def() -> LanguageDef {
1489
+    LanguageDef {
1490
+        name: "Terraform",
1491
+        keywords: [
1492
+            "data", "locals", "module", "output", "provider", "resource",
1493
+            "terraform", "variable", "for", "for_each", "if", "in", "dynamic",
1494
+            "content", "count", "depends_on", "lifecycle", "provisioner",
1495
+            "connection", "null_resource", "true", "false", "null",
1496
+        ].into_iter().collect(),
1497
+        types: [
1498
+            "string", "number", "bool", "list", "map", "set", "object", "tuple",
1499
+            "any",
1500
+        ].into_iter().collect(),
1501
+        line_comment: Some("#"),
1502
+        block_comment_start: Some("/*"),
1503
+        block_comment_end: Some("*/"),
1504
+        string_delimiters: vec!['"'],
1505
+        multiline_strings: true,
1506
+        operators: vec!["=", "=>", "==", "!=", "<=", ">=", "&&", "||", "!", "?", ":"],
1507
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ',', '.'],
1508
+        has_preprocessor: false,
1509
+        case_sensitive: true,
1510
+    }
1511
+}
1512
+
1513
+fn nix_def() -> LanguageDef {
1514
+    LanguageDef {
1515
+        name: "Nix",
1516
+        keywords: [
1517
+            "assert", "else", "if", "import", "in", "inherit", "let", "or",
1518
+            "rec", "then", "with", "true", "false", "null",
1519
+        ].into_iter().collect(),
1520
+        types: HashSet::new(),
1521
+        line_comment: Some("#"),
1522
+        block_comment_start: Some("/*"),
1523
+        block_comment_end: Some("*/"),
1524
+        string_delimiters: vec!['"'],
1525
+        multiline_strings: true,
1526
+        operators: vec!["=", "++", "//", "->", ":", "?", "@", "==", "!=", "<=", ">=", "&&", "||", "!"],
1527
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ',', '.', ';'],
1528
+        has_preprocessor: false,
1529
+        case_sensitive: true,
1530
+    }
1531
+}
1532
+
1533
+fn ocaml_def() -> LanguageDef {
1534
+    LanguageDef {
1535
+        name: "OCaml",
1536
+        keywords: [
1537
+            "and", "as", "assert", "asr", "begin", "class", "constraint", "do",
1538
+            "done", "downto", "else", "end", "exception", "external", "false",
1539
+            "for", "fun", "function", "functor", "if", "in", "include",
1540
+            "inherit", "initializer", "land", "lazy", "let", "lor", "lsl",
1541
+            "lsr", "lxor", "match", "method", "mod", "module", "mutable", "new",
1542
+            "nonrec", "object", "of", "open", "or", "private", "rec", "sig",
1543
+            "struct", "then", "to", "true", "try", "type", "val", "virtual",
1544
+            "when", "while", "with",
1545
+        ].into_iter().collect(),
1546
+        types: [
1547
+            "int", "float", "bool", "char", "string", "bytes", "unit", "exn",
1548
+            "array", "list", "option", "ref", "lazy_t",
1549
+        ].into_iter().collect(),
1550
+        line_comment: None,
1551
+        block_comment_start: Some("(*"),
1552
+        block_comment_end: Some("*)"),
1553
+        string_delimiters: vec!['"'],
1554
+        multiline_strings: false,
1555
+        operators: vec![
1556
+            "->", "<-", "|>", "@@", "::", "@", "^", "||", "&&", "==", "!=",
1557
+            "<=", ">=", "<>", ":=", "++", "--",
1558
+            "+", "-", "*", "/", "~", "!", "<", ">", "|", "&", "=",
1559
+        ],
1560
+        punctuation: vec!['{', '}', '(', ')', '[', ']', ';', ',', '.', ':'],
1561
+        has_preprocessor: false,
1562
+        case_sensitive: true,
1563
+    }
1564
+}
1565
+
1566
+fn fsharp_def() -> LanguageDef {
1567
+    let mut def = ocaml_def();
1568
+    def.name = "F#";
1569
+    def.keywords.extend([
1570
+        "abstract", "base", "default", "delegate", "elif", "elif", "fixed",
1571
+        "global", "inline", "interface", "internal", "member", "namespace",
1572
+        "null", "override", "public", "return", "static", "upcast", "use",
1573
+        "void", "yield",
1574
+    ]);
1575
+    def.line_comment = Some("//");
1576
+    def
1577
+}
1578
+
1579
+fn dart_def() -> LanguageDef {
1580
+    LanguageDef {
1581
+        name: "Dart",
1582
+        keywords: [
1583
+            "abstract", "as", "assert", "async", "await", "break", "case",
1584
+            "catch", "class", "const", "continue", "covariant", "default",
1585
+            "deferred", "do", "dynamic", "else", "enum", "export", "extends",
1586
+            "extension", "external", "factory", "false", "final", "finally",
1587
+            "for", "Function", "get", "hide", "if", "implements", "import",
1588
+            "in", "interface", "is", "late", "library", "mixin", "new", "null",
1589
+            "on", "operator", "part", "required", "rethrow", "return", "set",
1590
+            "show", "static", "super", "switch", "sync", "this", "throw",
1591
+            "true", "try", "typedef", "var", "void", "while", "with", "yield",
1592
+        ].into_iter().collect(),
1593
+        types: [
1594
+            "bool", "double", "dynamic", "int", "List", "Map", "Never", "Null",
1595
+            "num", "Object", "Set", "String", "Symbol", "Type", "void",
1596
+            "Future", "Stream", "Iterable", "Iterator",
1597
+        ].into_iter().collect(),
1598
+        line_comment: Some("//"),
1599
+        block_comment_start: Some("/*"),
1600
+        block_comment_end: Some("*/"),
1601
+        string_delimiters: vec!['"', '\''],
1602
+        multiline_strings: true,
1603
+        operators: C_OPERATORS.to_vec(),
1604
+        punctuation: C_PUNCTUATION.to_vec(),
1605
+        has_preprocessor: false,
1606
+        case_sensitive: true,
1607
+    }
1608
+}
1609
+
1610
+fn groovy_def() -> LanguageDef {
1611
+    let mut def = java_def();
1612
+    def.name = "Groovy";
1613
+    def.keywords.extend([
1614
+        "as", "def", "in", "trait",
1615
+    ]);
1616
+    def.multiline_strings = true;
1617
+    def
1618
+}
src/syntax/mod.rsadded
@@ -0,0 +1,6 @@
1
+//! Syntax highlighting module
2
+
3
+mod highlight;
4
+mod languages;
5
+
6
+pub use highlight::{Highlighter, HighlightState, Token};