1package lexers
 2
 3import (
 4	. "github.com/alecthomas/chroma/v2" // nolint
 5)
 6
 7// Org mode lexer.
 8var Org = Register(MustNewLexer(
 9	&Config{
10		Name:      "Org Mode",
11		Aliases:   []string{"org", "orgmode"},
12		Filenames: []string{"*.org"},
13		MimeTypes: []string{"text/org"}, // https://lists.gnu.org/r/emacs-orgmode/2017-09/msg00087.html
14	},
15	orgRules,
16))
17
18func orgRules() Rules {
19	return Rules{
20		"root": {
21			{`^# .*$`, Comment, nil},
22			// Headings
23			{`^(\*)( COMMENT)( .*)$`, ByGroups(GenericHeading, NameEntity, GenericStrong), nil},
24			{`^(\*\*+)( COMMENT)( .*)$`, ByGroups(GenericSubheading, NameEntity, Text), nil},
25			{`^(\*)( DONE)( .*)$`, ByGroups(GenericHeading, LiteralStringRegex, GenericStrong), nil},
26			{`^(\*\*+)( DONE)( .*)$`, ByGroups(GenericSubheading, LiteralStringRegex, Text), nil},
27			{`^(\*)( TODO)( .*)$`, ByGroups(GenericHeading, Error, GenericStrong), nil},
28			{`^(\*\*+)( TODO)( .*)$`, ByGroups(GenericSubheading, Error, Text), nil},
29			{`^(\*)( .+?)( :[a-zA-Z0-9_@:]+:)$`, ByGroups(GenericHeading, GenericStrong, GenericEmph), nil}, // Level 1 heading with tags
30			{`^(\*)( .+)$`, ByGroups(GenericHeading, GenericStrong), nil},                                   // // Level 1 heading with NO tags
31			{`^(\*\*+)( .+?)( :[a-zA-Z0-9_@:]+:)$`, ByGroups(GenericSubheading, Text, GenericEmph), nil},    // Level 2+ heading with tags
32			{`^(\*\*+)( .+)$`, ByGroups(GenericSubheading, Text), nil},                                      // Level 2+ heading with NO tags
33			// Checkbox lists
34			{`^( *)([+-] )(\[[ X]\])( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
35			{`^( +)(\* )(\[[ X]\])( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
36			// Definition lists
37			{`^( *)([+-] )([^ \n]+ ::)( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
38			{`^( +)(\* )([^ \n]+ ::)( .+)$`, ByGroups(Text, Keyword, Keyword, UsingSelf("inline")), nil},
39			// Unordered lists
40			{`^( *)([+-] )(.+)$`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
41			{`^( +)(\* )(.+)$`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
42			// Ordered lists
43			{`^( *)([0-9]+[.)])( \[@[0-9]+\])( .+)$`, ByGroups(Text, Keyword, GenericEmph, UsingSelf("inline")), nil},
44			{`^( *)([0-9]+[.)])( .+)$`, ByGroups(Text, Keyword, UsingSelf("inline")), nil},
45			// Dynamic Blocks
46			{`(?i)^( *#\+begin: )([^ ]+)([\w\W]*?\n)([\w\W]*?)(^ *#\+end: *$)`, ByGroups(Comment, CommentSpecial, Comment, UsingSelf("inline"), Comment), nil},
47			// Blocks
48			// - Comment Blocks
49			{`(?i)^( *#\+begin_comment *\n)([\w\W]*?)(^ *#\+end_comment *$)`, ByGroups(Comment, Comment, Comment), nil},
50			// - Src Blocks
51			{
52				`(?i)^( *#\+begin_src )([^ \n]+)(.*?\n)([\w\W]*?)(^ *#\+end_src *$)`,
53				UsingByGroup(2, 4, Comment, CommentSpecial, Comment, Text, Comment),
54				nil,
55			},
56			// - Export Blocks
57			{
58				`(?i)^( *#\+begin_export )(\w+)( *\n)([\w\W]*?)(^ *#\+end_export *$)`,
59				UsingByGroup(2, 4, Comment, CommentSpecial, Text, Text, Comment),
60				nil,
61			},
62			// - Org Special, Example, Verse, etc. Blocks
63			{`(?i)^( *#\+begin_)(\w+)( *\n)([\w\W]*?)(^ *#\+end_\2)( *$)`, ByGroups(Comment, Comment, Text, Text, Comment, Text), nil},
64			// Keywords
65			{`^(#\+\w+)(:.*)$`, ByGroups(CommentSpecial, Comment), nil}, // Other Org keywords like #+title
66			// Properties and Drawers
67			{`(?i)^( *:\w+: *\n)([\w\W]*?)(^ *:end: *$)`, ByGroups(Comment, CommentSpecial, Comment), nil},
68			// Line break operator
69			{`^(.*)(\\\\)$`, ByGroups(UsingSelf("inline"), Operator), nil},
70			// Deadline/Scheduled
71			{`(?i)^( *(?:DEADLINE|SCHEDULED): )(<[^<>]+?> *)$`, ByGroups(Comment, CommentSpecial), nil}, // DEADLINE/SCHEDULED: <datestamp>
72			// DONE state CLOSED
73			{`(?i)^( *CLOSED: )(\[[^][]+?\] *)$`, ByGroups(Comment, CommentSpecial), nil}, // CLOSED: [datestamp]
74			// All other lines
75			Include("inline"),
76		},
77		"inline": {
78			{`(\s)*(\*[^ \n*][^*]+?[^ \n*]\*)((?=\W|\n|$))`, ByGroups(Text, GenericStrong, Text), nil},                          // Bold
79			{`(\s)*(/[^/]+?/)((?=\W|\n|$))`, ByGroups(Text, GenericEmph, Text), nil},                                            // Italic
80			{`(\s)*(=[^\n=]+?=)((?=\W|\n|$))`, ByGroups(Text, NameClass, Text), nil},                                            // Verbatim
81			{`(\s)*(~[^\n~]+?~)((?=\W|\n|$))`, ByGroups(Text, NameClass, Text), nil},                                            // Code
82			{`(\s)*(\+[^+]+?\+)((?=\W|\n|$))`, ByGroups(Text, GenericDeleted, Text), nil},                                       // Strikethrough
83			{`(\s)*(_[^_]+?_)((?=\W|\n|$))`, ByGroups(Text, GenericUnderline, Text), nil},                                       // Underline
84			{`(<)([^<>]+?)(>)`, ByGroups(Text, String, Text), nil},                                                              // <datestamp>
85			{`[{]{3}[^}]+[}]{3}`, NameBuiltin, nil},                                                                             // {{{macro(foo,1)}}}
86			{`([^[])(\[fn:)([^]]+?)(\])([^]])`, ByGroups(Text, NameBuiltinPseudo, LiteralString, NameBuiltinPseudo, Text), nil}, // [fn:1]
87			// Links
88			{`(\[\[)([^][]+?)(\]\[)([^][]+)(\]\])`, ByGroups(Text, NameAttribute, Text, NameTag, Text), nil}, // [[link][descr]]
89			{`(\[\[)([^][]+?)(\]\])`, ByGroups(Text, NameAttribute, Text), nil},                              // [[link]]
90			{`(<<)([^<>]+?)(>>)`, ByGroups(Text, NameAttribute, Text), nil},                                  // <<targetlink>>
91			// Tables
92			{`^( *)(\|[ -].*?[ -]\|)$`, ByGroups(Text, String), nil},
93			// Blank lines, newlines
94			{`\n`, Text, nil},
95			// Any other text
96			{`.`, Text, nil},
97		},
98	}
99}