diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9518a5373ccb..2fa34e1f3328 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,12 +85,13 @@ jobs: rust: stable target: x86_64-pc-windows-msvc cross: false - - build: aarch64-macos - os: macos-latest - rust: stable - target: aarch64-apple-darwin - cross: false - skip_tests: true # x86_64 host can't run aarch64 code + # 23.03: build issues + # - build: aarch64-macos + # os: macos-latest + # rust: stable + # target: aarch64-apple-darwin + # cross: false + # skip_tests: true # x86_64 host can't run aarch64 code # - build: x86_64-win-gnu # os: windows-2019 # rust: stable-x86_64-gnu @@ -155,6 +156,10 @@ jobs: shell: bash if: matrix.build == 'aarch64-linux' || matrix.build == 'x86_64-linux' run: | + # Required as of 22.x https://github.com/AppImage/AppImageKit/wiki/FUSE + sudo add-apt-repository universe + sudo apt install libfuse2 + mkdir dist name=dev diff --git a/CHANGELOG.md b/CHANGELOG.md index dc91c9ff33a0..0778fef1d900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,265 @@ +# 23.03 (2023-03-31) + +> Checkpoint: `5323020c3f02b178f2b6807f13d89bf7f40d3cce` + +For the full log, check out the [git log](https://github.com/helix-editor/helix/compare/22.12..23.03). + +Breaking changes: + +- Select diagnostic range in `goto_*_diag` commands (#4713, #5164, #6193) +- Remove jump behavior from `increment`/`decrement` (#4123, #5929) +- Select change range in `goto_*_change` commands (#5206) +- Split file modification indicator from filename statusline elements (#4731, #6036) +- Jump to symbol ranges in LSP goto commands (#5986) + +Features: + +- Dynamic workspace symbol picker (#5055) +- Soft-wrap (#5420, #5786, #5893, #6142, #6440) +- Initial support for LSP snippet completions (#5864, b1f7528, #6263, bbf4800, 90348b8, f87299f, #6371) +- Add a statusline element for showing the current version control HEAD (#5682) +- Display LSP type hints (#5420, #5934, #6312) +- Enable the Kitty keyboard protocol on terminals with support (#4939, #6170, #6194) +- Add a statusline element for the basename of the current file (#5318) +- Add substring matching syntax for the picker (#5658) +- Support LSP `textDocument/prepareRename` (#6103) +- Allow multiple runtime directories with priorities (#5411) +- Allow configuring whether to insert or replace completions (#5728) + +Commands: + +- `:pipe-to` which pipes selections into a shell command and ignores output (#4931) +- `merge_consecutive_selections` (`A-_`) combines all consecutive selections (#5047) +- `rotate_view_reverse` which focuses the previous view (#5356) +- `goto_declaration` (`gD`, requires LSP) which jumps to a symbol's declaration (#5646) +- `file_picker_in_current_buffer_directory` (#4666) +- `:character-info` which shows information about the character under the cursor (#4000) +- `:toggle-option` for toggling config options at runtime (#4085) +- `dap_restart` for restarting a debug session in DAP (#5651) +- `:lsp-stop` to stop the language server of the current buffer (#5964) +- `:reset-diff-change` for resetting a diff hunk to its original text (#4974) + +Usability improvements: + +- Remove empty detail section in completion menu when LSP doesn't send details (#4902) +- Pass client information on LSP initialization (#4904) +- Allow specifying environment variables for language servers in language config (#4004) +- Allow detached git worktrees to be recognized as root paths (#5097) +- Improve error message handling for theme loading failures (#5073) +- Print the names of binaries required for LSP/DAP in health-check (#5195) +- Improve sorting in the picker in cases of ties (#5169) +- Add theming for prompt suggestions (#5104) +- Open a file picker when using `:open` on directories (#2707, #5278) +- Reload language config with `:config-reload` (#5239, #5381, #5431) +- Improve indent queries for python when the tree is errored (#5332) +- Picker: Open files without closing the picker with `A-ret` (#4435) +- Allow theming cursors by primary/secondary and by mode (#5130) +- Allow configuration of the minimum width for the line-numbers gutter (#4724, #5696) +- Use filename completer for `:run-shell-command` command (#5729) +- Surround with line-endings with `ms` (#4571) +- Hide duplicate symlinks in file pickers (#5658) +- Tabulate buffer picker contents (#5777) +- Add an option to disable LSP (#4425) +- Short-circuit tree-sitter and word object motions (#5851) +- Add exit code to failed command message (#5898) +- Make `m` textobject look for pairs enclosing selections (#3344) +- Negotiate LSP position encoding (#5894) +- Display deprecated LSP completions with strikethrough (#5932) +- Add JSONRPC request ID to failed LSP/DAP request log messages (#6010, #6018) +- Ignore case when filtering LSP completions (#6008) +- Show current language when no arguments are passed to `:set-language` (#5895) +- Refactor and rewrite all book documentation (#5534) +- Separate diagnostic picker message and code (#6095) +- Add a config option to bypass undercurl detection (#6253) +- Only complete appropriate arguments for typed commands (#5966) +- Discard outdated LSP diagnostics (3c9d5d0) +- Discard outdated LSP workspace edits (b6a4927) +- Run shell commands asynchronously (#6373) +- Show diagnostic codes in LSP diagnostic messages (#6378) + +Fixes: + +- Fix behavior of `auto-completion` flag for completion-on-trigger (#5042) +- Reset editor mode when changing buffers (#5072) +- Respect scrolloff settings in mouse movements (#5255) +- Avoid trailing `s` when only one file is opened (#5189) +- Fix erroneous indent between closers of auto-pairs (#5330) +- Expand `~` when parsing file paths in `:open` (#5329) +- Fix theme inheritance for default themes (#5218) +- Fix `extend_line` with a count when the current line(s) are selected (#5288) +- Prompt: Fix autocompletion for paths containing periods (#5175) +- Skip serializing JSONRPC params if params is null (#5471) +- Fix interaction with the `xclip` clipboard provider (#5426) +- Fix undo/redo execution from the command palette (#5294) +- Fix highlighting of non-block cursors (#5575) +- Fix panic when nooping in `join_selections` and `join_selections_space` (#5423) +- Fix selecting a changed file in global search (#5639) +- Fix initial syntax highlight layer sort order (#5196) +- Fix UTF-8 length handling for shellwords (#5738) +- Remove C-j and C-k bindings from the completion menu (#5070) +- Always commit to history when pasting (#5790) +- Properly handle LSP position encoding (#5711) +- Fix infinite loop in `copy_selection_on_prev_line` (#5888) +- Fix completion popup positioning (#5842) +- Fix a panic when uncommenting a line with only a comment token (#5933) +- Fix panic in `goto_window_center` at EOF (#5987) +- Ignore invalid file URIs sent by a language server (#6000) +- Decode LSP URIs for the workspace diagnostics picker (#6016) +- Fix incorrect usages of `tab_width` with `indent_width` (#5918) +- DAP: Send Disconnect if the Terminated event is received (#5532) +- DAP: Validate key and index exist when requesting variables (#5628) +- Check LSP renaming support before prompting for rename text (#6257) +- Fix indent guide rendering (#6136) +- Fix division by zero panic (#6155) +- Fix lacking space panic (#6109) +- Send error replies for malformed and unhandled LSP requests (#6058) +- Fix table column calculations for dynamic pickers (#5920) +- Skip adding jumplist entries for `:` line number previews (#5751) +- Fix completion race conditions (#6173) +- Fix `shrink_selection` with multiple cursors (#6093) +- Fix indentation calculation for lines with mixed tabs/spaces (#6278) +- No-op `client/registerCapability` LSP requests (#6258) +- Send the STOP signal to all processes in the process group (#3546) +- Fix workspace edit client capabilities declaration (7bf168d) +- Fix highlighting in picker results with multiple columns (#6333) + +Themes: + +- Update `serika` (#5038, #6344) +- Update `flatwhite` (#5036, #6323) +- Update `autumn` (#5051, #5397, #6280, #6316) +- Update `acme` (#5019, #5486, #5488) +- Update `gruvbox` themes (#5066, #5333, #5540, #6285, #6295) +- Update `base16_transparent` (#5105) +- Update `dark_high_contrast` (#5105) +- Update `dracula` (#5236, #5627, #6414) +- Update `monokai_pro_spectrum` (#5250, #5602) +- Update `rose_pine` (#5267, #5489, #6384) +- Update `kanagawa` (#5273, #5571, #6085) +- Update `emacs` (#5334) +- Add `github` themes (#5353, efeec12) + - Dark themes: `github_dark`, `github_dark_colorblind`, `github_dark_dimmed`, `github_dark_high_contrast`, `github_dark_tritanopia` + - Light themes: `github_light`, `github_light_colorblind`, `github_light_dimmed`, `github_light_high_contrast`, `github_light_tritanopia` +- Update `solarized` variants (#5445, #6327) +- Update `catppuccin` variants (#5404, #6107, #6269) +- Use curly underlines in built-in themes (#5419) +- Update `zenburn` (#5573) +- Rewrite `snazzy` (#3971) +- Add `monokai_aqua` (#5578) +- Add `markup.strikethrough` to existing themes (#5619) +- Update `sonokai` (#5440) +- Update `onedark` (#5755) +- Add `ayu_evolve` (#5638, #6028, #6225) +- Add `jellybeans` (#5719) +- Update `fleet_dark` (#5605, #6266, #6324, #6375) +- Add `darcula-solid` (#5778) +- Remove text background from monokai themes (#6009) +- Update `pop_dark` (#5992, #6208, #6227, #6292) +- Add `everblush` (#6086) +- Add `adwaita-dark` (#6042, #6342) +- Update `papercolor` (#6162) +- Update `onelight` (#6192, #6276) +- Add `molokai` (#6260) +- Update `ayu` variants (#6329) +- Update `tokyonight` variants (#6349) +- Update `nord` variants (#6376) + +New languages: + +- BibTeX (#5064) +- Mermaid.js (#5147) +- Crystal (#4993, #5205) +- MATLAB/Octave (#5192) +- `tfvars` (uses HCL) (#5396) +- Ponylang (#5416) +- DHall (1f6809c) +- Sagemath (#5649) +- MSBuild (#5793) +- pem (#5797) +- passwd (#4959) +- hosts (#4950, #5914) +- uxntal (#6047) +- Yuck (#6064, #6242) +- GNU gettext PO (#5996) +- Sway (#6023) +- NASM (#6068) +- PRQL (#6126) +- reStructuredText (#6180) +- Smithy (#6370) +- VHDL (#5826) +- Rego (OpenPolicy Agent) (#6415) +- Nim (#6123) + +Updated languages and queries: + +- Use diff syntax for patch files (#5085) +- Add Haskell textobjects (#5061) +- Fix commonlisp configuration (#5091) +- Update Scheme (bae890d) +- Add indent queries for Bash (#5149) +- Recognize `c++` as a C++ extension (#5183) +- Enable HTTP server in `metals` (Scala) config (#5551) +- Change V-lang language server to `v ls` from `vls` (#5677) +- Inject comment grammar into Nix (#5208) +- Update Rust highlights (#5238, #5349) +- Fix HTML injection within Markdown (#5265) +- Fix comment token for godot (#5276) +- Expand injections for Vue (#5268) +- Add `.bash_aliases` as a Bash file-type (#5347) +- Fix comment token for sshclientconfig (#5351) +- Update Prisma (#5417) +- Update C++ (#5457) +- Add more file-types for Python (#5593) +- Update tree-sitter-scala (#5576) +- Add an injection regex for Lua (#5606) +- Add `build.gradle` to java roots configuration (#5641) +- Add Hub PR files to markdown file-types (#5634) +- Add an external formatter configuration for Cue (#5679) +- Add injections for builders and writers to Nix (#5629) +- Update tree-sitter-xml to fix whitespace parsing (#5685) +- Add `Justfile` to the make file-types configuration (#5687) +- Update tree-sitter-sql and highlight queries (#5683, #5772) +- Use the bash grammar and queries for env language (#5720) +- Add podspec files to ruby file-types (#5811) +- Recognize `.C` and `.H` file-types as C++ (#5808) +- Recognize plist and mobileconfig files as XML (#5863) +- Fix `select` indentation in Go (#5713) +- Check for external file modifications when writing (#5805) +- Recognize containerfiles as dockerfile syntax (#5873) +- Update godot grammar and queries (#5944, #6186) +- Improve DHall highlights (#5959) +- Recognize `.env.dist` and `source.env` as env language (#6003) +- Update tree-sitter-git-rebase (#6030, #6094) +- Improve SQL highlights (#6041) +- Improve markdown highlights and inject LaTeX (#6100) +- Add textobject queries for Elm (#6084) +- Recognize graphql schema file type (#6159) +- Improve highlighting in comments (#6143) +- Improve highlighting for JavaScript/TypeScript/ECMAScript languages (#6205) +- Improve PHP highlights (#6203, #6250, #6299) +- Improve Go highlights (#6204) +- Highlight unchecked sqlx functions as SQL in Rust (#6256) +- Improve Erlang highlights (cdd6c8d) +- Improve Nix highlights (fb4d703) +- Improve gdscript highlights (#6311) +- Improve Vlang highlights (#6279) +- Improve Makefile highlights (#6339) +- Remove auto-pair for `'` in OCaml (#6381) +- Fix indents in switch statements in ECMA languages (#6369) +- Recognize xlb and storyboard file-types as XML (#6407) +- Recognize cts and mts file-types as TypeScript (#6424) +- Recognize SVG file-type as XML (#6431) +- Add theme scopes for (un)checked list item markup scopes (#6434) +- Update git commit grammar and add the comment textobject (#6439) +- Recognize ARB file-type as JSON (#6452) + +Packaging: + +- Fix Nix flake devShell for darwin hosts (#5368) +- Add Appstream metadata file to `contrib/` (#5643) +- Increase the MSRV to 1.65 (#5570, #6185) +- Expose the Nix flake's `wrapper` (#5994) + # 22.12 (2022-12-06) This is a great big release filled with changes from a 99 contributors. A big _thank you_ to you all! diff --git a/VERSION b/VERSION index e70b3aebd5b7..35371314c17b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -22.12 \ No newline at end of file +23.03 \ No newline at end of file diff --git a/contrib/Helix.appdata.xml b/contrib/Helix.appdata.xml index a242849751cd..b99738a18ea2 100644 --- a/contrib/Helix.appdata.xml +++ b/contrib/Helix.appdata.xml @@ -36,6 +36,9 @@ + + https://helix-editor.com/news/release-23-03-highlights/ + https://helix-editor.com/news/release-22-12-highlights/ diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 130a74af61d8..d48f937f9aa9 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -24,7 +24,7 @@ use crate::{ compositor::{Compositor, Event}, config::Config, job::Jobs, - keymap::Keymaps, + keymap::{Keymaps, LayoutRemap}, ui::{self, overlay::overlayed}, }; @@ -155,7 +155,13 @@ impl Application { let keys = Box::new(Map::new(Arc::clone(&config), |config: &Config| { &config.keys })); - let editor_view = Box::new(ui::EditorView::new(Keymaps::new(keys))); + let layout_remap = Box::new(Map::new(Arc::clone(&config), |config: &Config| { + &config.editor.layout_remap + })); + let editor_view = Box::new(ui::EditorView::new( + Keymaps::new(keys), + LayoutRemap::new(layout_remap), + )); compositor.push(editor_view); if args.load_tutor { diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 3033c6a4883c..4c03743d96e7 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -300,6 +300,32 @@ impl Keymap { res } + pub fn traverse_map(&self, f: F) + where + F: Fn(&Vec, String), + { + fn map_node(node: &KeyTrie, keys: &mut Vec, f: &F) + where + F: Fn(&Vec, String), + { + match node { + KeyTrie::Leaf(cmd) => match cmd { + MappableCommand::Typable { name, .. } => f(keys, name.into()), + MappableCommand::Static { name, .. } => f(keys, (*name).to_owned()), + }, + KeyTrie::Node(next) => { + for (key, trie) in &next.map { + keys.push(*key); + map_node(trie, keys, f); + keys.pop(); + } + } + KeyTrie::Sequence(_) => {} + } + } + map_node(&self.root, &mut Vec::new(), &f); + } + pub fn root(&self) -> &KeyTrie { &self.root } @@ -422,6 +448,35 @@ pub fn merge_keys(dst: &mut HashMap, mut delta: HashMap>>, +} + +impl LayoutRemap { + pub fn new(map: Box>>) -> Self { + Self { map } + } + + pub fn translate(&self, event: &KeyEvent) -> KeyEvent { + use helix_view::keyboard::KeyCode; + let remap = &*self.map.load(); + if let KeyCode::Char(c) = event.code { + KeyEvent { + code: KeyCode::Char(*remap.get(&c).unwrap_or(&c)), + modifiers: event.modifiers, + } + } else { + *event + } + } +} + +impl Default for LayoutRemap { + fn default() -> Self { + Self::new(Box::new(ArcSwap::new(Arc::new(HashMap::new())))) + } +} + #[cfg(test)] mod tests { use super::macros::keymap; diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index fd8e8fb21b47..9a54bd67ffac 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -3,7 +3,7 @@ use crate::{ compositor::{Component, Context, Event, EventResult}, job::{self, Callback}, key, - keymap::{KeymapResult, Keymaps}, + keymap::{KeymapResult, Keymaps, LayoutRemap}, ui::{ document::{render_document, LinePos, TextRenderer, TranslatedPosition}, Completion, ProgressSpinners, @@ -38,6 +38,7 @@ use super::{document::LineDecoration, lsp::SignatureHelp}; pub struct EditorView { pub keymaps: Keymaps, + layout_remap: LayoutRemap, on_next_key: Option, pseudo_pending: Vec, pub(crate) last_insert: (commands::MappableCommand, Vec), @@ -55,14 +56,15 @@ pub enum InsertEvent { impl Default for EditorView { fn default() -> Self { - Self::new(Keymaps::default()) + Self::new(Keymaps::default(), LayoutRemap::default()) } } impl EditorView { - pub fn new(keymaps: Keymaps) -> Self { + pub fn new(keymaps: Keymaps, layout_remap: LayoutRemap) -> Self { Self { keymaps, + layout_remap, on_next_key: None, pseudo_pending: Vec::new(), last_insert: (commands::MappableCommand::normal_mode, Vec::new()), @@ -793,6 +795,12 @@ impl EditorView { ) -> Option { let mut last_mode = mode; self.pseudo_pending.extend(self.keymaps.pending()); + // Translate Normal and Select mode keys using layout remap table. + let event = if !matches!(last_mode, Mode::Insert) { + self.layout_remap.translate(&event) + } else { + event + }; let key_result = self.keymaps.get(mode, event); cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox()); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 34c59b9b4b75..bd37245880bf 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -16,6 +16,7 @@ use helix_vcs::DiffProviderRegistry; use futures_util::stream::select_all::SelectAll; use futures_util::{future, StreamExt}; use helix_lsp::Call; +use log::warn; use tokio_stream::wrappers::UnboundedReceiverStream; use std::{ @@ -74,6 +75,52 @@ where ) } +#[derive(Serialize, Deserialize, Default)] +#[serde(rename_all = "kebab-case", default, deny_unknown_fields)] +struct LayoutMap { + from: String, + into: String, +} + +fn deserialize_layout_remap<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + // Make (from, to) char pairs from `Vec` of `LayoutMap`s filtering + // cases where `from == to` which we don't need to translate. + let mut pairs = Vec::::deserialize(deserializer)? + .iter() + .flat_map(|layout_map| layout_map.from.chars().zip(layout_map.into.chars())) + .filter(|(from, to)| from != to) + .collect::>(); + pairs + .iter() + .filter_map(|(_, to)| pairs.contains_key(to).then(|| *to)) + .collect::>() + .into_iter() + .for_each(|c| { + warn!( + "Key '{}' removed from layout-remap as it would overwrite Latin layout binding", + c + ); + pairs.remove(&c); + }); + Ok(pairs) +} + +fn serialize_layout_remap(map: &HashMap, serializer: S) -> Result +where + S: Serializer, +{ + let mut from = String::new(); + let mut into = String::new(); + map.iter().for_each(|(f, t)| { + from.push(*f); + into.push(*t); + }); + vec![LayoutMap { from, into }].serialize(serializer) +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct GutterConfig { @@ -284,6 +331,12 @@ pub struct Config { pub soft_wrap: SoftWrap, /// Workspace specific lsp ceiling dirs pub workspace_lsp_roots: Vec, + /// Translate pressed keys in Normal and Select modes when a keyboard layout different from Latin is chosen in the system. + #[serde( + serialize_with = "serialize_layout_remap", + deserialize_with = "deserialize_layout_remap" + )] + pub layout_remap: HashMap, } #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -753,6 +806,7 @@ impl Default for Config { text_width: 80, completion_replace: false, workspace_lsp_roots: Vec::new(), + layout_remap: HashMap::new(), } } } diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index d8832adce11e..bccfb7946327 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -73,6 +73,24 @@ impl KeyEvent { } } + /// Translates a `KeyCode` using the `f` function. Only + /// `KeyCode::Char(c)` are translated. This method should be used + /// whenever a translation for the characters is desired, e.g. when + /// remapping a keyboard layout from non-Latin to Latin. + pub fn translate(&self, f: F) -> KeyEvent + where + F: Fn(char) -> char, + { + if let KeyCode::Char(c) = self.code { + KeyEvent { + code: KeyCode::Char(f(c)), + modifiers: self.modifiers, + } + } else { + *self + } + } + /// Format the key in such a way that a concatenated sequence /// of keys can be read easily. ///