on master help sign in
Merge remote-tracking branch 'origin/master' into debug
commit4c410eef8720db0c92f39a050c4c5658636013fa authorBlaž Hrastnik <blaz@mxxn.io> dateFri 3 Sep 2021, 11:03 (5 years ago) parent9c646507e11236
18 files changed+671−75
.gitmodules+12 book/src/keymap.md+17−16 docs/vision.md+24 helix-syntax/build.rs+3 helix-syntax/languages/tree-sitter-lua+1 helix-syntax/languages/tree-sitter-ocaml+1 helix-syntax/languages/tree-sitter-yaml+1 helix-term/src/commands.rs+153−59 helix-term/src/keymap.rs+2 languages.toml+33 runtime/queries/lua/highlights.scm+166 runtime/queries/lua/indents.toml+24 runtime/queries/ocaml-interface/highlights.scm+1 runtime/queries/ocaml/highlights.scm+160 runtime/queries/ocaml/indents.toml+13 runtime/queries/ocaml/locals.scm+24 runtime/queries/yaml/highlights.scm+33 runtime/queries/yaml/indents.toml+3
.gitmodules+12
@@ -102,6 +102,18 @@
102102 path = helix-syntax/languages/tree-sitter-protobuf
103103 url = https://github.com/yusdacra/tree-sitter-protobuf.git
104104 shallow = true
105+[submodule "helix-syntax/languages/tree-sitter-ocaml"]
106+ path = helix-syntax/languages/tree-sitter-ocaml
107+ url = https://github.com/tree-sitter/tree-sitter-ocaml
108+ shallow = true
109+[submodule "helix-syntax/languages/tree-sitter-lua"]
110+ path = helix-syntax/languages/tree-sitter-lua
111+ url = https://github.com/nvim-treesitter/tree-sitter-lua
112+ shallow = true
113+[submodule "helix-syntax/languages/tree-sitter-yaml"]
114+ path = helix-syntax/languages/tree-sitter-yaml
115+ url = https://github.com/ikatyang/tree-sitter-yaml
116+ shallow = true
105117[submodule "helix-syntax/languages/tree-sitter-zig"]
106118 path = helix-syntax/languages/tree-sitter-zig
107119 url = https://github.com/maxxnino/tree-sitter-zig
book/src/keymap.md+17−16
@@ -110,27 +110,28 @@ in reverse, or searching via smartcase.
110110| `N` | Add next search match to selection | `extend_search_next` |
111111| `*` | Use current selection as the search pattern | `search_selection` |
112112
113−### Diagnostics
113+### Unimpaired
114114
115−> NOTE: `[` and `]` will likely contain more pair mappings in the style of
116−> [vim-unimpaired](https://github.com/tpope/vim-unimpaired)
115+Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaired)
117116
118−| Key | Description | Command |
119−| ----- | ----------- | ------- |
120−| `[d` | Go to previous diagnostic | `goto_prev_diag` |
121−| `]d` | Go to next diagnostic | `goto_next_diag` |
122−| `[D` | Go to first diagnostic in document | `goto_first_diag` |
123−| `]D` | Go to last diagnostic in document | `goto_last_diag` |
117+| Key | Description | Command |
118+| ----- | ----------- | ------- |
119+| `[d` | Go to previous diagnostic | `goto_prev_diag` |
120+| `]d` | Go to next diagnostic | `goto_next_diag` |
121+| `[D` | Go to first diagnostic in document | `goto_first_diag` |
122+| `]D` | Go to last diagnostic in document | `goto_last_diag` |
123+| `[space` | Add newline above | `add_newline_above` |
124+| `]space` | Add newline below | `add_newline_below` |
124125
125126### Shell
126127
127−| Key | Description | Command |
128−| ------ | ----------- | ------- |
129−| `\|` | Pipe each selection through shell command, replacing with output | `shell_pipe` |
130−| `A-\|` | Pipe each selection into shell command, ignoring output | `shell_pipe_to` |
131−| `!` | Run shell command, inserting output before each selection | `shell_insert_output` |
132−| `A-!` | Run shell command, appending output after each selection | `shell_append_output` |
133−| `$` | Pipe each selection into shell command, removing if the command exits >0 | `shell_keep_pipe` |
128+| Key | Description | Command |
129+| ------ | ----------- | ------- |
130+| `\|` | Pipe each selection through shell command, replacing with output | `shell_pipe` |
131+| `A-\|` | Pipe each selection into shell command, ignoring output | `shell_pipe_to` |
132+| `!` | Run shell command, inserting output before each selection | `shell_insert_output` |
133+| `A-!` | Run shell command, appending output after each selection | `shell_append_output` |
134+| `$` | Pipe each selection into shell command, keep selections where command returned 0 | `shell_keep_pipe` |
134135
135136## Select / extend mode
136137
docs/vision.mdnew file+24
@@ -0,0 +1,24 @@
1+The Helix project still has a ways to go before reaching its goals. This document outlines some of those goals and the overall vision for the project.
2+
3+# Vision
4+
5+An efficient, batteries-included editor you can take anywhere and be productive... if it's your kind of thing.
6+
7+* **Cross-platform.** Whether on Linux, Windows, or OSX, you should be able to take your editor with you.
8+* **Terminal first.** Not all environments have a windowing system, and you shouldn't have to abandon your preferred editor in those cases.
9+* **Native.** No Electron or HTML DOM here. We want an efficient, native-compiled editor that can run with minimal resources when needed. If you're working on a Raspberry Pi, your editor shouldn't consume half of your RAM.
10+* **Batteries included.** Both the default configuration and bundled features should be enough to have a good editing experience and be productive. You shouldn't need a massive custom config or external executables and plugins for basic features and functionality.
11+* **Don't try to be everything for everyone.** There are many great editors out there to choose from. Let's make Helix *one of* those great options, with its own take on things.
12+
13+# Goals
14+
15+Vision statements are all well and good, but are also vague and subjective. Here is a (non-exhaustive) list of some of Helix's more concrete goals, to help give a clearer idea of the project's direction:
16+
17+* **Modal.** Vim is a great idea.
18+* **Selection -> Action**, not Verb -> Object. Interaction models aren't linguistics, and "selection first" lets you see what you're doing (among other benefits).
19+* **We aren't playing code golf.** It's more important for the keymap to be consistent and easy to memorize than it is to save a key stroke or two when editing.
20+* **Built-in tools** for working with code bases efficiently. Most projects aren't a single file, and an editor should handle that as a first-class use case. In Helix's case, this means (among other things) a fuzzy-search file navigator and LSP support.
21+* **Edit anything** that comes up when coding, within reason. Whether it's a 200 MB XML file, a megabyte of minified javascript on a single line, or Japanese text encoded in ShiftJIS, you should be able to open it and edit it without problems. (Note: this doesn't mean handle every esoteric use case. Sometimes you do just need a specialized tool, and Helix isn't that.)
22+* **Configurable**, within reason. Although the defaults should be good, not everyone will agree on what "good" is. Within the bounds of Helix's core interaction models, it should be reasonably configurable so that it can be "good" for more people. This means, for example, custom key maps among other things.
23+* **Extensible**, within reason. Although we want Helix to be productive out-of-the-box, it's not practical or desirable to cram every useful feature and use case into the core editor. The basics should be built-in, but you should be able to extend it with additional functionality as needed. Right now we're thinking Wasm-based plugins.
24+* **Clean code base.** Sometimes other factors (e.g. significant performance gains, important features, correctness, etc.) will trump strict readability, but we nevertheless want to keep the code base straightforward and easy to understand to the extent we can.
helix-syntax/build.rs+3
@@ -177,6 +177,7 @@ fn main() {
177177 let ignore = vec![
178178 "tree-sitter-typescript".to_string(),
179179 "tree-sitter-haskell".to_string(), // aarch64 failures: https://github.com/tree-sitter/tree-sitter-haskell/issues/34
180+ "tree-sitter-ocaml".to_string(),
180181 ];
181182 let dirs = collect_tree_sitter_dirs(&ignore).unwrap();
182183
@@ -202,4 +203,6 @@ fn main() {
202203
203204 build_dir("tree-sitter-typescript/tsx", "tsx");
204205 build_dir("tree-sitter-typescript/typescript", "typescript");
206+ build_dir("tree-sitter-ocaml/ocaml", "ocaml");
207+ build_dir("tree-sitter-ocaml/interface", "ocaml-interface")
205208}
helix-syntax/languages/tree-sitter-luanew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-ocamlnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-yamlnew file+1

Submodule changed.

helix-term/src/commands.rs+153−59
@@ -231,6 +231,8 @@ impl Command {
231231 select_mode, "Enter selection extend mode",
232232 exit_select_mode, "Exit selection mode",
233233 goto_definition, "Goto definition",
234+ add_newline_above, "Add newline above",
235+ add_newline_below, "Add newline below",
234236 goto_type_definition, "Goto type definition",
235237 goto_implementation, "Goto implementation",
236238 goto_file_start, "Goto file start/line",
@@ -1850,7 +1852,11 @@ mod cmd {
18501852 args: &[&str],
18511853 _event: PromptEvent,
18521854 ) -> anyhow::Result<()> {
1853− let dir = args.first().context("target directory not provided")?;
1855+ let dir = helix_core::path::expand_tilde(
1856+ args.first()
1857+ .context("target directory not provided")?
1858+ .as_ref(),
1859+ );
18541860
18551861 if let Err(e) = std::env::set_current_dir(dir) {
18561862 bail!("Couldn't change the current working directory: {}", e);
@@ -4494,7 +4500,6 @@ enum ShellBehavior {
44944500 Ignore,
44954501 Insert,
44964502 Append,
4497− Filter,
44984503}
44994504
45004505fn shell_pipe(cx: &mut Context) {
@@ -4514,18 +4519,101 @@ fn shell_append_output(cx: &mut Context) {
45144519}
45154520
45164521fn shell_keep_pipe(cx: &mut Context) {
4517− shell(cx, "keep-pipe:".into(), ShellBehavior::Filter);
4522+ let prompt = Prompt::new(
4523+ "keep-pipe:".into(),
4524+ Some('|'),
4525+ |_input: &str| Vec::new(),
4526+ move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
4527+ let shell = &cx.editor.config.shell;
4528+ if event != PromptEvent::Validate {
4529+ return;
4530+ }
4531+ if input.is_empty() {
4532+ return;
4533+ }
4534+ let (view, doc) = current!(cx.editor);
4535+ let selection = doc.selection(view.id);
4536+
4537+ let mut ranges = SmallVec::with_capacity(selection.len());
4538+ let old_index = selection.primary_index();
4539+ let mut index: Option<usize> = None;
4540+ let text = doc.text().slice(..);
4541+
4542+ for (i, range) in selection.ranges().iter().enumerate() {
4543+ let fragment = range.fragment(text);
4544+ let (_output, success) = match shell_impl(shell, input, Some(fragment.as_bytes())) {
4545+ Ok(result) => result,
4546+ Err(err) => {
4547+ cx.editor.set_error(err.to_string());
4548+ return;
4549+ }
4550+ };
4551+
4552+ // if the process exits successfully, keep the selection
4553+ if success {
4554+ ranges.push(*range);
4555+ if i >= old_index && index.is_none() {
4556+ index = Some(ranges.len() - 1);
4557+ }
4558+ }
4559+ }
4560+
4561+ if ranges.is_empty() {
4562+ cx.editor.set_error("No selections remaining".to_string());
4563+ return;
4564+ }
4565+
4566+ let index = index.unwrap_or_else(|| ranges.len() - 1);
4567+ doc.set_selection(view.id, Selection::new(ranges, index));
4568+ },
4569+ );
4570+
4571+ cx.push_layer(Box::new(prompt));
45184572}
45194573
4520−fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
4574+fn shell_impl(
4575+ shell: &[String],
4576+ cmd: &str,
4577+ input: Option<&[u8]>,
4578+) -> anyhow::Result<(Tendril, bool)> {
45214579 use std::io::Write;
45224580 use std::process::{Command, Stdio};
4523− if cx.editor.config.shell.is_empty() {
4524− cx.editor.set_error("No shell set".to_owned());
4525− return;
4581+ if shell.is_empty() {
4582+ bail!("No shell set");
4583+ }
4584+
4585+ let mut process = match Command::new(&shell[0])
4586+ .args(&shell[1..])
4587+ .arg(cmd)
4588+ .stdin(Stdio::piped())
4589+ .stdout(Stdio::piped())
4590+ .stderr(Stdio::piped())
4591+ .spawn()
4592+ {
4593+ Ok(process) => process,
4594+ Err(e) => {
4595+ log::error!("Failed to start shell: {}", e);
4596+ return Err(e.into());
4597+ }
4598+ };
4599+ if let Some(input) = input {
4600+ let mut stdin = process.stdin.take().unwrap();
4601+ stdin.write_all(input)?;
4602+ }
4603+ let output = process.wait_with_output()?;
4604+
4605+ if !output.stderr.is_empty() {
4606+ log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr));
45264607 }
4608+
4609+ let tendril = Tendril::try_from_byte_slice(&output.stdout)
4610+ .map_err(|_| anyhow!("Process did not output valid UTF-8"))?;
4611+ Ok((tendril, output.status.success()))
4612+}
4613+
4614+fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
45274615 let pipe = match behavior {
4528− ShellBehavior::Replace | ShellBehavior::Ignore | ShellBehavior::Filter => true,
4616+ ShellBehavior::Replace | ShellBehavior::Ignore => true,
45294617 ShellBehavior::Insert | ShellBehavior::Append => false,
45304618 };
45314619 let prompt = Prompt::new(
@@ -4533,70 +4621,42 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
45334621 Some('|'),
45344622 |_input: &str| Vec::new(),
45354623 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
4624+ let shell = &cx.editor.config.shell;
45364625 if event != PromptEvent::Validate {
45374626 return;
45384627 }
4539− let shell = &cx.editor.config.shell;
4628+ if input.is_empty() {
4629+ return;
4630+ }
45404631 let (view, doc) = current!(cx.editor);
45414632 let selection = doc.selection(view.id);
45424633
45434634 let mut changes = Vec::with_capacity(selection.len());
4635+ let text = doc.text().slice(..);
45444636
45454637 for range in selection.ranges() {
4546− let mut process = match Command::new(&shell[0])
4547− .args(&shell[1..])
4548− .arg(input)
4549− .stdin(Stdio::piped())
4550− .stdout(Stdio::piped())
4551− .stderr(Stdio::piped())
4552− .spawn()
4553− {
4554− Ok(process) => process,
4555− Err(e) => {
4556− log::error!("Failed to start shell: {}", e);
4557− cx.editor.set_error("Failed to start shell".to_owned());
4558− return;
4559− }
4560− };
4561− if pipe {
4562− let stdin = process.stdin.as_mut().unwrap();
4563− let fragment = range.fragment(doc.text().slice(..));
4564− stdin.write_all(fragment.as_bytes()).unwrap();
4565− }
4566− let output = process.wait_with_output().unwrap();
4567−
4568− if behavior != ShellBehavior::Filter {
4569− if !output.status.success() {
4570− if !output.stderr.is_empty() {
4571− log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr));
4572− }
4573− cx.editor.set_error("Command failed".to_owned());
4574− return;
4575− }
4576− let tendril = match Tendril::try_from_byte_slice(&output.stdout) {
4577− Ok(tendril) => tendril,
4578− Err(_) => {
4579− cx.editor
4580− .set_error("Process did not output valid UTF-8".to_owned());
4638+ let fragment = range.fragment(text);
4639+ let (output, success) =
4640+ match shell_impl(shell, input, pipe.then(|| fragment.as_bytes())) {
4641+ Ok(result) => result,
4642+ Err(err) => {
4643+ cx.editor.set_error(err.to_string());
45814644 return;
45824645 }
45834646 };
4584− let (from, to) = match behavior {
4585− ShellBehavior::Replace => (range.from(), range.to()),
4586− ShellBehavior::Insert => (range.from(), range.from()),
4587− ShellBehavior::Append => (range.to(), range.to()),
4588− _ => (range.from(), range.from()),
4589− };
4590− changes.push((from, to, Some(tendril)));
4591− } else {
4592− // if the process exits successfully, keep the selection, otherwise delete it.
4593− let keep = output.status.success();
4594− changes.push((
4595− range.from(),
4596− if keep { range.from() } else { range.to() },
4597− None,
4598− ));
4647+
4648+ if !success {
4649+ cx.editor.set_error("Command failed".to_string());
4650+ return;
45994651 }
4652+
4653+ let (from, to) = match behavior {
4654+ ShellBehavior::Replace => (range.from(), range.to()),
4655+ ShellBehavior::Insert => (range.from(), range.from()),
4656+ ShellBehavior::Append => (range.to(), range.to()),
4657+ _ => (range.from(), range.from()),
4658+ };
4659+ changes.push((from, to, Some(output)));
46004660 }
46014661
46024662 if behavior != ShellBehavior::Ignore {
@@ -4614,3 +4674,37 @@ fn suspend(_cx: &mut Context) {
46144674 #[cfg(not(windows))]
46154675 signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
46164676}
4677+
4678+fn add_newline_above(cx: &mut Context) {
4679+ add_newline_impl(cx, Open::Above);
4680+}
4681+
4682+fn add_newline_below(cx: &mut Context) {
4683+ add_newline_impl(cx, Open::Below)
4684+}
4685+
4686+fn add_newline_impl(cx: &mut Context, open: Open) {
4687+ let count = cx.count();
4688+ let (view, doc) = current!(cx.editor);
4689+ let selection = doc.selection(view.id);
4690+ let text = doc.text();
4691+ let slice = text.slice(..);
4692+
4693+ let changes = selection.into_iter().map(|range| {
4694+ let (start, end) = range.line_range(slice);
4695+ let line = match open {
4696+ Open::Above => start,
4697+ Open::Below => end + 1,
4698+ };
4699+ let pos = text.line_to_char(line);
4700+ (
4701+ pos,
4702+ pos,
4703+ Some(doc.line_ending.as_str().repeat(count).into()),
4704+ )
4705+ });
4706+
4707+ let transaction = Transaction::change(text, changes);
4708+ doc.apply(&transaction, view.id);
4709+ doc.append_changes_to_history(view.id);
4710+}
helix-term/src/keymap.rs+2
@@ -412,10 +412,12 @@ impl Default for Keymaps {
412412 "[" => { "Left bracket"
413413 "d" => goto_prev_diag,
414414 "D" => goto_first_diag,
415+ "space" => add_newline_above,
415416 },
416417 "]" => { "Right bracket"
417418 "d" => goto_next_diag,
418419 "D" => goto_last_diag,
420+ "space" => add_newline_below,
419421 },
420422
421423 "/" => search,
languages.toml+33
@@ -312,6 +312,39 @@ roots = []
312312comment-token = ";"
313313indent = { tab-width = 4, unit = " " }
314314
315+[[language]]
316+name = "ocaml"
317+scope = "source.ocaml"
318+injection-regex = "ocaml"
319+file-types = ["ml"]
320+roots = []
321+comment-token = "(**)"
322+indent = { tab-width = 2, unit = " " }
323+
324+[[language]]
325+name = "ocaml-interface"
326+scope = "source.ocaml.interface"
327+file-types = ["mli"]
328+roots = []
329+comment-token = "(**)"
330+indent = { tab-width = 2, unit = " "}
331+
332+[[language]]
333+name = "lua"
334+scope = "source.lua"
335+file-types = ["lua"]
336+roots = []
337+comment-token = "--"
338+indent = { tab-width = 2, unit = " " }
339+
340+[[language]]
341+name = "yaml"
342+scope = "source.yaml"
343+file-types = ["yml", "yaml"]
344+roots = []
345+comment-token = "#"
346+indent = { tab-width = 2, unit = " " }
347+
315348# [[language]]
316349# name = "haskell"
317350# scope = "source.haskell"
runtime/queries/lua/highlights.scmnew file+166
@@ -0,0 +1,166 @@
1+;;; Highlighting for lua
2+
3+;;; Builtins
4+(self) @variable.builtin
5+
6+;; Keywords
7+
8+(if_statement
9+[
10+ "if"
11+ "then"
12+ "end"
13+] @keyword.control.conditional)
14+
15+[
16+ "else"
17+ "elseif"
18+ "then"
19+] @keyword.control.conditional
20+
21+(for_statement
22+[
23+ "for"
24+ "do"
25+ "end"
26+] @keyword.control.loop)
27+
28+(for_in_statement
29+[
30+ "for"
31+ "do"
32+ "end"
33+] @keyword.control.loop)
34+
35+(while_statement
36+[
37+ "while"
38+ "do"
39+ "end"
40+] @keyword.control.loop)
41+
42+(repeat_statement
43+[
44+ "repeat"
45+ "until"
46+] @keyword.control.loop)
47+
48+(do_statement
49+[
50+ "do"
51+ "end"
52+] @keyword)
53+
54+[
55+ "in"
56+ "local"
57+ (break_statement)
58+ "goto"
59+ "return"
60+] @keyword
61+
62+;; Operators
63+
64+[
65+ "not"
66+ "and"
67+ "or"
68+] @keyword.operator
69+
70+[
71+"="
72+"~="
73+"=="
74+"<="
75+">="
76+"<"
77+">"
78+"+"
79+"-"
80+"%"
81+"/"
82+"//"
83+"*"
84+"^"
85+"&"
86+"~"
87+"|"
88+">>"
89+"<<"
90+".."
91+"#"
92+ ] @operator
93+
94+;; Punctuation
95+["," "." ":" ";"] @punctuation.delimiter
96+
97+;; Brackets
98+[
99+ "("
100+ ")"
101+ "["
102+ "]"
103+ "{"
104+ "}"
105+] @punctuation.bracket
106+
107+; ;; Constants
108+[
109+(false)
110+(true)
111+] @boolean
112+(nil) @constant.builtin
113+(spread) @constant ;; "..."
114+((identifier) @constant
115+ (#match? @constant "^[A-Z][A-Z_0-9]*$"))
116+
117+;; Parameters
118+(parameters
119+ (identifier) @parameter)
120+
121+; ;; Functions
122+(function [(function_name) (identifier)] @function)
123+(function ["function" "end"] @keyword.function)
124+
125+(function
126+ (function_name
127+ (function_name_field
128+ (property_identifier) @function .)))
129+
130+(local_function (identifier) @function)
131+(local_function ["function" "end"] @keyword.function)
132+
133+(variable_declaration
134+ (variable_declarator (identifier) @function) (function_definition))
135+(local_variable_declaration
136+ (variable_declarator (identifier) @function) (function_definition))
137+
138+(function_definition ["function" "end"] @keyword.function)
139+
140+(function_call
141+ [
142+ ((identifier) @variable (method) @method)
143+ ((_) (method) @method)
144+ (identifier) @function
145+ (field_expression (property_identifier) @function)
146+ ]
147+ . (arguments))
148+
149+;; Nodes
150+(table ["{" "}"] @constructor)
151+(comment) @comment
152+(string) @string
153+(number) @number
154+(label_statement) @label
155+; A bit of a tricky one, this will only match field names
156+(field . (identifier) @property (_))
157+(shebang) @comment
158+
159+;; Property
160+(property_identifier) @property
161+
162+;; Variable
163+(identifier) @variable
164+
165+;; Error
166+(ERROR) @error
runtime/queries/lua/indents.tomlnew file+24
@@ -0,0 +1,24 @@
1+indent = [
2+ "function_definition",
3+ "variable_declaration",
4+ "local_variable_declaration",
5+ "field",
6+ "local_function",
7+ "function",
8+ "if_statement",
9+ "for_statement",
10+ "for_in_statement",
11+ "repeat_statement",
12+ "return_statement",
13+ "while_statement",
14+ "table",
15+ "arguments",
16+ "do_statement",
17+]
18+
19+oudent = [
20+ "end",
21+ "until",
22+ "}",
23+ ")",
24+]
runtime/queries/ocaml-interface/highlights.scmnew file+1
@@ -0,0 +1 @@
1+; inherits: ocaml
runtime/queries/ocaml/highlights.scmnew file+160
@@ -0,0 +1,160 @@
1+; Modules
2+;--------
3+
4+[(module_name) (module_type_name)] @namespace
5+
6+; Types
7+;------
8+
9+[(class_name) (class_type_name) (type_constructor)] @type
10+
11+[(constructor_name) (tag)] @constructor
12+
13+; Functions
14+;----------
15+
16+(let_binding
17+ pattern: (value_name) @function
18+ (parameter))
19+
20+(let_binding
21+ pattern: (value_name) @function
22+ body: [(fun_expression) (function_expression)])
23+
24+(value_specification (value_name) @function)
25+
26+(external (value_name) @function)
27+
28+(method_name) @method
29+
30+; Variables
31+;----------
32+
33+(value_pattern) @parameter
34+
35+; Application
36+;------------
37+
38+(infix_expression
39+ left: (value_path (value_name) @function)
40+ (infix_operator) @operator
41+ (#eq? @operator "@@"))
42+
43+(infix_expression
44+ (infix_operator) @operator
45+ right: (value_path (value_name) @function)
46+ (#eq? @operator "|>"))
47+
48+(application_expression
49+ function: (value_path (value_name) @function))
50+
51+; Properties
52+;-----------
53+
54+[(label_name) (field_name) (instance_variable_name)] @property
55+
56+; Constants
57+;----------
58+
59+[(boolean) (unit)] @constant
60+
61+[(number) (signed_number)] @number
62+
63+(character) @character
64+
65+(string) @string
66+
67+(quoted_string "{" @string "}" @string) @string
68+
69+(escape_sequence) @string.escape
70+
71+[
72+ (conversion_specification)
73+ (pretty_printing_indication)
74+] @punctuation.special
75+
76+; Keywords
77+;---------
78+
79+[
80+ "and" "as" "assert" "begin" "class" "constraint"
81+ "end" "external" "in"
82+ "inherit" "initializer" "lazy" "let" "match" "method" "module"
83+ "mutable" "new" "nonrec" "object" "of" "private" "rec" "sig" "struct"
84+ "type" "val" "virtual" "when" "with"
85+] @keyword
86+
87+["fun" "function" "functor"] @keyword.function
88+
89+["if" "then" "else"] @keyword.control.conditional
90+
91+["exception" "try"] @keyword.control.exception
92+
93+["include" "open"] @include
94+
95+["for" "to" "downto" "while" "do" "done"] @keyword.control.loop
96+
97+; Macros
98+;-------
99+
100+(attribute ["[@" "]"] @attribute)
101+(item_attribute ["[@@" "]"] @attribute)
102+(floating_attribute ["[@@@" "]"] @attribute)
103+(extension ["[%" "]"] @function.macro)
104+(item_extension ["[%%" "]"] @function.macro)
105+(quoted_extension ["{%" "}"] @function.macro)
106+(quoted_item_extension ["{%%" "}"] @function.macro)
107+"%" @function.macro
108+
109+["(" ")" "[" "]" "{" "}" "[|" "|]" "[<" "[>"] @punctuation.bracket
110+
111+(object_type ["<" ">"] @punctuation.bracket)
112+
113+[
114+ "," "." ";" ":" "=" "|" "~" "?" "+" "-" "!" ">" "&"
115+ "->" ";;" ":>" "+=" ":=" ".."
116+] @punctuation.delimiter
117+
118+; Operators
119+;----------
120+
121+[
122+ (prefix_operator)
123+ (sign_operator)
124+ (infix_operator)
125+ (hash_operator)
126+ (indexing_operator)
127+ (let_operator)
128+ (and_operator)
129+ (match_operator)
130+] @operator
131+
132+(match_expression (match_operator) @keyword)
133+
134+(value_definition [(let_operator) (and_operator)] @keyword)
135+
136+;; TODO: this is an error now
137+;(prefix_operator "!" @operator)
138+
139+(infix_operator ["&" "+" "-" "=" ">" "|" "%"] @operator)
140+
141+(signed_number ["+" "-"] @operator)
142+
143+["*" "#" "::" "<-"] @operator
144+
145+; Attributes
146+;-----------
147+
148+(attribute_id) @property
149+
150+; Comments
151+;---------
152+
153+[(comment) (line_number_directive) (directive) (shebang)] @comment
154+
155+(ERROR) @error
156+
157+; Blanket highlights
158+; ------------------
159+
160+[(value_name) (type_variable)] @variable
runtime/queries/ocaml/indents.tomlnew file+13
@@ -0,0 +1,13 @@
1+indent = [
2+ "let_binding",
3+ "type_binding",
4+ "structure",
5+ "signature",
6+ "record_declaration",
7+ "function_expression",
8+ "match_case",
9+]
10+
11+oudent = [
12+ "}",
13+]
runtime/queries/ocaml/locals.scmnew file+24
@@ -0,0 +1,24 @@
1+; Scopes
2+;-------
3+
4+[
5+ (let_binding)
6+ (class_binding)
7+ (class_function)
8+ (method_definition)
9+ (fun_expression)
10+ (object_expression)
11+ (for_expression)
12+ (match_case)
13+ (attribute_payload)
14+] @local.scope
15+
16+; Definitions
17+;------------
18+
19+(value_pattern) @local.definition
20+
21+; References
22+;-----------
23+
24+(value_path . (value_name) @local.reference)
runtime/queries/yaml/highlights.scmnew file+33
@@ -0,0 +1,33 @@
1+(block_mapping_pair key: (_) @property)
2+(flow_mapping (_ key: (_) @property))
3+(boolean_scalar) @boolean
4+(null_scalar) @constant.builtin
5+(double_quote_scalar) @string
6+(single_quote_scalar) @string
7+(escape_sequence) @string.escape
8+(integer_scalar) @number
9+(float_scalar) @number
10+(comment) @comment
11+(anchor_name) @type
12+(alias_name) @type
13+(tag) @type
14+(yaml_directive) @keyword
15+(ERROR) @error
16+
17+[
18+","
19+"-"
20+":"
21+">"
22+"?"
23+"|"
24+] @punctuation.delimiter
25+
26+[
27+"["
28+"]"
29+"{"
30+"}"
31+] @punctuation.bracket
32+
33+["*" "&"] @punctuation.special
runtime/queries/yaml/indents.tomlnew file+3
@@ -0,0 +1,3 @@
1+indent = [
2+ "block_mapping_pair",
3+]