1package n
2
3import (
4 "strings"
5
6 . "github.com/alecthomas/chroma" // nolint
7 "github.com/alecthomas/chroma/lexers/internal"
8)
9
10// nixb matches right boundary of a nix word. Use it instead of \b.
11const nixb = `(?![a-zA-Z0-9_'-])`
12
13// Nix lexer.
14var Nix = internal.Register(MustNewLexer(
15 &Config{
16 Name: "Nix",
17 Aliases: []string{"nixos", "nix"},
18 Filenames: []string{"*.nix"},
19 MimeTypes: []string{"text/x-nix"},
20 },
21 Rules{
22 "root": {
23 Include("keywords"),
24 Include("builtins"),
25 // "./path" and ".float" literals have to be above "." operator
26 Include("literals"),
27 Include("operators"),
28 {`#.*$`, CommentSingle, nil},
29 {`/\*`, CommentMultiline, Push("comment")},
30 {`\(`, Punctuation, Push("paren")},
31 {`\[`, Punctuation, Push("list")},
32 {`"`, StringDouble, Push("qstring")},
33 {`''`, StringSingle, Push("istring")},
34 {`{`, Punctuation, Push("scope")},
35 {`let` + nixb, Keyword, Push("scope")},
36 Include("id"),
37 Include("space"),
38 },
39 "keywords": {
40 {`import` + nixb, KeywordNamespace, nil},
41 {Words(``, nixb, strings.Fields("rec inherit with if then else assert")...), Keyword, nil},
42 },
43 "builtins": {
44 {`throw` + nixb, NameException, nil},
45 {Words(``, nixb, strings.Fields("abort baseNameOf builtins currentTime dependencyClosure derivation dirOf fetchTarball filterSource getAttr getEnv hasAttr isNull map removeAttrs toString toXML")...), NameBuiltin, nil},
46 },
47 "literals": {
48 {Words(``, nixb, strings.Fields("true false null")...), NameConstant, nil},
49 Include("uri"),
50 Include("path"),
51 Include("int"),
52 Include("float"),
53 },
54 "operators": {
55 {` [/-] `, Operator, nil},
56 {`(\.)(\${)`, ByGroups(Operator, StringInterpol), Push("interpol")},
57 {`(\?)(\s*)(\${)`, ByGroups(Operator, Text, StringInterpol), Push("interpol")},
58 {Words(``, ``, strings.Fields("@ . ? ++ + != ! // == && || -> <= < >= > *")...), Operator, nil},
59 {`[;:]`, Punctuation, nil},
60 },
61 "comment": {
62 {`\*/`, CommentMultiline, Pop(1)},
63 {`.|\n`, CommentMultiline, nil},
64 },
65 "paren": {
66 {`\)`, Punctuation, Pop(1)},
67 Include("root"),
68 },
69 "list": {
70 {`\]`, Punctuation, Pop(1)},
71 Include("root"),
72 },
73 "qstring": {
74 {`"`, StringDouble, Pop(1)},
75 {`\${`, StringInterpol, Push("interpol")},
76 {`\\.`, StringEscape, nil},
77 {`.|\n`, StringDouble, nil},
78 },
79 "istring": {
80 {`''\$`, StringEscape, nil}, // "$"
81 {`'''`, StringEscape, nil}, // "''"
82 {`''\\.`, StringEscape, nil}, // "\."
83 {`''`, StringSingle, Pop(1)},
84 {`\${`, StringInterpol, Push("interpol")},
85 // The next rule is important: "$" escapes any symbol except "{"!
86 {`\$.`, StringSingle, nil}, // "$."
87 {`.|\n`, StringSingle, nil},
88 },
89 "scope": {
90 {`}:`, Punctuation, Pop(1)},
91 {`}`, Punctuation, Pop(1)},
92 {`in` + nixb, Keyword, Pop(1)},
93 {`\${`, StringInterpol, Push("interpol")},
94 Include("root"), // "==" has to be above "="
95 {Words(``, ``, strings.Fields("= ? ,")...), Operator, nil},
96 },
97 "interpol": {
98 {`}`, StringInterpol, Pop(1)},
99 Include("root"),
100 },
101 "id": {
102 {`[a-zA-Z_][a-zA-Z0-9_'-]*`, Name, nil},
103 },
104 "uri": {
105 {`[a-zA-Z][a-zA-Z0-9+.-]*:[a-zA-Z0-9%/?:@&=+$,_.!~*'-]+`, StringDoc, nil},
106 },
107 "path": {
108 {`[a-zA-Z0-9._+-]*(/[a-zA-Z0-9._+-]+)+`, StringRegex, nil},
109 {`~(/[a-zA-Z0-9._+-]+)+/?`, StringRegex, nil},
110 {`<[a-zA-Z0-9._+-]+(/[a-zA-Z0-9._+-]+)*>`, StringRegex, nil},
111 },
112 "int": {
113 {`-?[0-9]+` + nixb, NumberInteger, nil},
114 },
115 "float": {
116 {`-?(([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?` + nixb, NumberFloat, nil},
117 },
118 "space": {
119 {`[ \t\r\n]+`, Text, nil},
120 },
121 },
122))