aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules4
-rw-r--r--book/src/keymap.md292
-rw-r--r--book/src/themes.md3
-rw-r--r--helix-core/src/lib.rs83
-rw-r--r--helix-core/src/state.rs24
-rw-r--r--helix-core/src/syntax.rs17
-rw-r--r--helix-core/src/transaction.rs14
m---------helix-syntax/languages/tree-sitter-zig0
-rw-r--r--helix-term/src/application.rs21
-rw-r--r--helix-term/src/commands.rs2
-rw-r--r--helix-term/src/keymap.rs2
-rw-r--r--helix-term/src/ui/editor.rs8
-rw-r--r--helix-term/src/ui/menu.rs7
-rw-r--r--helix-term/src/ui/mod.rs3
-rw-r--r--helix-term/src/ui/prompt.rs20
-rw-r--r--helix-view/src/document.rs17
-rw-r--r--helix-view/src/input.rs4
-rw-r--r--languages.toml12
-rw-r--r--runtime/queries/zig/highlights.scm198
-rw-r--r--runtime/queries/zig/indents.toml12
20 files changed, 519 insertions, 224 deletions
diff --git a/.gitmodules b/.gitmodules
index e750198a..0e015658 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -102,3 +102,7 @@
path = helix-syntax/languages/tree-sitter-protobuf
url = https://github.com/yusdacra/tree-sitter-protobuf.git
shallow = true
+[submodule "helix-syntax/languages/tree-sitter-zig"]
+ path = helix-syntax/languages/tree-sitter-zig
+ url = https://github.com/maxxnino/tree-sitter-zig
+ shallow = true
diff --git a/book/src/keymap.md b/book/src/keymap.md
index 4eb85636..61378863 100644
--- a/book/src/keymap.md
+++ b/book/src/keymap.md
@@ -6,121 +6,121 @@
> NOTE: `f`, `F`, `t` and `T` are not confined to the current line.
-| Key | Description |
-| ----- | ----------- |
-| `h`, `Left` | Move left |
-| `j`, `Down` | Move down |
-| `k`, `Up` | Move up |
-| `l`, `Right` | Move right |
-| `w` | Move next word start |
-| `b` | Move previous word start |
-| `e` | Move next word end |
-| `W` | Move next WORD start |
-| `B` | Move previous WORD start |
-| `E` | Move next WORD end |
-| `t` | Find 'till next char |
-| `f` | Find next char |
-| `T` | Find 'till previous char |
-| `F` | Find previous char |
-| `Home` | Move to the start of the line |
-| `End` | Move to the end of the line |
-| `PageUp` | Move page up |
-| `PageDown` | Move page down |
-| `Ctrl-u` | Move half page up |
-| `Ctrl-d` | Move half page down |
-| `Ctrl-i` | Jump forward on the jumplist TODO: conflicts tab |
-| `Ctrl-o` | Jump backward on the jumplist |
-| `v` | Enter [select (extend) mode](#select--extend-mode) |
-| `g` | Enter [goto mode](#goto-mode) |
-| `m` | Enter [match mode](#match-mode) |
-| `:` | Enter command mode |
-| `z` | Enter [view mode](#view-mode) |
-| `Ctrl-w` | Enter [window mode](#window-mode) (maybe will be remove for spc w w later) |
-| `Space` | Enter [space mode](#space-mode) |
-| `K` | Show documentation for the item under the cursor |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `h`, `Left` | Move left | `move_char_left` |
+| `j`, `Down` | Move down | `move_char_right` |
+| `k`, `Up` | Move up | `move_line_up` |
+| `l`, `Right` | Move right | `move_line_down` |
+| `w` | Move next word start | `move_next_word_start` |
+| `b` | Move previous word start | `move_prev_word_start` |
+| `e` | Move next word end | `move_next_word_end` |
+| `W` | Move next WORD start | `move_next_long_word_start` |
+| `B` | Move previous WORD start | `move_prev_long_word_start` |
+| `E` | Move next WORD end | `move_next_long_word_end` |
+| `t` | Find 'till next char | `find_till_char` |
+| `f` | Find next char | `find_next_char` |
+| `T` | Find 'till previous char | `till_prev_char` |
+| `F` | Find previous char | `find_prev_char` |
+| `Home` | Move to the start of the line | `goto_line_start` |
+| `End` | Move to the end of the line | `goto_line_end` |
+| `PageUp` | Move page up | `page_up` |
+| `PageDown` | Move page down | `page_down` |
+| `Ctrl-u` | Move half page up | `half_page_up` |
+| `Ctrl-d` | Move half page down | `half_page_down` |
+| `Ctrl-i` | Jump forward on the jumplist TODO: conflicts tab | `jump_forward` |
+| `Ctrl-o` | Jump backward on the jumplist | `jump_backward` |
+| `v` | Enter [select (extend) mode](#select--extend-mode) | `select_mode` |
+| `g` | Enter [goto mode](#goto-mode) | N/A |
+| `m` | Enter [match mode](#match-mode) | N/A |
+| `:` | Enter command mode | `command_mode` |
+| `z` | Enter [view mode](#view-mode) | N/A |
+| `Ctrl-w` | Enter [window mode](#window-mode) (maybe will be remove for spc w w later) | N/A |
+| `Space` | Enter [space mode](#space-mode) | N/A |
+| `K` | Show documentation for the item under the cursor | `hover` |
### Changes
-| Key | Description |
-| ----- | ----------- |
-| `r` | Replace with a character |
-| `R` | Replace with yanked text |
-| `~` | Switch case of the selected text |
-| `` ` `` | Set the selected text to lower case |
-| `` Alt-` `` | Set the selected text to upper case |
-| `i` | Insert before selection |
-| `a` | Insert after selection (append) |
-| `I` | Insert at the start of the line |
-| `A` | Insert at the end of the line |
-| `o` | Open new line below selection |
-| `O` | Open new line above selection |
-| `u` | Undo change |
-| `U` | Redo change |
-| `y` | Yank selection |
-| `p` | Paste after selection |
-| `P` | Paste before selection |
-| `"` `<reg>` | Select a register to yank to or paste from |
-| `>` | Indent selection |
-| `<` | Unindent selection |
-| `=` | Format selection |
-| `d` | Delete selection |
-| `c` | Change selection (delete and enter insert mode) |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `r` | Replace with a character | `replace` |
+| `R` | Replace with yanked text | `replace_with_yanked` |
+| `~` | Switch case of the selected text | `switch_case` |
+| `` ` `` | Set the selected text to lower case | `switch_to_lowercase` |
+| `` Alt-` `` | Set the selected text to upper case | `switch_to_uppercase` |
+| `i` | Insert before selection | `insert_mode` |
+| `a` | Insert after selection (append) | `append_mode` |
+| `I` | Insert at the start of the line | `prepend_to_line` |
+| `A` | Insert at the end of the line | `append_to_line` |
+| `o` | Open new line below selection | `open_below` |
+| `O` | Open new line above selection | `open_above` |
+| `u` | Undo change | `undo` |
+| `U` | Redo change | `redo` |
+| `y` | Yank selection | `yank` |
+| `p` | Paste after selection | `paste_after` |
+| `P` | Paste before selection | `paste_before` |
+| `"` `<reg>` | Select a register to yank to or paste from | `select_register` |
+| `>` | Indent selection | `indent` |
+| `<` | Unindent selection | `unindent` |
+| `=` | Format selection | `format_selections` |
+| `d` | Delete selection | `delete_selection` |
+| `c` | Change selection (delete and enter insert mode) | `change_selection` |
### Selection manipulation
-| Key | Description |
-| ----- | ----------- |
-| `s` | Select all regex matches inside selections |
-| `S` | Split selection into subselections on regex matches |
-| `Alt-s` | Split selection on newlines |
-| `;` | Collapse selection onto a single cursor |
-| `Alt-;` | Flip selection cursor and anchor |
-| `C` | Copy selection onto the next line |
-| `Alt-C` | Copy selection onto the previous line |
-| `(` | Rotate main selection forward |
-| `)` | Rotate main selection backward |
-| `Alt-(` | Rotate selection contents forward |
-| `Alt-)` | Rotate selection contents backward |
-| `%` | Select entire file |
-| `x` | Select current line, if already selected, extend to next line |
-| `X` | Extend selection to line bounds (line-wise selection) |
-| | Expand selection to parent syntax node TODO: pick a key |
-| `J` | Join lines inside selection |
-| `K` | Keep selections matching the regex TODO: overlapped by hover help |
-| `Space` | Keep only the primary selection TODO: overlapped by space mode |
-| `Ctrl-c` | Comment/uncomment the selections |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `s` | Select all regex matches inside selections | `select_regex` |
+| `S` | Split selection into subselections on regex matches | `split_selection` |
+| `Alt-s` | Split selection on newlines | `split_selection_on_newline` |
+| `;` | Collapse selection onto a single cursor | `collapse_selection` |
+| `Alt-;` | Flip selection cursor and anchor | `flip_selections` |
+| `C` | Copy selection onto the next line | `copy_selection_on_next_line` |
+| `Alt-C` | Copy selection onto the previous line | `copy_selection_on_prev_line` |
+| `(` | Rotate main selection forward | `rotate_selections_backward` |
+| `)` | Rotate main selection backward | `rotate_selections_forward` |
+| `Alt-(` | Rotate selection contents forward | `rotate_selection_contents_backward` |
+| `Alt-)` | Rotate selection contents backward | `rotate_selection_contents_forward` |
+| `%` | Select entire file | `select_all` |
+| `x` | Select current line, if already selected, extend to next line | `extend_line` |
+| `X` | Extend selection to line bounds (line-wise selection) | `extend_to_line_bounds` |
+| | Expand selection to parent syntax node TODO: pick a key | `expand_selection` |
+| `J` | Join lines inside selection | `join_selections` |
+| `K` | Keep selections matching the regex TODO: overlapped by hover help | `keep_selections` |
+| `Space` | Keep only the primary selection TODO: overlapped by space mode | `keep_primary_selection` |
+| `Ctrl-c` | Comment/uncomment the selections | `toggle_comments` |
### Insert Mode
-| Key | Description |
-| ----- | ----------- |
-| `Escape` | Switch to normal mode |
-| `Ctrl-x` | Autocomplete |
-| `Ctrl-w` | Delete previous word |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `Escape` | Switch to normal mode | `normal_mode` |
+| `Ctrl-x` | Autocomplete | `completion` |
+| `Ctrl-w` | Delete previous word | `delete_word_backward` |
### Search
> TODO: The search implementation isn't ideal yet -- we don't support searching
in reverse, or searching via smartcase.
-| Key | Description |
-| ----- | ----------- |
-| `/` | Search for regex pattern |
-| `n` | Select next search match |
-| `N` | Add next search match to selection |
-| `*` | Use current selection as the search pattern |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `/` | Search for regex pattern | `search` |
+| `n` | Select next search match | `search_next` |
+| `N` | Add next search match to selection | `extend_search_next` |
+| `*` | Use current selection as the search pattern | `search_selection` |
### Diagnostics
> NOTE: `[` and `]` will likely contain more pair mappings in the style of
> [vim-unimpaired](https://github.com/tpope/vim-unimpaired)
-| Key | Description |
-| ----- | ----------- |
-| `[d` | Go to previous diagnostic |
-| `]d` | Go to next diagnostic |
-| `[D` | Go to first diagnostic in document |
-| `]D` | Go to last diagnostic in document |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `[d` | Go to previous diagnostic | `goto_prev_diag` |
+| `]d` | Go to next diagnostic | `goto_next_diag` |
+| `[D` | Go to first diagnostic in document | `goto_first_diag` |
+| `]D` | Go to last diagnostic in document | `goto_last_diag` |
## Select / extend mode
@@ -135,14 +135,14 @@ commands to extend the existing selection instead of replacing it.
View mode is intended for scrolling and manipulating the view without changing
the selection.
-| Key | Description |
-| ----- | ----------- |
-| `z` , `c` | Vertically center the line |
-| `t` | Align the line to the top of the screen |
-| `b` | Align the line to the bottom of the screen |
-| `m` | Align the line to the middle of the screen (horizontally) |
-| `j` | Scroll the view downwards |
-| `k` | Scroll the view upwards |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `z` , `c` | Vertically center the line | `align_view_center` |
+| `t` | Align the line to the top of the screen | `align_view_top` |
+| `b` | Align the line to the bottom of the screen | `align_view_bottom` |
+| `m` | Align the line to the middle of the screen (horizontally) | `align_view_middle` |
+| `j` | Scroll the view downwards | `scroll_down` |
+| `k` | Scroll the view upwards | `scroll_up` |
## Goto mode
@@ -150,21 +150,21 @@ Jumps to various locations.
> NOTE: Some of these features are only available with the LSP present.
-| Key | Description |
-| ----- | ----------- |
-| `g` | Go to the start of the file |
-| `e` | Go to the end of the file |
-| `h` | Go to the start of the line |
-| `l` | Go to the end of the line |
-| `s` | Go to first non-whitespace character of the line |
-| `t` | Go to the top of the screen |
-| `m` | Go to the middle of the screen |
-| `b` | Go to the bottom of the screen |
-| `d` | Go to definition |
-| `y` | Go to type definition |
-| `r` | Go to references |
-| `i` | Go to implementation |
-| `a` | Go to the last accessed/alternate file |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `g` | Go to the start of the file | `goto_file_start` |
+| `e` | Go to the end of the file | `goto_last_line` |
+| `h` | Go to the start of the line | `goto_line_start` |
+| `l` | Go to the end of the line | `goto_line_end` |
+| `s` | Go to first non-whitespace character of the line | `goto_first_nonwhitespace` |
+| `t` | Go to the top of the screen | `goto_window_top` |
+| `m` | Go to the middle of the screen | `goto_window_middle` |
+| `b` | Go to the bottom of the screen | `goto_window_bottom` |
+| `d` | Go to definition | `goto_definition` |
+| `y` | Go to type definition | `goto_type_definition` |
+| `r` | Go to references | `goto_reference` |
+| `i` | Go to implementation | `goto_implementation` |
+| `a` | Go to the last accessed/alternate file | `goto_last_accessed_file` |
## Match mode
@@ -172,14 +172,14 @@ Enter this mode using `m` from normal mode. See the relavant section
in [Usage](./usage.md) for an explanation about [surround](./usage.md#surround)
and [textobject](./usage.md#textobject) usage.
-| Key | Description |
-| ----- | ----------- |
-| `m` | Goto matching bracket |
-| `s` `<char>` | Surround current selection with `<char>` |
-| `r` `<from><to>` | Replace surround character `<from>` with `<to>` |
-| `d` `<char>` | Delete surround character `<char>` |
-| `a` `<object>` | Select around textobject |
-| `i` `<object>` | Select inside textobject |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `m` | Goto matching bracket | `match_brackets` |
+| `s` `<char>` | Surround current selection with `<char>` | `surround_add` |
+| `r` `<from><to>` | Replace surround character `<from>` with `<to>` | `surround_replace` |
+| `d` `<char>` | Delete surround character `<char>` | `surround_delete` |
+| `a` `<object>` | Select around textobject | `select_textobject_around` |
+| `i` `<object>` | Select inside textobject | `select_textobject_inner` |
## Object mode
@@ -189,35 +189,35 @@ TODO: Mappings for selecting syntax nodes (a superset of `[`).
This layer is similar to vim keybindings as kakoune does not support window.
-| Key | Description |
-| ----- | ------------- |
-| `w`, `Ctrl-w` | Switch to next window |
-| `v`, `Ctrl-v` | Vertical right split |
-| `h`, `Ctrl-h` | Horizontal bottom split |
-| `q`, `Ctrl-q` | Close current window |
+| Key | Description | Command |
+| ----- | ------------- | ------- |
+| `w`, `Ctrl-w` | Switch to next window | `rotate_view` |
+| `v`, `Ctrl-v` | Vertical right split | `vsplit` |
+| `h`, `Ctrl-h` | Horizontal bottom split | `hsplit` |
+| `q`, `Ctrl-q` | Close current window | `wclose` |
## Space mode
This layer is a kludge of mappings I had under leader key in neovim.
-| Key | Description |
-| ----- | ----------- |
-| `f` | Open file picker |
-| `b` | Open buffer picker |
-| `s` | Open symbol picker (current document) |
-| `a` | Apply code action |
-| `'` | Open last fuzzy picker |
-| `w` | Enter [window mode](#window-mode) |
-| `space` | Keep primary selection TODO: it's here because space mode replaced it |
-| `p` | Paste system clipboard after selections |
-| `P` | Paste system clipboard before selections |
-| `y` | Join and yank selections to clipboard |
-| `Y` | Yank main selection to clipboard |
-| `R` | Replace selections by clipboard contents |
+| Key | Description | Command |
+| ----- | ----------- | ------- |
+| `f` | Open file picker | `file_picker` |
+| `b` | Open buffer picker | `buffer_picker` |
+| `s` | Open symbol picker (current document) | `symbol_picker` |
+| `a` | Apply code action | `code_action` |
+| `'` | Open last fuzzy picker | `last_picker` |
+| `w` | Enter [window mode](#window-mode) | N/A |
+| `space` | Keep primary selection TODO: it's here because space mode replaced it | `keep_primary_selection` |
+| `p` | Paste system clipboard after selections | `paste_clipboard_after` |
+| `P` | Paste system clipboard before selections | `paste_clipboard_before` |
+| `y` | Join and yank selections to clipboard | `yank_joined_to_clipboard` |
+| `Y` | Yank main selection to clipboard | `yank_main_selection_to_clipboard` |
+| `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` |
# Picker
-Keys to use within picker.
+Keys to use within picker. Remapping currently not supported.
| Key | Description |
| ----- | ------------- |
diff --git a/book/src/themes.md b/book/src/themes.md
index f17195aa..0a4d58ad 100644
--- a/book/src/themes.md
+++ b/book/src/themes.md
@@ -91,6 +91,9 @@ Possible keys:
| `ui.help` | |
| `ui.text` | |
| `ui.text.focus` | |
+| `ui.info` | |
+| `ui.info.text` | |
+| `ui.menu` | |
| `ui.menu.selected` | |
| `ui.selection` | For selections in the editing area |
| `ui.selection.primary` | |
diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs
index be01c302..d971464a 100644
--- a/helix-core/src/lib.rs
+++ b/helix-core/src/lib.rs
@@ -98,6 +98,89 @@ pub fn cache_dir() -> std::path::PathBuf {
path
}
+// right overrides left
+pub fn merge_toml_values(left: toml::Value, right: toml::Value) -> toml::Value {
+ use toml::Value;
+
+ fn get_name(v: &Value) -> Option<&str> {
+ v.get("name").and_then(Value::as_str)
+ }
+
+ match (left, right) {
+ (Value::Array(mut left_items), Value::Array(right_items)) => {
+ left_items.reserve(right_items.len());
+ for rvalue in right_items {
+ let lvalue = get_name(&rvalue)
+ .and_then(|rname| left_items.iter().position(|v| get_name(v) == Some(rname)))
+ .map(|lpos| left_items.remove(lpos));
+ let mvalue = match lvalue {
+ Some(lvalue) => merge_toml_values(lvalue, rvalue),
+ None => rvalue,
+ };
+ left_items.push(mvalue);
+ }
+ Value::Array(left_items)
+ }
+ (Value::Table(mut left_map), Value::Table(right_map)) => {
+ for (rname, rvalue) in right_map {
+ match left_map.remove(&rname) {
+ Some(lvalue) => {
+ let merged_value = merge_toml_values(lvalue, rvalue);
+ left_map.insert(rname, merged_value);
+ }
+ None => {
+ left_map.insert(rname, rvalue);
+ }
+ }
+ }
+ Value::Table(left_map)
+ }
+ // Catch everything else we didn't handle, and use the right value
+ (_, value) => value,
+ }
+}
+
+#[cfg(test)]
+mod merge_toml_tests {
+ use super::merge_toml_values;
+
+ #[test]
+ fn language_tomls() {
+ use toml::Value;
+
+ const USER: &str = "
+ [[language]]
+ name = \"nix\"
+ test = \"bbb\"
+ indent = { tab-width = 4, unit = \" \", test = \"aaa\" }
+ ";
+
+ let base: Value = toml::from_slice(include_bytes!("../../languages.toml"))
+ .expect("Couldn't parse built-in langauges config");
+ let user: Value = toml::from_str(USER).unwrap();
+
+ let merged = merge_toml_values(base, user);
+ let languages = merged.get("language").unwrap().as_array().unwrap();
+ let nix = languages
+ .iter()
+ .find(|v| v.get("name").unwrap().as_str().unwrap() == "nix")
+ .unwrap();
+ let nix_indent = nix.get("indent").unwrap();
+
+ // We changed tab-width and unit in indent so check them if they are the new values
+ assert_eq!(
+ nix_indent.get("tab-width").unwrap().as_integer().unwrap(),
+ 4
+ );
+ assert_eq!(nix_indent.get("unit").unwrap().as_str().unwrap(), " ");
+ // We added a new keys, so check them
+ assert_eq!(nix.get("test").unwrap().as_str().unwrap(), "bbb");
+ assert_eq!(nix_indent.get("test").unwrap().as_str().unwrap(), "aaa");
+ // We didn't change comment-token so it should be same
+ assert_eq!(nix.get("comment-token").unwrap().as_str().unwrap(), "#");
+ }
+}
+
pub use etcetera::home_dir;
use etcetera::base_strategy::{choose_base_strategy, BaseStrategy};
diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs
index 7e4a7f70..dcc4b11b 100644
--- a/helix-core/src/state.rs
+++ b/helix-core/src/state.rs
@@ -1,6 +1,5 @@
use crate::{Rope, Selection};
-/// A state represents the current editor state of a single buffer.
#[derive(Debug, Clone)]
pub struct State {
pub doc: Rope,
@@ -15,27 +14,4 @@ impl State {
selection: Selection::point(0),
}
}
-
- // update/transact:
- // update(desc) => transaction ? transaction.doc() for applied doc
- // transaction.apply(doc)
- // doc.transact(fn -> ... end)
-
- // replaceSelection (transaction that replaces selection)
- // changeByRange
- // changes
- // slice
- //
- // getters:
- // tabSize
- // indentUnit
- // languageDataAt()
- //
- // config:
- // indentation
- // tabSize
- // lineUnit
- // syntax
- // foldable
- // changeFilter/transactionFilter
}
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index ae99a159..a7410203 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -1831,15 +1831,14 @@ mod test {
#[test]
fn test_input_edits() {
- use crate::State;
use tree_sitter::InputEdit;
- let state = State::new("hello world!\ntest 123".into());
+ let doc = Rope::from("hello world!\ntest 123");
let transaction = Transaction::change(
- &state.doc,
+ &doc,
vec![(6, 11, Some("test".into())), (12, 17, None)].into_iter(),
);
- let edits = LanguageLayer::generate_edits(state.doc.slice(..), transaction.changes());
+ let edits = LanguageLayer::generate_edits(doc.slice(..), transaction.changes());
// transaction.apply(&mut state);
assert_eq!(
@@ -1865,13 +1864,13 @@ mod test {
);
// Testing with the official example from tree-sitter
- let mut state = State::new("fn test() {}".into());
+ let mut doc = Rope::from("fn test() {}");
let transaction =
- Transaction::change(&state.doc, vec![(8, 8, Some("a: u32".into()))].into_iter());
- let edits = LanguageLayer::generate_edits(state.doc.slice(..), transaction.changes());
- transaction.apply(&mut state.doc);
+ Transaction::change(&doc, vec![(8, 8, Some("a: u32".into()))].into_iter());
+ let edits = LanguageLayer::generate_edits(doc.slice(..), transaction.changes());
+ transaction.apply(&mut doc);
- assert_eq!(state.doc, "fn test(a: u32) {}");
+ assert_eq!(doc, "fn test(a: u32) {}");
assert_eq!(
edits,
&[InputEdit {
diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs
index e20e550f..d682f058 100644
--- a/helix-core/src/transaction.rs
+++ b/helix-core/src/transaction.rs
@@ -125,7 +125,7 @@ impl ChangeSet {
/// In other words, If `this` goes `docA` → `docB` and `other` represents `docB` → `docC`, the
/// returned value will represent the change `docA` → `docC`.
pub fn compose(self, other: Self) -> Self {
- debug_assert!(self.len_after == other.len);
+ assert!(self.len_after == other.len);
// composing fails in weird ways if one of the sets is empty
// a: [] len: 0 len_after: 1 | b: [Insert(Tendril<UTF8>(inline: "\n")), Retain(1)] len 1
@@ -689,21 +689,21 @@ mod test {
#[test]
fn transaction_change() {
- let mut state = State::new("hello world!\ntest 123".into());
+ let mut doc = Rope::from("hello world!\ntest 123");
let transaction = Transaction::change(
- &state.doc,
+ &doc,
// (1, 1, None) is a useless 0-width delete
vec![(1, 1, None), (6, 11, Some("void".into())), (12, 17, None)].into_iter(),
);
- transaction.apply(&mut state.doc);
- assert_eq!(state.doc, Rope::from_str("hello void! 123"));
+ transaction.apply(&mut doc);
+ assert_eq!(doc, Rope::from_str("hello void! 123"));
}
#[test]
fn changes_iter() {
- let state = State::new("hello world!\ntest 123".into());
+ let doc = Rope::from("hello world!\ntest 123");
let changes = vec![(6, 11, Some("void".into())), (12, 17, None)];
- let transaction = Transaction::change(&state.doc, changes.clone().into_iter());
+ let transaction = Transaction::change(&doc, changes.clone().into_iter());
assert_eq!(transaction.changes_iter().collect::<Vec<_>>(), changes);
}
diff --git a/helix-syntax/languages/tree-sitter-zig b/helix-syntax/languages/tree-sitter-zig
new file mode 160000
+Subproject 049162bea8a44e1a4acd01b06e1c8672d9231a8
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index 17c762da..9aa98271 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -1,4 +1,4 @@
-use helix_core::{syntax, Range, Selection};
+use helix_core::{merge_toml_values, syntax, Range, Selection};
use helix_dap::Payload;
use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
use helix_view::{theme, Editor};
@@ -66,11 +66,16 @@ impl Application {
let theme_loader =
std::sync::Arc::new(theme::Loader::new(&conf_dir, &helix_core::runtime_dir()));
- // load $HOME/.config/helix/languages.toml, fallback to default config
- let lang_conf = std::fs::read(conf_dir.join("languages.toml"));
- let lang_conf = lang_conf
- .as_deref()
- .unwrap_or(include_bytes!("../../languages.toml"));
+ // load default and user config, and merge both
+ let def_lang_conf: toml::Value = toml::from_slice(include_bytes!("../../languages.toml"))
+ .expect("Could not parse built-in languages.toml, something must be very wrong");
+ let user_lang_conf: Option<toml::Value> = std::fs::read(conf_dir.join("languages.toml"))
+ .ok()
+ .map(|raw| toml::from_slice(&raw).expect("Could not parse user languages.toml"));
+ let lang_conf = match user_lang_conf {
+ Some(value) => merge_toml_values(def_lang_conf, value),
+ None => def_lang_conf,
+ };
let theme = if let Some(theme) = &config.theme {
match theme_loader.load(theme) {
@@ -84,7 +89,9 @@ impl Application {
theme_loader.default()
};
- let syn_loader_conf = toml::from_slice(lang_conf).expect("Could not parse languages.toml");
+ let syn_loader_conf: helix_core::syntax::Configuration = lang_conf
+ .try_into()
+ .expect("Could not parse merged (built-in + user) languages.toml");
let syn_loader = std::sync::Arc::new(syntax::Loader::new(syn_loader_conf));
let mut editor = Editor::new(
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 5eb51965..e5db1624 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -2369,7 +2369,7 @@ mod cmd {
},
TypableCommand {
name: "vsplit",
- alias: Some("vsp"),
+ alias: Some("vs"),
doc: "Open the file in a vertical split.",
fun: vsplit,
completer: Some(completers::filename),
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index d16c9a3a..2aa3f9f3 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -541,6 +541,8 @@ impl Default for Keymaps {
"home" => goto_line_start,
"end" => goto_line_end,
"esc" => exit_select_mode,
+
+ "v" => normal_mode,
}));
let insert = keymap!({ "Insert mode"
"esc" => normal_mode,
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 1f4fc1c5..6428870e 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -508,7 +508,13 @@ impl EditorView {
} else {
let line = match config.line_number {
LineNumber::Absolute => line + 1,
- LineNumber::Relative => abs_diff(current_line, line),
+ LineNumber::Relative => {
+ if current_line == line {
+ line + 1
+ } else {
+ abs_diff(current_line, line)
+ }
+ }
};
format!("{:>5}", line)
};
diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs
index a56cf19b..24dd3e61 100644
--- a/helix-term/src/ui/menu.rs
+++ b/helix-term/src/ui/menu.rs
@@ -259,8 +259,11 @@ impl<T: Item + 'static> Component for Menu<T> {
// TODO: required size should re-trigger when we filter items so we can draw a smaller menu
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
- let style = cx.editor.theme.get("ui.text");
- let selected = cx.editor.theme.get("ui.menu.selected");
+ let theme = &cx.editor.theme;
+ let style = theme
+ .try_get("ui.menu")
+ .unwrap_or_else(|| theme.get("ui.text"));
+ let selected = theme.get("ui.menu.selected");
let scroll = self.scroll;
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index e4871312..f3f8670e 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -228,7 +228,8 @@ pub mod completers {
let end = input.len()..;
- let mut files: Vec<_> = WalkBuilder::new(dir.clone())
+ let mut files: Vec<_> = WalkBuilder::new(&dir)
+ .hidden(false)
.max_depth(Some(1))
.build()
.filter_map(|file| {
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index 19986b5c..7197adea 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -400,18 +400,6 @@ impl Component for Prompt {
})));
match event {
- // char or shift char
- KeyEvent {
- code: KeyCode::Char(c),
- modifiers: KeyModifiers::NONE,
- }
- | KeyEvent {
- code: KeyCode::Char(c),
- modifiers: KeyModifiers::SHIFT,
- } => {
- self.insert_char(c);
- (self.callback_fn)(cx, &self.line, PromptEvent::Update);
- }
KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL,
@@ -539,6 +527,14 @@ impl Component for Prompt {
code: KeyCode::Char('q'),
modifiers: KeyModifiers::CONTROL,
} => self.exit_selection(),
+ // any char event that's not combined with control or mapped to any other combo
+ KeyEvent {
+ code: KeyCode::Char(c),
+ modifiers,
+ } if !modifiers.contains(KeyModifiers::CONTROL) => {
+ self.insert_char(c);
+ (self.callback_fn)(cx, &self.line, PromptEvent::Update);
+ }
_ => (),
};
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index a238644a..e890a336 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -102,7 +102,7 @@ pub struct Document {
language_server: Option<Arc<helix_lsp::Client>>,
}
-use std::fmt;
+use std::{fmt, mem};
impl fmt::Debug for Document {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Document")
@@ -301,20 +301,13 @@ pub async fn to_writer<'a, W: tokio::io::AsyncWriteExt + Unpin + ?Sized>(
Ok(())
}
-/// Like std::mem::replace() except it allows the replacement value to be mapped from the
-/// original value.
-fn take_with<T, F>(mut_ref: &mut T, closure: F)
+fn take_with<T, F>(mut_ref: &mut T, f: F)
where
+ T: Default,
F: FnOnce(T) -> T,
{
- use std::{panic, ptr};
-
- unsafe {
- let old_t = ptr::read(mut_ref);
- let new_t = panic::catch_unwind(panic::AssertUnwindSafe(|| closure(old_t)))
- .unwrap_or_else(|_| ::std::process::abort());
- ptr::write(mut_ref, new_t);
- }
+ let t = mem::take(mut_ref);
+ let _ = mem::replace(mut_ref, f(t));
}
use helix_lsp::lsp;
diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs
index 28b204bb..1e0ddfe2 100644
--- a/helix-view/src/input.rs
+++ b/helix-view/src/input.rs
@@ -15,10 +15,10 @@ pub struct KeyEvent {
}
impl KeyEvent {
- /// If a character was pressed (without modifiers), return it.
+ /// If a character was pressed, return it.
pub fn char(&self) -> Option<char> {
match self.code {
- KeyCode::Char(ch) if self.modifiers.is_empty() => Some(ch),
+ KeyCode::Char(ch) => Some(ch),
_ => None,
}
}
diff --git a/languages.toml b/languages.toml
index 9b9fb4b0..f4badb6e 100644
--- a/languages.toml
+++ b/languages.toml
@@ -311,3 +311,15 @@ indent = { tab-width = 4, unit = " " }
# comment-token = "--"
#
# indent = { tab-width = 2, unit = " " }
+
+[[language]]
+name = "zig"
+scope = "source.zig"
+injection-regex = "zig"
+file-types = ["zig"]
+roots = ["build.zig"]
+auto-format = true
+comment-token = "//"
+
+language-server = { command = "zls" }
+indent = { tab-width = 4, unit = " " }
diff --git a/runtime/queries/zig/highlights.scm b/runtime/queries/zig/highlights.scm
new file mode 100644
index 00000000..5a3d62dc
--- /dev/null
+++ b/runtime/queries/zig/highlights.scm
@@ -0,0 +1,198 @@
+[
+ (container_doc_comment)
+ (doc_comment)
+ (line_comment)
+] @comment
+
+; field in top level decl, and in struct, union...
+(ContainerField
+ (IDENTIFIER) @property
+ (SuffixExpr (IDENTIFIER) @type)?
+)
+
+; error.OutOfMemory;
+(SuffixExpr
+ "error"
+ "."
+ (IDENTIFIER) @constant
+)
+
+; var x: IDENTIFIER
+type: (SuffixExpr (IDENTIFIER) @type)
+
+; IDENTIFIER{}
+constructor: (SuffixExpr (IDENTIFIER) @constructor)
+
+; fields
+(FieldInit (IDENTIFIER) @property)
+
+; foo.bar.baz.function() calls
+(
+ (SuffixOp
+ (IDENTIFIER) @function
+ )
+ .
+ (FnCallArguments)
+)
+
+; function() calls
+(
+ (
+ (IDENTIFIER) @function
+ )
+ .
+ (FnCallArguments)
+)
+
+; functionn decl
+(FnProto
+ (IDENTIFIER) @function
+ (SuffixExpr (IDENTIFIER) @type)?
+ ("!")? @function.macro
+)
+
+; function parameters and types
+(ParamDecl
+ (IDENTIFIER) @variable.parameter
+ ":"
+ [
+ (ParamType (SuffixExpr (IDENTIFIER) @type))
+ (ParamType)
+ ]
+)
+
+; switch
+(SwitchItem
+ (SuffixExpr
+ "."
+ .
+ (IDENTIFIER) @constant
+ )
+)
+
+(INTEGER) @number
+
+(FLOAT) @number
+
+[
+ (STRINGLITERAL)
+ (STRINGLITERALSINGLE)
+] @string
+
+(CHAR_LITERAL) @string
+
+[
+ "allowzero"
+ "volatile"
+ "anytype"
+ "anyframe"
+ (BuildinTypeExpr)
+] @type.builtin
+
+(BreakLabel (IDENTIFIER) @label)
+(BlockLabel (IDENTIFIER) @label)
+
+[
+ "true"
+ "false"
+ "undefined"
+ "unreachable"
+ "null"
+] @constant.builtin
+
+[
+ "else"
+ "if"
+ "switch"
+ "for"
+ "while"
+ "return"
+ "break"
+ "continue"
+ "defer"
+ "errdefer"
+ "async"
+ "nosuspend"
+ "await"
+ "suspend"
+ "resume"
+ "try"
+ "catch"
+] @keyword.control
+
+[
+ "struct"
+ "enum"
+ "union"
+ "error"
+ "packed"
+ "opaque"
+ "test"
+ "usingnamespace"
+ "export"
+ "extern"
+ "const"
+ "var"
+ "comptime"
+ "threadlocal"
+] @keyword
+
+[
+ "pub"
+ "fn"
+] @keyword.function
+
+; PrecProc
+[
+ "inline"
+ "noinline"
+ "asm"
+ "callconv"
+ "noalias"
+] @attribute
+
+[
+ (BUILTINIDENTIFIER)
+ "linksection"
+ "align"
+] @function.builtin
+
+[
+ (CompareOp)
+ (BitwiseOp)
+ (BitShiftOp)
+ (AdditionOp)
+ (MultiplyOp)
+ (PrefixOp)
+ "or"
+ "and"
+ "orelse"
+ "*"
+ "**"
+ "->"
+ "=>"
+ ".?"
+ ".*"
+ "="
+] @operator
+
+[
+ ";"
+ "."
+ ","
+ ":"
+] @punctuation.delimiter
+
+[
+ ".."
+ "..."
+ "["
+ "]"
+ "("
+ ")"
+ "{"
+ "}"
+ (Payload "|")
+ (PtrPayload "|")
+ (PtrIndexPayload "|")
+] @punctuation
diff --git a/runtime/queries/zig/indents.toml b/runtime/queries/zig/indents.toml
new file mode 100644
index 00000000..e119078b
--- /dev/null
+++ b/runtime/queries/zig/indents.toml
@@ -0,0 +1,12 @@
+indent = [
+ "block",
+ "match_block",
+ "arguments",
+ "parameters"
+]
+
+outdent = [
+ "}",
+ "]",
+ ")"
+]