1 /* Title: Tools/VSCode/src/grammar.scala
4 Generate static TextMate grammar for VSCode editor.
7 package isabelle.vscode
17 /* generate grammar */
19 private lazy val default_logic = Isabelle_System.getenv("ISABELLE_LOGIC")
21 private def default_output(logic: String = ""): String =
22 if (logic == "" || logic == default_logic) "isabelle-grammar.json"
23 else "isabelle-" + logic + "-grammar.json"
25 def generate(keywords: Keyword.Keywords): JSON.S =
27 val (minor_keywords, operators) =
28 keywords.minor.iterator.toList.partition(Symbol.is_ascii_identifier(_))
30 def major_keywords(pred: String => Boolean): List[String] =
32 k <- keywords.major.iterator
33 kind <- keywords.kinds.get(k)
38 major_keywords(k => k != Keyword.THY_END && k != Keyword.PRF_ASM && k != Keyword.PRF_ASM_GOAL)
39 val keywords2 = minor_keywords ::: major_keywords(Set(Keyword.THY_END))
40 val keywords3 = major_keywords(Set(Keyword.PRF_ASM, Keyword.PRF_ASM_GOAL))
43 def quote_name(a: String): String =
44 if (Symbol.is_ascii_identifier(a)) a else "\\Q" + a + "\\E"
46 def grouped_names(as: List[String]): String =
47 JSON.Format("\\b(" + as.sorted.map(quote_name(_)).mkString("|") + ")\\b")
51 "scopeName": "source.isabelle",
53 "uuid": """ + JSON.Format(UUID.randomUUID().toString) + """,
58 "name": "comment.block.isabelle",
61 "0": { "name": "punctuation.definition.comment.begin.isabelle" }
63 "patterns": [{ "include": "#comment" }],
66 "0": { "name": "punctuation.definition.comment.end.isabelle" }
74 "name": "string.quoted.other.multiline.isabelle",
75 "begin": "(?:\\\\<open>|‹)",
77 "0": { "name": "punctuation.definition.string.begin.isabelle" }
79 "patterns": [{ "include": "#cartouche" }],
80 "end": "(?:\\\\<close>|›)",
82 "0": { "name": "punctuation.definition.string.end.isabelle" }
93 "include": "#cartouche"
96 "name": "keyword.control.isabelle",
97 "match": """ + grouped_names(keywords1) + """
100 "name": "keyword.other.isabelle",
101 "match": """ + grouped_names(keywords2) + """
104 "name": "keyword.operator.isabelle",
105 "match": """ + grouped_names(operators) + """
108 "name": "constant.language.isabelle",
109 "match": """ + grouped_names(keywords3) + """
112 "match": "\\b\\d*\\.?\\d+\\b",
113 "name": "constant.numeric.isabelle"
116 "name": "string.quoted.double.isabelle",
119 "0": { "name": "punctuation.definition.string.begin.isabelle" }
123 "match": """ + JSON.Format("""\\[\"]|\\\d\d\d""") + """,
124 "name": "constant.character.escape.isabelle"
129 "0": { "name": "punctuation.definition.string.end.isabelle" }
133 "name": "string.quoted.backtick.isabelle",
136 "0": { "name": "punctuation.definition.string.begin.isabelle" }
140 "match": """ + JSON.Format("""\\[\`]|\\\d\d\d""") + """,
141 "name": "constant.character.escape.isabelle"
146 "0": { "name": "punctuation.definition.string.end.isabelle" }
155 /* Isabelle tool wrapper */
157 val isabelle_tool = Isabelle_Tool("vscode_grammar",
158 "generate static TextMate grammar for VSCode editor", args =>
160 var dirs: List[Path] = Nil
161 var logic = default_logic
162 var output: Option[Path] = None
164 val getopts = Getopts("""
165 Usage: isabelle vscode_grammar [OPTIONS]
168 -d DIR include session directory
169 -l NAME logic session name (default ISABELLE_LOGIC=""" + quote(default_logic) + """)
170 -o FILE output file name (default """ + default_output() + """)
172 Generate static TextMate grammar for VSCode editor.
174 "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))),
175 "l:" -> (arg => logic = arg),
176 "o:" -> (arg => output = Some(Path.explode(arg))))
178 val more_args = getopts(args)
179 if (more_args.nonEmpty) getopts.usage()
181 val keywords = Build.outer_syntax(Options.init(), dirs, logic).keywords
182 val output_path = output getOrElse Path.explode(default_output(logic))
184 Output.writeln(output_path.implode)
185 File.write_backup(output_path, generate(keywords))