Merge branch 'master' of github.com:helix-editor/helix

imgbot
trivernis 2 years ago
commit c24c30584b
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -0,0 +1,13 @@
---
name: Enhancement
about: Suggest an improvement
title: ''
labels: C-enhancement
assignees: ''
---
<!--
Your enhancement may already be reported!
Please search on the issue tracker before creating a new issue.
If this is an idea for a feature, please open an "Idea" Discussion instead.
-->

@ -1,13 +0,0 @@
---
name: Feature request
about: Suggest a new feature or improvement
title: ''
labels: C-enhancement
assignees: ''
---
<!-- Your feature may already be reported!
Please search on the issue tracker before creating one. -->
#### Describe your feature request

@ -131,3 +131,23 @@ jobs:
|| (echo "Run 'cargo xtask docgen', commit the changes and push again" \
&& exit 1)
queries:
name: Tree-sitter queries
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install stable toolchain
uses: helix-editor/rust-toolchain@v1
with:
profile: minimal
override: true
- uses: Swatinem/rust-cache@v1
- name: Generate docs
uses: actions-rs/cargo@v1
with:
command: xtask
args: query-check

@ -1,3 +1,272 @@
# 22.08.1 (2022-09-01)
This is a patch release that fixes a panic caused by closing splits or buffers. ([#3633](https://github.com/helix-editor/helix/pull/3633))
# 22.08 (2022-08-31)
A big _thank you_ to our contributors! This release had 87 contributors.
As usual, the following is a summary of each of the changes since the last release.
For the full log, check out the [git log](https://github.com/helix-editor/helix/compare/22.05..22.08).
Breaking changes:
- Special keymap names for `+`, `;` and `%` have been replaced with those literal characters ([#2677](https://github.com/helix-editor/helix/pull/2677), [#3556](https://github.com/helix-editor/helix/pull/3556))
- `A-Left` and `A-Right` have become `C-Left` and `C-Right` for word-wise motion ([#2500](https://github.com/helix-editor/helix/pull/2500))
- The `catppuccin` theme's name has been corrected from `catpuccin` ([#2713](https://github.com/helix-editor/helix/pull/2713))
- `catppuccin` has been replaced by its variants, `catppuccin_frappe`, `catppuccin_latte`, `catppuccin_macchiato`, `catppuccin_mocha` ([#3281](https://github.com/helix-editor/helix/pull/3281))
- `C-n` and `C-p` have been removed from the default insert mode keymap ([#3340](https://github.com/helix-editor/helix/pull/3340))
- The `extend_line` command has been replaced with `extend_line_below` and a new `extend_line` command now exists ([#3046](https://github.com/helix-editor/helix/pull/3046))
Features:
- Add an integration testing harness ([#2359](https://github.com/helix-editor/helix/pull/2359))
- Indent guides ([#1796](https://github.com/helix-editor/helix/pull/1796), [906259c](https://github.com/helix-editor/helix/commit/906259c))
- Cursorline ([#2170](https://github.com/helix-editor/helix/pull/2170), [fde9e03](https://github.com/helix-editor/helix/commit/fde9e03))
- Select all instances of the symbol under the cursor (`<space>h`) ([#2738](https://github.com/helix-editor/helix/pull/2738))
- A picker for document and workspace LSP diagnostics (`<space>g`/`<space>G`) ([#2013](https://github.com/helix-editor/helix/pull/2013), [#2984](https://github.com/helix-editor/helix/pull/2984))
- Allow styling the mode indicator per-mode ([#2676](https://github.com/helix-editor/helix/pull/2676))
- Live preview for the theme picker ([#1798](https://github.com/helix-editor/helix/pull/1798))
- Configurable statusline ([#2434](https://github.com/helix-editor/helix/pull/2434))
- LSP SignatureHelp ([#1755](https://github.com/helix-editor/helix/pull/1755), [a8b123f](https://github.com/helix-editor/helix/commit/a8b123f))
- A picker for the jumplist ([#3033](https://github.com/helix-editor/helix/pull/3033))
- Configurable external formatter binaries ([#2942](https://github.com/helix-editor/helix/pull/2942))
- Bracketed paste support ([#3233](https://github.com/helix-editor/helix/pull/3233), [12ddd03](https://github.com/helix-editor/helix/commit/12ddd03))
Commands:
- `:insert-output` and `:append-output` which insert/append output from a shell command ([#2589](https://github.com/helix-editor/helix/pull/2589))
- The `t` textobject (`]t`/`[t`/`mit`/`mat`) for navigating tests ([#2807](https://github.com/helix-editor/helix/pull/2807))
- `C-Backspace` and `C-Delete` for word-wise deletion in prompts and pickers ([#2500](https://github.com/helix-editor/helix/pull/2500))
- `A-Delete` for forward word-wise deletion in insert mode ([#2500](https://github.com/helix-editor/helix/pull/2500))
- `C-t` for toggling the preview pane in pickers ([#3021](https://github.com/helix-editor/helix/pull/3021))
- `extend_line` now extends in the direction of the cursor ([#3046](https://github.com/helix-editor/helix/pull/3046))
Usability improvements and fixes:
- Fix tree-sitter parser builds on illumos ([#2602](https://github.com/helix-editor/helix/pull/2602))
- Remove empty stratch buffer from jumplists when removing ([5ed6223](https://github.com/helix-editor/helix/commit/5ed6223))
- Fix panic on undo after `shell_append_output` ([#2625](https://github.com/helix-editor/helix/pull/2625))
- Sort LSP edits by start range ([3d91c99](https://github.com/helix-editor/helix/commit/3d91c99))
- Be more defensive about LSP URI conversions ([6de6a3e](https://github.com/helix-editor/helix/commit/6de6a3e), [378f438](https://github.com/helix-editor/helix/commit/378f438))
- Ignore SendErrors when grammar builds fail ([#2641](https://github.com/helix-editor/helix/pull/2641))
- Append `set_line_ending` to document history ([#2649](https://github.com/helix-editor/helix/pull/2649))
- Use last prompt entry when empty ([b14c258](https://github.com/helix-editor/helix/commit/b14c258), [#2870](https://github.com/helix-editor/helix/pull/2870))
- Do not add extra line breaks in markdown lists ([#2689](https://github.com/helix-editor/helix/pull/2689))
- Disable dialyzer by default for ElixirLS ([#2710](https://github.com/helix-editor/helix/pull/2710))
- Refactor textobject node capture ([#2741](https://github.com/helix-editor/helix/pull/2741))
- Prevent re-selecting the same range with `expand_selection` ([#2760](https://github.com/helix-editor/helix/pull/2760))
- Introduce `keyword.storage` highlight scope ([#2731](https://github.com/helix-editor/helix/pull/2731))
- Handle symlinks more consistently ([#2718](https://github.com/helix-editor/helix/pull/2718))
- Improve markdown list rendering ([#2687](https://github.com/helix-editor/helix/pull/2687))
- Update auto-pairs and idle-timout settings when the config is reloaded ([#2736](https://github.com/helix-editor/helix/pull/2736))
- Fix panic on closing last buffer ([#2658](https://github.com/helix-editor/helix/pull/2658))
- Prevent modifying jumplist until jumping to a reference ([#2670](https://github.com/helix-editor/helix/pull/2670))
- Ensure `:quit` and `:quit!` take no arguments ([#2654](https://github.com/helix-editor/helix/pull/2654))
- Fix crash due to cycles when replaying macros ([#2647](https://github.com/helix-editor/helix/pull/2647))
- Pass LSP FormattingOptions ([#2635](https://github.com/helix-editor/helix/pull/2635))
- Prevent showing colors when the health-check is piped ([#2836](https://github.com/helix-editor/helix/pull/2836))
- Use character indexing for mouse selection ([#2839](https://github.com/helix-editor/helix/pull/2839))
- Display the highest severity diagnostic for a line in the gutter ([#2835](https://github.com/helix-editor/helix/pull/2835))
- Default the ruler color to red background ([#2669](https://github.com/helix-editor/helix/pull/2669))
- Make `move_vertically` aware of tabs and wide characters ([#2620](https://github.com/helix-editor/helix/pull/2620))
- Enable shellwords for Windows ([#2767](https://github.com/helix-editor/helix/pull/2767))
- Add history suggestions to global search ([#2717](https://github.com/helix-editor/helix/pull/2717))
- Fix the scrollbar's length proportional to total menu items ([#2860](https://github.com/helix-editor/helix/pull/2860))
- Reset terminal modifiers for diagnostic text ([#2861](https://github.com/helix-editor/helix/pull/2861), [#2900](https://github.com/helix-editor/helix/pull/2900))
- Redetect indents and line-endings after a Language Server replaces the document ([#2778](https://github.com/helix-editor/helix/pull/2778))
- Check selection's visible width when copying on mouse click ([#2711](https://github.com/helix-editor/helix/pull/2711))
- Fix edge-case in tree-sitter `expand_selection` command ([#2877](https://github.com/helix-editor/helix/pull/2877))
- Add a single-width left margin for the completion popup ([#2728](https://github.com/helix-editor/helix/pull/2728))
- Right-align the scrollbar in the completion popup ([#2754](https://github.com/helix-editor/helix/pull/2754))
- Fix recursive macro crash and empty macro lockout ([#2902](https://github.com/helix-editor/helix/pull/2902))
- Fix backwards character deletion on other whitespaces ([#2855](https://github.com/helix-editor/helix/pull/2855))
- Add search and space/backspace bindings to view modes ([#2803](https://github.com/helix-editor/helix/pull/2803))
- Add `--vsplit` and `--hsplit` CLI arguments for opening in splits ([#2773](https://github.com/helix-editor/helix/pull/2773), [#3073](https://github.com/helix-editor/helix/pull/3073))
- Sort themes, languages and files inputs by score and name ([#2675](https://github.com/helix-editor/helix/pull/2675))
- Highlight entire rows in ([#2939](https://github.com/helix-editor/helix/pull/2939))
- Fix backwards selection duplication widening bug ([#2945](https://github.com/helix-editor/helix/pull/2945), [#3024](https://github.com/helix-editor/helix/pull/3024))
- Skip serializing Option type DAP fields ([44f5963](https://github.com/helix-editor/helix/commit/44f5963))
- Fix required `cwd` field in DAP `RunTerminalArguments` type ([85411be](https://github.com/helix-editor/helix/commit/85411be), [#3240](https://github.com/helix-editor/helix/pull/3240))
- Add LSP `workspace/applyEdit` to client capabilities ([#3012](https://github.com/helix-editor/helix/pull/3012))
- Respect count for repeating motion ([#3057](https://github.com/helix-editor/helix/pull/3057))
- Respect count for selecting next/previous match ([#3056](https://github.com/helix-editor/helix/pull/3056))
- Respect count for tree-sitter motions ([#3058](https://github.com/helix-editor/helix/pull/3058))
- Make gutters padding optional ([#2996](https://github.com/helix-editor/helix/pull/2996))
- Support pre-filling prompts ([#2459](https://github.com/helix-editor/helix/pull/2459), [#3259](https://github.com/helix-editor/helix/pull/3259))
- Add statusline element to display file line-endings ([#3113](https://github.com/helix-editor/helix/pull/3113))
- Keep jump and file history when using `:split` ([#3031](https://github.com/helix-editor/helix/pull/3031), [#3160](https://github.com/helix-editor/helix/pull/3160))
- Make tree-sitter query `; inherits <language>` feature imperative ([#2470](https://github.com/helix-editor/helix/pull/2470))
- Indent with tabs by default ([#3095](https://github.com/helix-editor/helix/pull/3095))
- Fix non-msvc grammar compilation on Windows ([#3190](https://github.com/helix-editor/helix/pull/3190))
- Add spacer element to the statusline ([#3165](https://github.com/helix-editor/helix/pull/3165), [255c173](https://github.com/helix-editor/helix/commit/255c173))
- Make gutters padding automatic ([#3163](https://github.com/helix-editor/helix/pull/3163))
- Add `code` for LSP `Diagnostic` type ([#3096](https://github.com/helix-editor/helix/pull/3096))
- Add position percentage to the statusline ([#3168](https://github.com/helix-editor/helix/pull/3168))
- Add a configurable and themable statusline separator string ([#3175](https://github.com/helix-editor/helix/pull/3175))
- Use OR of all selections when `search_selection` acts on multiple selections ([#3138](https://github.com/helix-editor/helix/pull/3138))
- Add clipboard information to logs and the healthcheck ([#3271](https://github.com/helix-editor/helix/pull/3271))
- Fix align selection behavior on tabs ([#3276](https://github.com/helix-editor/helix/pull/3276))
- Fix terminal cursor shape reset ([#3289](https://github.com/helix-editor/helix/pull/3289))
- Add an `injection.include-unnamed-children` predicate to injections queries ([#3129](https://github.com/helix-editor/helix/pull/3129))
- Add a `-c`/`--config` CLI flag for specifying config file location ([#2666](https://github.com/helix-editor/helix/pull/2666))
- Detect indent-style in `:set-language` command ([#3330](https://github.com/helix-editor/helix/pull/3330))
- Fix non-deterministic highlighting ([#3275](https://github.com/helix-editor/helix/pull/3275))
- Avoid setting the stdin handle when not necessary ([#3248](https://github.com/helix-editor/helix/pull/3248), [#3379](https://github.com/helix-editor/helix/pull/3379))
- Fix indent guide styling ([#3324](https://github.com/helix-editor/helix/pull/3324))
- Fix tab highlight when tab is partially visible ([#3313](https://github.com/helix-editor/helix/pull/3313))
- Add completion for nested settings ([#3183](https://github.com/helix-editor/helix/pull/3183))
- Advertise WorkspaceSymbolClientCapabilities LSP client capability ([#3361](https://github.com/helix-editor/helix/pull/3361))
- Remove duplicate entries from the theme picker ([#3439](https://github.com/helix-editor/helix/pull/3439))
- Shorted output for grammar fetching and building ([#3396](https://github.com/helix-editor/helix/pull/3396))
- Add a `tabpad` option for visible tab padding whitespace characters ([#3458](https://github.com/helix-editor/helix/pull/3458))
- Make DAP external terminal provider configurable ([cb7615e](https://github.com/helix-editor/helix/commit/cb7615e))
- Use health checkmark character with shorter width ([#3505](https://github.com/helix-editor/helix/pull/3505))
- Reset document mode to normal on view focus loss ([e4c9d40](https://github.com/helix-editor/helix/commit/e4c9d40))
- Render indented code-blocks in markdown ([#3503](https://github.com/helix-editor/helix/pull/3503))
- Add WezTerm to DAP terminal provider defaults ([#3588](https://github.com/helix-editor/helix/pull/3588))
- Derive `Document` language name from `languages.toml` `name` key ([#3338](https://github.com/helix-editor/helix/pull/3338))
- Fix process spawning error handling ([#3349](https://github.com/helix-editor/helix/pull/3349))
- Don't resolve links for `:o` completion ([8a4fbf6](https://github.com/helix-editor/helix/commit/8a4fbf6))
- Recalculate completion after pasting into prompt ([e77b7d1](https://github.com/helix-editor/helix/commit/e77b7d1))
- Fix extra selections with regex anchors ([#3598](https://github.com/helix-editor/helix/pull/3598))
- Move mode transition logic to `handle_keymap_event` ([#2634](https://github.com/helix-editor/helix/pull/2634))
- Add documents to view history when using the jumplist ([#3593](https://github.com/helix-editor/helix/pull/3593))
- Prevent panic when loading tree-sitter queries ([fa1dc7e](https://github.com/helix-editor/helix/commit/fa1dc7e))
- Discard LSP publishDiagnostic when LS is not initialized ([#3403](https://github.com/helix-editor/helix/pull/3403))
- Refactor tree-sitter textobject motions as repeatable motions ([#3264](https://github.com/helix-editor/helix/pull/3264))
- Avoid command execution hooks on closed docs ([#3613](https://github.com/helix-editor/helix/pull/3613))
- Share `restore_term` code between panic and normal exits ([#2612](https://github.com/helix-editor/helix/pull/2612))
- Show clipboard info in `--health` output ([#2947](https://github.com/helix-editor/helix/pull/2947))
- Recalculate completion when going through prompt history ([#3193](https://github.com/helix-editor/helix/pull/3193))
Themes:
- Update `tokyonight` and `tokyonight_storm` themes ([#2606](https://github.com/helix-editor/helix/pull/2606))
- Update `solarized_light` themes ([#2626](https://github.com/helix-editor/helix/pull/2626))
- Fix `catpuccin` `ui.popup` theme ([#2644](https://github.com/helix-editor/helix/pull/2644))
- Update selection style of `night_owl` ([#2668](https://github.com/helix-editor/helix/pull/2668))
- Fix spelling of `catppuccin` theme ([#2713](https://github.com/helix-editor/helix/pull/2713))
- Update `base16_default`'s `ui.menu` ([#2794](https://github.com/helix-editor/helix/pull/2794))
- Add `noctis_bordo` ([#2830](https://github.com/helix-editor/helix/pull/2830))
- Add `acme` ([#2876](https://github.com/helix-editor/helix/pull/2876))
- Add `meliora` ([#2884](https://github.com/helix-editor/helix/pull/2884), [#2890](https://github.com/helix-editor/helix/pull/2890))
- Add cursorline scopes to various themes ([33d287a](https://github.com/helix-editor/helix/commit/33d287a), [#2892](https://github.com/helix-editor/helix/pull/2892), [#2915](https://github.com/helix-editor/helix/pull/2915), [#2916](https://github.com/helix-editor/helix/pull/2916), [#2918](https://github.com/helix-editor/helix/pull/2918), [#2927](https://github.com/helix-editor/helix/pull/2927), [#2925](https://github.com/helix-editor/helix/pull/2925), [#2938](https://github.com/helix-editor/helix/pull/2938), [#2962](https://github.com/helix-editor/helix/pull/2962), [#3054](https://github.com/helix-editor/helix/pull/3054))
- Add mode colors to various themes ([#2926](https://github.com/helix-editor/helix/pull/2926), [#2933](https://github.com/helix-editor/helix/pull/2933), [#2929](https://github.com/helix-editor/helix/pull/2929), [#3098](https://github.com/helix-editor/helix/pull/3098), [#3104](https://github.com/helix-editor/helix/pull/3104), [#3128](https://github.com/helix-editor/helix/pull/3128), [#3135](https://github.com/helix-editor/helix/pull/3135), [#3200](https://github.com/helix-editor/helix/pull/3200))
- Add `nord_light` ([#2908](https://github.com/helix-editor/helix/pull/2908))
- Update `night_owl` ([#2929](https://github.com/helix-editor/helix/pull/2929))
- Update `autumn` ([2e70985](https://github.com/helix-editor/helix/commit/2e70985), [936ed3a](https://github.com/helix-editor/helix/commit/936ed3a))
- Update `one_dark` ([#3011](https://github.com/helix-editor/helix/pull/3011))
- Add `noctis` ([#3043](https://github.com/helix-editor/helix/pull/3043), [#3128](https://github.com/helix-editor/helix/pull/3128))
- Update `boo_berry` ([#3191](https://github.com/helix-editor/helix/pull/3191))
- Update `monokai` ([#3131](https://github.com/helix-editor/helix/pull/3131))
- Add `ayu_dark`, `ayu_light`, `ayu_mirage` ([#3184](https://github.com/helix-editor/helix/pull/3184))
- Update `onelight` ([#3226](https://github.com/helix-editor/helix/pull/3226))
- Add `base16_transparent` ([#3216](https://github.com/helix-editor/helix/pull/3216), [b565fff](https://github.com/helix-editor/helix/commit/b565fff))
- Add `flatwhite` ([#3236](https://github.com/helix-editor/helix/pull/3236))
- Update `dark_plus` ([#3302](https://github.com/helix-editor/helix/pull/3302))
- Add `doom_acario_dark` ([#3308](https://github.com/helix-editor/helix/pull/3308), [#3539](https://github.com/helix-editor/helix/pull/3539))
- Add `rose_pine_moon` ([#3229](https://github.com/helix-editor/helix/pull/3229))
- Update `spacebones_light` ([#3342](https://github.com/helix-editor/helix/pull/3342))
- Fix typos in themes ([8deaebd](https://github.com/helix-editor/helix/commit/8deaebd), [#3412](https://github.com/helix-editor/helix/pull/3412))
- Add `emacs` ([#3410](https://github.com/helix-editor/helix/pull/3410))
- Add `papercolor-light` ([#3426](https://github.com/helix-editor/helix/pull/3426), [#3470](https://github.com/helix-editor/helix/pull/3470), [#3585](https://github.com/helix-editor/helix/pull/3585))
- Add `penumbra+` ([#3398](https://github.com/helix-editor/helix/pull/3398))
- Add `fleetish` ([#3591](https://github.com/helix-editor/helix/pull/3591), [#3607](https://github.com/helix-editor/helix/pull/3607))
- Add `sonokai` ([#3595](https://github.com/helix-editor/helix/pull/3595))
- Update all themes for theme lints ([#3587](https://github.com/helix-editor/helix/pull/3587))
LSP:
- V ([#2526](https://github.com/helix-editor/helix/pull/2526))
- Prisma ([#2703](https://github.com/helix-editor/helix/pull/2703))
- Clojure ([#2780](https://github.com/helix-editor/helix/pull/2780))
- WGSL ([#2872](https://github.com/helix-editor/helix/pull/2872))
- Elvish ([#2948](https://github.com/helix-editor/helix/pull/2948))
- Idris ([#2971](https://github.com/helix-editor/helix/pull/2971))
- Fortran ([#3025](https://github.com/helix-editor/helix/pull/3025))
- Gleam ([#3139](https://github.com/helix-editor/helix/pull/3139))
- Odin ([#3214](https://github.com/helix-editor/helix/pull/3214))
New languages:
- V ([#2526](https://github.com/helix-editor/helix/pull/2526))
- EDoc ([#2640](https://github.com/helix-editor/helix/pull/2640))
- JSDoc ([#2650](https://github.com/helix-editor/helix/pull/2650))
- OpenSCAD ([#2680](https://github.com/helix-editor/helix/pull/2680))
- Prisma ([#2703](https://github.com/helix-editor/helix/pull/2703))
- Clojure ([#2780](https://github.com/helix-editor/helix/pull/2780))
- Starlark ([#2903](https://github.com/helix-editor/helix/pull/2903))
- Elvish ([#2948](https://github.com/helix-editor/helix/pull/2948))
- Fortran ([#3025](https://github.com/helix-editor/helix/pull/3025))
- Ungrammar ([#3048](https://github.com/helix-editor/helix/pull/3048))
- SCSS ([#3074](https://github.com/helix-editor/helix/pull/3074))
- Go Template ([#3091](https://github.com/helix-editor/helix/pull/3091))
- Graphviz dot ([#3241](https://github.com/helix-editor/helix/pull/3241))
- Cue ([#3262](https://github.com/helix-editor/helix/pull/3262))
- Slint ([#3355](https://github.com/helix-editor/helix/pull/3355))
- Beancount ([#3297](https://github.com/helix-editor/helix/pull/3297))
- Taskwarrior ([#3468](https://github.com/helix-editor/helix/pull/3468))
- xit ([#3521](https://github.com/helix-editor/helix/pull/3521))
- ESDL ([#3526](https://github.com/helix-editor/helix/pull/3526))
- Awk ([#3528](https://github.com/helix-editor/helix/pull/3528), [#3535](https://github.com/helix-editor/helix/pull/3535))
- Pascal ([#3542](https://github.com/helix-editor/helix/pull/3542))
Updated languages and queries:
- Nix ([#2472](https://github.com/helix-editor/helix/pull/2472))
- Elixir ([#2619](https://github.com/helix-editor/helix/pull/2619))
- CPON ([#2643](https://github.com/helix-editor/helix/pull/2643))
- Textobjects queries for Erlang, Elixir, Gleam ([#2661](https://github.com/helix-editor/helix/pull/2661))
- Capture rust closures as function textobjects ([4a27e2d](https://github.com/helix-editor/helix/commit/4a27e2d))
- Heex ([#2800](https://github.com/helix-editor/helix/pull/2800), [#3170](https://github.com/helix-editor/helix/pull/3170))
- Add `<<=` operator highlighting for Rust ([#2805](https://github.com/helix-editor/helix/pull/2805))
- Fix comment injection in JavaScript/TypeScript ([#2763](https://github.com/helix-editor/helix/pull/2763))
- Nickel ([#2859](https://github.com/helix-editor/helix/pull/2859))
- Add `Rakefile` and `Gemfile` to Ruby file-types ([#2875](https://github.com/helix-editor/helix/pull/2875))
- Erlang ([#2910](https://github.com/helix-editor/helix/pull/2910), [ac669ad](https://github.com/helix-editor/helix/commit/ac669ad))
- Markdown ([#2910](https://github.com/helix-editor/helix/pull/2910), [#3108](https://github.com/helix-editor/helix/pull/3108), [#3400](https://github.com/helix-editor/helix/pull/3400))
- Bash ([#2910](https://github.com/helix-editor/helix/pull/2910))
- Rust ([#2910](https://github.com/helix-editor/helix/pull/2910), [#3397](https://github.com/helix-editor/helix/pull/3397))
- Edoc ([#2910](https://github.com/helix-editor/helix/pull/2910))
- HTML ([#2910](https://github.com/helix-editor/helix/pull/2910))
- Make ([#2910](https://github.com/helix-editor/helix/pull/2910))
- TSQ ([#2910](https://github.com/helix-editor/helix/pull/2910), [#2960](https://github.com/helix-editor/helix/pull/2960))
- git-commit ([#2910](https://github.com/helix-editor/helix/pull/2910))
- Use default fallback for Python indents ([9ae70cc](https://github.com/helix-editor/helix/commit/9ae70cc))
- Add Haskell LSP roots ([#2954](https://github.com/helix-editor/helix/pull/2954))
- Ledger ([#2936](https://github.com/helix-editor/helix/pull/2936), [#2988](https://github.com/helix-editor/helix/pull/2988))
- Nickel ([#2987](https://github.com/helix-editor/helix/pull/2987))
- JavaScript/TypeScript ([#2961](https://github.com/helix-editor/helix/pull/2961), [#3219](https://github.com/helix-editor/helix/pull/3219), [#3213](https://github.com/helix-editor/helix/pull/3213), [#3280](https://github.com/helix-editor/helix/pull/3280), [#3301](https://github.com/helix-editor/helix/pull/3301))
- GLSL ([#3051](https://github.com/helix-editor/helix/pull/3051))
- Fix locals tracking in Rust ([#3027](https://github.com/helix-editor/helix/pull/3027), [#3212](https://github.com/helix-editor/helix/pull/3212), [#3345](https://github.com/helix-editor/helix/pull/3345))
- Verilog ([#3158](https://github.com/helix-editor/helix/pull/3158))
- Ruby ([#3173](https://github.com/helix-editor/helix/pull/3173), [#3527](https://github.com/helix-editor/helix/pull/3527))
- Svelte ([#3147](https://github.com/helix-editor/helix/pull/3147))
- Add Elixir and HEEx comment textobjects ([#3179](https://github.com/helix-editor/helix/pull/3179))
- Python ([#3103](https://github.com/helix-editor/helix/pull/3103), [#3201](https://github.com/helix-editor/helix/pull/3201), [#3284](https://github.com/helix-editor/helix/pull/3284))
- PHP ([#3317](https://github.com/helix-editor/helix/pull/3317))
- Latex ([#3370](https://github.com/helix-editor/helix/pull/3370))
- Clojure ([#3387](https://github.com/helix-editor/helix/pull/3387))
- Swift ([#3461](https://github.com/helix-editor/helix/pull/3461))
- C# ([#3480](https://github.com/helix-editor/helix/pull/3480), [#3494](https://github.com/helix-editor/helix/pull/3494))
- Org ([#3489](https://github.com/helix-editor/helix/pull/3489))
- Elm ([#3497](https://github.com/helix-editor/helix/pull/3497))
- Dart ([#3419](https://github.com/helix-editor/helix/pull/3419))
- Julia ([#3507](https://github.com/helix-editor/helix/pull/3507))
- Fix Rust textobjects ([#3590](https://github.com/helix-editor/helix/pull/3590))
- C ([00d88e5](https://github.com/helix-editor/helix/commit/00d88e5))
- Update Rust ([0ef0ef9](https://github.com/helix-editor/helix/commit/0ef0ef9))
Packaging:
- Add `rust-analyzer` to Nix flake devShell ([#2739](https://github.com/helix-editor/helix/pull/2739))
- Add cachix information to the Nix flake ([#2999](https://github.com/helix-editor/helix/pull/2999))
- Pass makeWrapperArgs to wrapProgram in the Nix flake ([#3003](https://github.com/helix-editor/helix/pull/3003))
- Add a way to override which grammars are built by Nix ([#3141](https://github.com/helix-editor/helix/pull/3141))
- Add a GitHub actions release for `aarch64-macos` ([#3137](https://github.com/helix-editor/helix/pull/3137))
- Add shell auto-completions for Elvish ([#3331](https://github.com/helix-editor/helix/pull/3331))
# 22.05 (2022-05-28)
An even bigger shout out than usual to all the contributors - we had a whopping

72
Cargo.lock generated

@ -13,18 +13,18 @@ dependencies = [
[[package]]
name = "android_system_properties"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.62"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
checksum = "a26fa4d7e3f2eebadf743988fc8aec9fa9a9e82611acafd77c1462ed6262440a"
[[package]]
name = "arc-swap"
@ -57,9 +57,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.10.0"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
name = "bytecount"
@ -199,9 +199,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "encoding_rs"
@ -278,15 +278,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]]
name = "futures-executor"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528"
checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
dependencies = [
"futures-core",
"futures-task",
@ -295,15 +295,15 @@ dependencies = [
[[package]]
name = "futures-task"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306"
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
[[package]]
name = "futures-util"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577"
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
dependencies = [
"futures-core",
"futures-task",
@ -550,13 +550,14 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.44"
version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf7d67cf4a22adc5be66e75ebdf769b3f2ea032041437a7061f97a63dad4b"
checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"js-sys",
"once_cell",
"wasm-bindgen",
"winapi",
]
@ -628,9 +629,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.127"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "libloading"
@ -644,9 +645,9 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390"
dependencies = [
"autocfg",
"scopeguard",
@ -663,9 +664,9 @@ dependencies = [
[[package]]
name = "lsp-types"
version = "0.93.0"
version = "0.93.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70c74e2173b2b31f8655d33724b4b45ac13f439386f66290f539c22b144c2212"
checksum = "a3bcfee315dde785ba887edb540b08765fd7df75a7d948844be6bf5712246734"
dependencies = [
"bitflags",
"serde",
@ -688,9 +689,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
version = "0.5.5"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a79b39c93a7a5a27eeaf9a23b5ff43f1b9e0ad6b1cdd441140ae53c35613fc7"
checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
dependencies = [
"libc",
]
@ -1061,9 +1062,9 @@ checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]]
name = "socket2"
version = "0.4.4"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
dependencies = [
"libc",
"winapi",
@ -1125,18 +1126,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09"
dependencies = [
"proc-macro2",
"quote",
@ -1387,13 +1388,13 @@ checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "which"
version = "4.2.5"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
dependencies = [
"either",
"lazy_static",
"libc",
"once_cell",
]
[[package]]
@ -1475,6 +1476,7 @@ name = "xtask"
version = "0.6.0"
dependencies = [
"helix-core",
"helix-loader",
"helix-term",
"toml",
]

@ -70,10 +70,9 @@ for a language.
## MacOS
Helix can be installed on MacOS through homebrew via:
Helix can be installed on MacOS through homebrew:
```
brew tap helix-editor/helix
brew install helix
```

@ -1 +1 @@
22.05
22.08.1

@ -49,6 +49,7 @@ You may also specify a file to use for configuration with the `-c` or
| `auto-info` | Whether to display infoboxes | `true` |
| `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` |
| `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` |
| `bufferline` | Renders a line at the top of the editor displaying open buffers. Can be `always`, `never` or `multiple` (only shown if more than one buffer is in use) | `never` |
| `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` |
### `[editor.statusline]` Section

@ -4,7 +4,7 @@
| bash | ✓ | | | `bash-language-server` |
| beancount | ✓ | | | |
| c | ✓ | ✓ | ✓ | `clangd` |
| c-sharp | ✓ | | | `OmniSharp` |
| c-sharp | ✓ | | | `OmniSharp` |
| cairo | ✓ | | | |
| clojure | ✓ | | | `clojure-lsp` |
| cmake | ✓ | ✓ | ✓ | `cmake-language-server` |
@ -14,7 +14,7 @@
| css | ✓ | | | `vscode-css-language-server` |
| cue | ✓ | | | `cuelsp` |
| dart | ✓ | | ✓ | `dart` |
| devicetree | ✓ | | | |
| devicetree | ✓ | | | |
| dockerfile | ✓ | | | `docker-langserver` |
| dot | ✓ | | | `dot-language-server` |
| edoc | ✓ | | | |
@ -28,7 +28,7 @@
| esdl | ✓ | | | |
| fish | ✓ | ✓ | ✓ | |
| fortran | ✓ | | ✓ | `fortls` |
| gdscript | ✓ | | | |
| gdscript | ✓ | | | |
| git-attributes | ✓ | | | |
| git-commit | ✓ | | | |
| git-config | ✓ | | | |
@ -42,7 +42,7 @@
| gotmpl | ✓ | | | `gopls` |
| gowork | ✓ | | | `gopls` |
| graphql | ✓ | | | |
| hare | ✓ | | | |
| hare | ✓ | | | |
| haskell | ✓ | | | `haskell-language-server-wrapper` |
| hcl | ✓ | | ✓ | `terraform-ls` |
| heex | ✓ | ✓ | | |
@ -69,7 +69,7 @@
| meson | ✓ | | ✓ | |
| mint | | | | `mint` |
| nickel | ✓ | | ✓ | `nls` |
| nix | ✓ | | | `rnix-lsp` |
| nix | ✓ | | | `rnix-lsp` |
| nu | ✓ | | | |
| ocaml | ✓ | | ✓ | `ocamllsp` |
| ocaml-interface | ✓ | | | `ocamllsp` |
@ -99,7 +99,7 @@
| sql | ✓ | | | |
| sshclientconfig | ✓ | | | |
| starlark | ✓ | ✓ | | |
| svelte | ✓ | | | `svelteserver` |
| svelte | ✓ | | | `svelteserver` |
| swift | ✓ | | | `sourcekit-lsp` |
| tablegen | ✓ | ✓ | ✓ | |
| task | ✓ | | | |

@ -6,10 +6,9 @@ We provide pre-built binaries on the [GitHub Releases page](https://github.com/h
## OSX
A Homebrew tap is available:
Helix is available in homebrew-core:
```
brew tap helix-editor/helix
brew install helix
```

@ -125,7 +125,7 @@
| `Alt-(` | Rotate selection contents backward | `rotate_selection_contents_backward` |
| `Alt-)` | Rotate selection contents forward | `rotate_selection_contents_forward` |
| `%` | Select entire file | `select_all` |
| `x` | Select current line, if already selected, extend to next line | `extend_line` |
| `x` | Select current line, if already selected, extend to next line | `extend_line_below` |
| `X` | Extend selection to line bounds (line-wise selection) | `extend_to_line_bounds` |
| `Alt-x` | Shrink selection to line bounds (line-wise selection) | `shrink_to_line_bounds` |
| `J` | Join lines inside selection | `join_selections` |

@ -23,7 +23,7 @@ we'll use `<tag>` as a placeholder for the tag being published.
* Post to reddit
* [Example post](https://www.reddit.com/r/rust/comments/uzp5ze/helix_editor_2205_released/)
[homebrew formula]: https://github.com/helix-editor/homebrew-helix/blob/master/Formula/helix.rb
[homebrew formula]: https://github.com/Homebrew/homebrew-core/blob/master/Formula/helix.rb
## Changelog Curation

@ -659,7 +659,13 @@ pub fn select_on_matches(
let start = text.byte_to_char(start_byte + mat.start());
let end = text.byte_to_char(start_byte + mat.end());
result.push(Range::new(start, end));
let range = Range::new(start, end);
// Make sure the match is not right outside of the selection.
// These invalid matches can come from using RegEx anchors like `^`, `$`
if range != Range::point(sel.to()) {
result.push(range);
}
}
}
@ -929,6 +935,76 @@ mod test {
assert_eq!(Range::new(6, 5).min_width_1(s), Range::new(6, 5));
}
#[test]
fn test_select_on_matches() {
use crate::regex::{Regex, RegexBuilder};
let r = Rope::from_str("Nobody expects the Spanish inquisition");
let s = r.slice(..);
let selection = Selection::single(0, r.len_chars());
assert_eq!(
select_on_matches(s, &selection, &Regex::new(r"[A-Z][a-z]*").unwrap()),
Some(Selection::new(
smallvec![Range::new(0, 6), Range::new(19, 26)],
0
))
);
let r = Rope::from_str("This\nString\n\ncontains multiple\nlines");
let s = r.slice(..);
let start_of_line = RegexBuilder::new(r"^").multi_line(true).build().unwrap();
let end_of_line = RegexBuilder::new(r"$").multi_line(true).build().unwrap();
// line without ending
assert_eq!(
select_on_matches(s, &Selection::single(0, 4), &start_of_line),
Some(Selection::single(0, 0))
);
assert_eq!(
select_on_matches(s, &Selection::single(0, 4), &end_of_line),
None
);
// line with ending
assert_eq!(
select_on_matches(s, &Selection::single(0, 5), &start_of_line),
Some(Selection::single(0, 0))
);
assert_eq!(
select_on_matches(s, &Selection::single(0, 5), &end_of_line),
Some(Selection::single(4, 4))
);
// line with start of next line
assert_eq!(
select_on_matches(s, &Selection::single(0, 6), &start_of_line),
Some(Selection::new(
smallvec![Range::point(0), Range::point(5)],
0
))
);
assert_eq!(
select_on_matches(s, &Selection::single(0, 6), &end_of_line),
Some(Selection::single(4, 4))
);
// multiple lines
assert_eq!(
select_on_matches(
s,
&Selection::single(0, s.len_chars()),
&RegexBuilder::new(r"^[a-z ]*$")
.multi_line(true)
.build()
.unwrap()
),
Some(Selection::new(
smallvec![Range::point(12), Range::new(13, 30), Range::new(31, 36)],
0
))
);
}
#[test]
fn test_line_range() {
let r = Rope::from_str("\r\nHi\r\nthere!");

@ -334,7 +334,7 @@ impl TextObjectQuery {
}
}
fn read_query(language: &str, filename: &str) -> String {
pub fn read_query(language: &str, filename: &str) -> String {
static INHERITS_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r";+\s*inherits\s*:?\s*([a-z_,()-]+)\s*").unwrap());
@ -374,7 +374,8 @@ impl LanguageConfiguration {
&injections_query,
&locals_query,
)
.unwrap_or_else(|query_error| panic!("Could not parse queries for language {:?}. Are your grammars out of sync? Try running 'hx --grammar fetch' and 'hx --grammar build'. This query could not be parsed: {:?}", self.language_id, query_error));
.map_err(|err| log::error!("Could not parse queries for language {:?}. Are your grammars out of sync? Try running 'hx --grammar fetch' and 'hx --grammar build'. This query could not be parsed: {:?}", self.language_id, err))
.ok()?;
config.configure(scopes);
Some(Arc::new(config))
@ -399,28 +400,15 @@ impl LanguageConfiguration {
pub fn indent_query(&self) -> Option<&Query> {
self.indent_query
.get_or_init(|| {
let lang_name = self.language_id.to_ascii_lowercase();
let query_text = read_query(&lang_name, "indents.scm");
if query_text.is_empty() {
return None;
}
let lang = self.highlight_config.get()?.as_ref()?.language;
Query::new(lang, &query_text).ok()
})
.get_or_init(|| self.load_query("indents.scm"))
.as_ref()
}
pub fn textobject_query(&self) -> Option<&TextObjectQuery> {
self.textobject_query
.get_or_init(|| -> Option<TextObjectQuery> {
let lang_name = self.language_id.to_ascii_lowercase();
let query_text = read_query(&lang_name, "textobjects.scm");
let lang = self.highlight_config.get()?.as_ref()?.language;
let query = Query::new(lang, &query_text)
.map_err(|e| log::error!("Failed to parse textobjects.scm queries: {}", e))
.ok()?;
Some(TextObjectQuery { query })
.get_or_init(|| {
self.load_query("textobjects.scm")
.map(|query| TextObjectQuery { query })
})
.as_ref()
}
@ -428,6 +416,18 @@ impl LanguageConfiguration {
pub fn scope(&self) -> &str {
&self.scope
}
fn load_query(&self, kind: &str) -> Option<Query> {
let lang_name = self.language_id.to_ascii_lowercase();
let query_text = read_query(&lang_name, kind);
if query_text.is_empty() {
return None;
}
let lang = self.highlight_config.get()?.as_ref()?.language;
Query::new(lang, &query_text)
.map_err(|e| log::error!("Failed to parse {} queries for {}: {}", kind, lang_name, e))
.ok()
}
}
// Expose loader as Lazy<> global since it's always static?

@ -29,7 +29,10 @@ use std::{
use anyhow::{Context, Error};
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture, Event as CrosstermEvent},
event::{
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,
Event as CrosstermEvent,
},
execute, terminal,
tty::IsTty,
};
@ -82,6 +85,22 @@ fn setup_integration_logging() {
.apply();
}
fn restore_term() -> Result<(), Error> {
let mut stdout = stdout();
// reset cursor shape
write!(stdout, "\x1B[0 q")?;
// Ignore errors on disabling, this might trigger on windows if we call
// disable without calling enable previously
let _ = execute!(stdout, DisableMouseCapture);
execute!(
stdout,
DisableBracketedPaste,
terminal::LeaveAlternateScreen
)?;
terminal::disable_raw_mode()?;
Ok(())
}
impl Application {
pub fn new(args: Args, config: Config) -> Result<Self, Error> {
#[cfg(feature = "integration")]
@ -386,7 +405,7 @@ impl Application {
match signal {
signal::SIGTSTP => {
self.compositor.save_cursor();
self.restore_term().unwrap();
restore_term().unwrap();
low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
}
signal::SIGCONT => {
@ -425,14 +444,13 @@ impl Application {
scroll: None,
};
// Handle key events
let should_redraw = match event {
Ok(CrosstermEvent::Resize(width, height)) => {
let should_redraw = match event.unwrap() {
CrosstermEvent::Resize(width, height) => {
self.compositor.resize(width, height);
self.compositor
.handle_event(Event::Resize(width, height), &mut cx)
.handle_event(&Event::Resize(width, height), &mut cx)
}
Ok(event) => self.compositor.handle_event(event.into(), &mut cx),
Err(x) => panic!("{}", x),
event => self.compositor.handle_event(&event.into(), &mut cx),
};
if should_redraw && !self.editor.should_close() {
@ -510,7 +528,12 @@ impl Application {
use helix_core::diagnostic::{Diagnostic, Range, Severity::*};
use lsp::DiagnosticSeverity;
let language_server = doc.language_server().unwrap();
let language_server = if let Some(language_server) = doc.language_server() {
language_server
} else {
log::warn!("Discarding diagnostic because language server is not initialized: {:?}", diagnostic);
return None;
};
// TODO: convert inside server
let start = if let Some(start) = lsp_pos_to_pos(
@ -788,7 +811,7 @@ impl Application {
async fn claim_term(&mut self) -> Result<(), Error> {
terminal::enable_raw_mode()?;
let mut stdout = stdout();
execute!(stdout, terminal::EnterAlternateScreen)?;
execute!(stdout, terminal::EnterAlternateScreen, EnableBracketedPaste)?;
execute!(stdout, terminal::Clear(terminal::ClearType::All))?;
if self.config.load().editor.mouse {
execute!(stdout, EnableMouseCapture)?;
@ -796,18 +819,6 @@ impl Application {
Ok(())
}
fn restore_term(&mut self) -> Result<(), Error> {
let mut stdout = stdout();
// reset cursor shape
write!(stdout, "\x1B[0 q")?;
// Ignore errors on disabling, this might trigger on windows if we call
// disable without calling enable previously
let _ = execute!(stdout, DisableMouseCapture);
execute!(stdout, terminal::LeaveAlternateScreen)?;
terminal::disable_raw_mode()?;
Ok(())
}
pub async fn run<S>(&mut self, input_stream: &mut S) -> Result<i32, Error>
where
S: Stream<Item = crossterm::Result<crossterm::event::Event>> + Unpin,
@ -819,16 +830,14 @@ impl Application {
std::panic::set_hook(Box::new(move |info| {
// We can't handle errors properly inside this closure. And it's
// probably not a good idea to `unwrap()` inside a panic handler.
// So we just ignore the `Result`s.
let _ = execute!(std::io::stdout(), DisableMouseCapture);
let _ = execute!(std::io::stdout(), terminal::LeaveAlternateScreen);
let _ = terminal::disable_raw_mode();
// So we just ignore the `Result`.
let _ = restore_term();
hook(info);
}));
self.event_loop(input_stream).await;
self.close().await?;
self.restore_term()?;
restore_term()?;
Ok(self.editor.exit_code)
}

@ -247,7 +247,8 @@ impl MappableCommand {
extend_search_prev, "Add previous search match to selection",
search_selection, "Use current selection as search pattern",
global_search, "Global search in workspace folder",
extend_line, "Select current line, if already selected, extend to next line",
extend_line, "Select current line, if already selected, extend to another line based on the anchor",
extend_line_below, "Select current line, if already selected, extend to next line",
extend_line_above, "Select current line, if already selected, extend to previous line",
extend_to_line_bounds, "Extend selection to line bounds",
shrink_to_line_bounds, "Shrink selection to line bounds",
@ -588,7 +589,7 @@ fn goto_line_end(cx: &mut Context) {
goto_line_end_impl(
view,
doc,
if doc.mode == Mode::Select {
if cx.editor.mode == Mode::Select {
Movement::Extend
} else {
Movement::Move
@ -618,7 +619,7 @@ fn goto_line_end_newline(cx: &mut Context) {
goto_line_end_newline_impl(
view,
doc,
if doc.mode == Mode::Select {
if cx.editor.mode == Mode::Select {
Movement::Extend
} else {
Movement::Move
@ -649,7 +650,7 @@ fn goto_line_start(cx: &mut Context) {
goto_line_start_impl(
view,
doc,
if doc.mode == Mode::Select {
if cx.editor.mode == Mode::Select {
Movement::Extend
} else {
Movement::Move
@ -754,7 +755,7 @@ fn goto_first_nonwhitespace(cx: &mut Context) {
if let Some(pos) = find_first_non_whitespace_char(text.line(line)) {
let pos = pos + text.line_to_char(line);
range.put_cursor(text, pos, doc.mode == Mode::Select)
range.put_cursor(text, pos, cx.editor.mode == Mode::Select)
} else {
range
}
@ -954,7 +955,7 @@ where
let motion = move |editor: &mut Editor| {
let (view, doc) = current!(editor);
let text = doc.text().slice(..);
let behavior = if doc.mode == Mode::Select {
let behavior = if editor.mode == Mode::Select {
Movement::Extend
} else {
Movement::Move
@ -987,7 +988,7 @@ fn goto_file_start(cx: &mut Context) {
let selection = doc
.selection(view.id)
.clone()
.transform(|range| range.put_cursor(text, 0, doc.mode == Mode::Select));
.transform(|range| range.put_cursor(text, 0, cx.editor.mode == Mode::Select));
push_jump(view, doc);
doc.set_selection(view.id, selection);
}
@ -1000,7 +1001,7 @@ fn goto_file_end(cx: &mut Context) {
let selection = doc
.selection(view.id)
.clone()
.transform(|range| range.put_cursor(text, pos, doc.mode == Mode::Select));
.transform(|range| range.put_cursor(text, pos, cx.editor.mode == Mode::Select));
push_jump(view, doc);
doc.set_selection(view.id, selection);
}
@ -1377,7 +1378,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
if line != cursor.row {
let head = pos_at_coords(text, Position::new(line, cursor.col), true); // this func will properly truncate to line end
let anchor = if doc.mode == Mode::Select {
let anchor = if cx.editor.mode == Mode::Select {
range.anchor
} else {
head
@ -1948,6 +1949,15 @@ enum Extend {
}
fn extend_line(cx: &mut Context) {
let (view, doc) = current_ref!(cx.editor);
let extend = match doc.selection(view.id).primary().direction() {
Direction::Forward => Extend::Below,
Direction::Backward => Extend::Above,
};
extend_line_impl(cx, extend);
}
fn extend_line_below(cx: &mut Context) {
extend_line_impl(cx, Extend::Below);
}
@ -1963,20 +1973,32 @@ fn extend_line_impl(cx: &mut Context, extend: Extend) {
let selection = doc.selection(view.id).clone().transform(|range| {
let (start_line, end_line) = range.line_range(text.slice(..));
let start = text.line_to_char(start_line);
let end = text.line_to_char((end_line + count).min(text.len_lines()));
let start = text.line_to_char(match extend {
Extend::Above => start_line.saturating_sub(count),
Extend::Below => start_line,
});
let end = text.line_to_char(
match extend {
Extend::Above => end_line + 1, // the start of next line
Extend::Below => end_line + count,
}
.min(text.len_lines()),
);
// extend to previous/next line if current line is selected
let (anchor, head) = if range.from() == start && range.to() == end {
match extend {
Extend::Above => (end, text.line_to_char(start_line.saturating_sub(1))),
Extend::Above => (end, text.line_to_char(start_line.saturating_sub(count + 1))),
Extend::Below => (
start,
text.line_to_char((end_line + count + 1).min(text.len_lines())),
),
}
} else {
(start, end)
match extend {
Extend::Above => (end, start),
Extend::Below => (start, end),
}
};
Range::new(anchor, head)
@ -2079,7 +2101,7 @@ fn delete_selection_impl(cx: &mut Context, op: Operation) {
exit_select_mode(cx);
}
Operation::Change => {
enter_insert_mode(doc);
enter_insert_mode(cx);
}
}
}
@ -2148,14 +2170,14 @@ fn ensure_selections_forward(cx: &mut Context) {
doc.set_selection(view.id, selection);
}
fn enter_insert_mode(doc: &mut Document) {
doc.mode = Mode::Insert;
fn enter_insert_mode(cx: &mut Context) {
cx.editor.mode = Mode::Insert;
}
// inserts at the start of each selection
fn insert_mode(cx: &mut Context) {
enter_insert_mode(cx);
let (view, doc) = current!(cx.editor);
enter_insert_mode(doc);
log::trace!(
"entering insert mode with sel: {:?}, text: {:?}",
@ -2173,8 +2195,8 @@ fn insert_mode(cx: &mut Context) {
// inserts at the end of each selection
fn append_mode(cx: &mut Context) {
enter_insert_mode(cx);
let (view, doc) = current!(cx.editor);
enter_insert_mode(doc);
doc.restore_cursor = true;
let text = doc.text().slice(..);
@ -2439,9 +2461,9 @@ impl ui::menu::Item for MappableCommand {
pub fn command_palette(cx: &mut Context) {
cx.callback = Some(Box::new(
move |compositor: &mut Compositor, cx: &mut compositor::Context| {
let doc = doc_mut!(cx.editor);
let keymap =
compositor.find::<ui::EditorView>().unwrap().keymaps.map()[&doc.mode].reverse_map();
let keymap = compositor.find::<ui::EditorView>().unwrap().keymaps.map()
[&cx.editor.mode]
.reverse_map();
let mut commands: Vec<MappableCommand> = MappableCommand::STATIC_COMMAND_LIST.into();
commands.extend(typed::TYPABLE_COMMAND_LIST.iter().map(|cmd| {
@ -2482,14 +2504,13 @@ fn last_picker(cx: &mut Context) {
// I inserts at the first nonwhitespace character of each line with a selection
fn prepend_to_line(cx: &mut Context) {
goto_first_nonwhitespace(cx);
let doc = doc_mut!(cx.editor);
enter_insert_mode(doc);
enter_insert_mode(cx);
}
// A inserts at the end of each line with a selection
fn append_to_line(cx: &mut Context) {
enter_insert_mode(cx);
let (view, doc) = current!(cx.editor);
enter_insert_mode(doc);
let selection = doc.selection(view.id).clone().transform(|range| {
let text = doc.text().slice(..);
@ -2545,8 +2566,8 @@ pub enum Open {
fn open(cx: &mut Context, open: Open) {
let count = cx.count();
enter_insert_mode(cx);
let (view, doc) = current!(cx.editor);
enter_insert_mode(doc);
let text = doc.text().slice(..);
let contents = doc.text();
@ -2624,13 +2645,12 @@ fn open_above(cx: &mut Context) {
}
fn normal_mode(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
if doc.mode == Mode::Normal {
if cx.editor.mode == Mode::Normal {
return;
}
doc.mode = Mode::Normal;
cx.editor.mode = Mode::Normal;
let (view, doc) = current!(cx.editor);
try_restore_indent(doc, view.id);
@ -2708,7 +2728,7 @@ fn goto_line_impl(editor: &mut Editor, count: Option<NonZeroUsize>) {
let selection = doc
.selection(view.id)
.clone()
.transform(|range| range.put_cursor(text, pos, doc.mode == Mode::Select));
.transform(|range| range.put_cursor(text, pos, editor.mode == Mode::Select));
push_jump(view, doc);
doc.set_selection(view.id, selection);
@ -2728,7 +2748,7 @@ fn goto_last_line(cx: &mut Context) {
let selection = doc
.selection(view.id)
.clone()
.transform(|range| range.put_cursor(text, pos, doc.mode == Mode::Select));
.transform(|range| range.put_cursor(text, pos, cx.editor.mode == Mode::Select));
push_jump(view, doc);
doc.set_selection(view.id, selection);
@ -2751,7 +2771,7 @@ fn goto_last_modification(cx: &mut Context) {
let selection = doc
.selection(view.id)
.clone()
.transform(|range| range.put_cursor(text, pos, doc.mode == Mode::Select));
.transform(|range| range.put_cursor(text, pos, cx.editor.mode == Mode::Select));
doc.set_selection(view.id, selection);
}
}
@ -2788,13 +2808,12 @@ fn select_mode(cx: &mut Context) {
});
doc.set_selection(view.id, selection);
doc_mut!(cx.editor).mode = Mode::Select;
cx.editor.mode = Mode::Select;
}
fn exit_select_mode(cx: &mut Context) {
let doc = doc_mut!(cx.editor);
if doc.mode == Mode::Select {
doc.mode = Mode::Normal;
if cx.editor.mode == Mode::Select {
cx.editor.mode = Mode::Normal;
}
}
@ -3409,13 +3428,7 @@ enum Paste {
Cursor,
}
fn paste_impl(
values: &[String],
doc: &mut Document,
view: &View,
action: Paste,
count: usize,
) -> Option<Transaction> {
fn paste_impl(values: &[String], doc: &mut Document, view: &View, action: Paste, count: usize) {
let repeat = std::iter::repeat(
values
.last()
@ -3458,8 +3471,17 @@ fn paste_impl(
};
(pos, pos, values.next())
});
doc.apply(&transaction, view.id);
}
Some(transaction)
pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) {
let count = cx.count();
let paste = match cx.editor.mode {
Mode::Insert | Mode::Select => Paste::Cursor,
Mode::Normal => Paste::Before,
};
let (view, doc) = current!(cx.editor);
paste_impl(&[contents], doc, view, paste, count);
}
fn paste_clipboard_impl(
@ -3469,18 +3491,11 @@ fn paste_clipboard_impl(
count: usize,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor);
match editor
.clipboard_provider
.get_contents(clipboard_type)
.map(|contents| paste_impl(&[contents], doc, view, action, count))
{
Ok(Some(transaction)) => {
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
match editor.clipboard_provider.get_contents(clipboard_type) {
Ok(contents) => {
paste_impl(&[contents], doc, view, action, count);
Ok(())
}
Ok(None) => Ok(()),
Err(e) => Err(e.context("Couldn't get system clipboard contents")),
}
}
@ -3593,11 +3608,8 @@ fn paste(cx: &mut Context, pos: Paste) {
let (view, doc) = current!(cx.editor);
let registers = &mut cx.editor.registers;
if let Some(transaction) = registers
.read(reg_name)
.and_then(|values| paste_impl(values, doc, view, pos, count))
{
doc.apply(&transaction, view.id);
if let Some(values) = registers.read(reg_name) {
paste_impl(values, doc, view, pos, count);
}
}
@ -3863,8 +3875,7 @@ pub fn completion(cx: &mut Context) {
cx.callback(
future,
move |editor, compositor, response: Option<lsp::CompletionResponse>| {
let doc = doc!(editor);
if doc.mode() != Mode::Insert {
if editor.mode != Mode::Insert {
// we're not in insert mode anymore
return;
}
@ -4071,7 +4082,7 @@ fn match_brackets(cx: &mut Context) {
if let Some(pos) =
match_brackets::find_matching_bracket_fuzzy(syntax, doc.text(), range.cursor(text))
{
range.put_cursor(text, pos, doc.mode == Mode::Select)
range.put_cursor(text, pos, cx.editor.mode == Mode::Select)
} else {
range
}
@ -4085,13 +4096,18 @@ fn match_brackets(cx: &mut Context) {
fn jump_forward(cx: &mut Context) {
let count = cx.count();
let view = view_mut!(cx.editor);
let doc_id = view.doc;
if let Some((id, selection)) = view.jumps.forward(count) {
view.doc = *id;
let selection = selection.clone();
let (view, doc) = current!(cx.editor); // refetch doc
doc.set_selection(view.id, selection);
if doc.id() != doc_id {
view.add_to_history(doc_id);
}
doc.set_selection(view.id, selection);
align_view(doc, view, Align::Center);
};
}
@ -4099,13 +4115,18 @@ fn jump_forward(cx: &mut Context) {
fn jump_backward(cx: &mut Context) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
let doc_id = doc.id();
if let Some((id, selection)) = view.jumps.backward(view.id, doc, count) {
view.doc = *id;
let selection = selection.clone();
let (view, doc) = current!(cx.editor); // refetch doc
doc.set_selection(view.id, selection);
if doc.id() != doc_id {
view.add_to_history(doc_id);
}
doc.set_selection(view.id, selection);
align_view(doc, view, Align::Center);
};
}
@ -4266,26 +4287,33 @@ fn scroll_down(cx: &mut Context) {
scroll(cx, cx.count(), Direction::Forward);
}
fn goto_ts_object_impl(cx: &mut Context, object: &str, direction: Direction) {
fn goto_ts_object_impl(cx: &mut Context, object: &'static str, direction: Direction) {
let count = cx.count();
let (view, doc) = current!(cx.editor);
let text = doc.text().slice(..);
let range = doc.selection(view.id).primary();
let motion = move |editor: &mut Editor| {
let (view, doc) = current!(editor);
if let Some((lang_config, syntax)) = doc.language_config().zip(doc.syntax()) {
let text = doc.text().slice(..);
let root = syntax.tree().root_node();
let new_range = match doc.language_config().zip(doc.syntax()) {
Some((lang_config, syntax)) => movement::goto_treesitter_object(
text,
range,
object,
direction,
syntax.tree().root_node(),
lang_config,
count,
),
None => range,
};
let selection = doc.selection(view.id).clone().transform(|range| {
movement::goto_treesitter_object(
text,
range,
object,
direction,
root,
lang_config,
count,
)
});
doc.set_selection(view.id, Selection::single(new_range.anchor, new_range.head));
doc.set_selection(view.id, selection);
} else {
editor.set_status("Syntax-tree is not available in current buffer");
}
};
motion(cx.editor);
cx.editor.last_motion = Some(Motion(Box::new(motion)));
}
fn goto_next_function(cx: &mut Context) {
@ -4621,8 +4649,18 @@ fn shell_impl(
}
let output = process.wait_with_output()?;
if !output.stderr.is_empty() {
log::error!("Shell error: {}", String::from_utf8_lossy(&output.stderr));
if !output.status.success() {
if !output.stderr.is_empty() {
let err = String::from_utf8_lossy(&output.stderr).to_string();
log::error!("Shell error: {}", err);
bail!("Shell error: {}", err);
}
bail!("Shell command failed");
} else if !output.stderr.is_empty() {
log::debug!(
"Command printed to stderr: {}",
String::from_utf8_lossy(&output.stderr).to_string()
);
}
let str = std::str::from_utf8(&output.stdout)
@ -4889,7 +4927,7 @@ fn replay_macro(cx: &mut Context) {
cx.callback = Some(Box::new(move |compositor, cx| {
for _ in 0..count {
for &key in keys.iter() {
compositor.handle_event(compositor::Event::Key(key), cx);
compositor.handle_event(&compositor::Event::Key(key), cx);
}
}
// The macro under replay is cleared at the end of the callback, not in the

@ -582,7 +582,7 @@ pub fn dap_edit_condition(cx: &mut Context) {
None => return,
};
let callback = Box::pin(async move {
let call: Callback = Box::new(move |_editor, compositor| {
let call: Callback = Box::new(move |editor, compositor| {
let mut prompt = Prompt::new(
"condition:".into(),
None,
@ -607,7 +607,7 @@ pub fn dap_edit_condition(cx: &mut Context) {
},
);
if let Some(condition) = breakpoint.condition {
prompt.insert_str(&condition)
prompt.insert_str(&condition, editor)
}
compositor.push(Box::new(prompt));
});
@ -624,7 +624,7 @@ pub fn dap_edit_log(cx: &mut Context) {
None => return,
};
let callback = Box::pin(async move {
let call: Callback = Box::new(move |_editor, compositor| {
let call: Callback = Box::new(move |editor, compositor| {
let mut prompt = Prompt::new(
"log-message:".into(),
None,
@ -648,7 +648,7 @@ pub fn dap_edit_log(cx: &mut Context) {
},
);
if let Some(log_message) = breakpoint.log_message {
prompt.insert_str(&log_message);
prompt.insert_str(&log_message, editor);
}
compositor.push(Box::new(prompt));
});

@ -863,10 +863,7 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
}
};
let doc = doc!(editor);
let language = doc
.language()
.and_then(|scope| scope.strip_prefix("source."))
.unwrap_or("");
let language = doc.language_name().unwrap_or("");
let signature = match response
.signatures

@ -29,7 +29,7 @@ pub struct Context<'a> {
pub trait Component: Any + AnyComponent {
/// Process input events, return true if handled.
fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult {
fn handle_event(&mut self, _event: &Event, _ctx: &mut Context) -> EventResult {
EventResult::Ignored(None)
}
// , args: ()
@ -157,10 +157,10 @@ impl Compositor {
Some(self.layers.remove(idx))
}
pub fn handle_event(&mut self, event: Event, cx: &mut Context) -> bool {
pub fn handle_event(&mut self, event: &Event, cx: &mut Context) -> bool {
// If it is a key event and a macro is being recorded, push the key event to the recording.
if let (Event::Key(key), Some((_, keys))) = (event, &mut cx.editor.macro_recording) {
keys.push(key);
keys.push(*key);
}
let mut callbacks = Vec::new();

@ -83,6 +83,33 @@ pub fn general() -> std::io::Result<()> {
Ok(())
}
pub fn clipboard() -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
let board = get_clipboard_provider();
match board.name().as_ref() {
"none" => {
writeln!(
stdout,
"{}",
"System clipboard provider: Not installed".red()
)?;
writeln!(
stdout,
" {}",
"For troubleshooting system clipboard issues, refer".red()
)?;
writeln!(stdout, " {}",
"https://github.com/helix-editor/helix/wiki/Troubleshooting#copypaste-fromto-system-clipboard-not-working"
.red().underlined())?;
}
name => writeln!(stdout, "System clipboard provider: {}", name)?,
}
Ok(())
}
pub fn languages_all() -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
@ -281,13 +308,15 @@ fn probe_treesitter_feature(lang: &str, feature: TsFeature) -> std::io::Result<(
pub fn print_health(health_arg: Option<String>) -> std::io::Result<()> {
match health_arg.as_deref() {
Some("all") => languages_all()?,
Some(lang) => language(lang.to_string())?,
None => {
Some("languages") => languages_all()?,
Some("clipboard") => clipboard()?,
None | Some("all") => {
general()?;
clipboard()?;
writeln!(std::io::stdout().lock())?;
languages_all()?;
}
Some(lang) => language(lang.to_string())?,
}
Ok(())
}

@ -85,7 +85,7 @@ pub fn default() -> HashMap<Mode, Keymap> {
"A-n" | "A-right" => select_next_sibling,
"%" => select_all,
"x" => extend_line,
"x" => extend_line_below,
"X" => extend_to_line_bounds,
"A-x" => shrink_to_line_bounds,

@ -61,8 +61,9 @@ ARGS:
FLAGS:
-h, --help Prints help information
--tutor Loads the tutorial
--health [LANG] Checks for potential errors in editor setup
If given, checks for config errors in language LANG
--health [CATEGORY] Checks for potential errors in editor setup
CATEGORY can be a language or one of 'clipboard', 'languages'
or 'all'. 'all' is the default if not specified.
-g, --grammar {{fetch|build}} Fetches or builds tree-sitter grammars listed in languages.toml
-c, --config <file> Specifies a file to use for configuration
-v Increases logging verbosity each use for up to 3 times

@ -298,7 +298,7 @@ impl Completion {
}
impl Component for Completion {
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
// let the Editor handle Esc instead
if let Event::Key(KeyEvent {
code: KeyCode::Esc, ..
@ -324,10 +324,7 @@ impl Component for Completion {
// option.documentation
let (view, doc) = current!(cx.editor);
let language = doc
.language()
.and_then(|scope| scope.strip_prefix("source."))
.unwrap_or("");
let language = doc.language_name().unwrap_or("");
let text = doc.text().slice(..);
let cursor_pos = doc.selection(view.id).primary().cursor(text);
let coords = helix_core::visual_coords_at_pos(text, cursor_pos, doc.tab_width());

@ -16,14 +16,14 @@ use helix_core::{
LineEnding, Position, Range, Selection, Transaction,
};
use helix_view::{
document::Mode,
document::{Mode, SCRATCH_BUFFER_NAME},
editor::{CompleteAction, CursorShapeConfig},
graphics::{Color, CursorKind, Modifier, Rect, Style},
input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind},
keyboard::{KeyCode, KeyModifiers},
Document, Editor, Theme, View,
};
use std::borrow::Cow;
use std::{borrow::Cow, path::PathBuf};
use tui::buffer::Buffer as Surface;
@ -124,7 +124,13 @@ impl EditorView {
let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused {
Box::new(syntax::merge(
highlights,
Self::doc_selection_highlights(doc, view, theme, &editor.config().cursor_shape),
Self::doc_selection_highlights(
editor.mode(),
doc,
view,
theme,
&editor.config().cursor_shape,
),
))
} else {
Box::new(highlights)
@ -299,6 +305,7 @@ impl EditorView {
/// Get highlight spans for selections in a document view.
pub fn doc_selection_highlights(
mode: Mode,
doc: &Document,
view: &View,
theme: &Theme,
@ -308,7 +315,6 @@ impl EditorView {
let selection = doc.selection(view.id);
let primary_idx = selection.primary_index();
let mode = doc.mode();
let cursorkind = cursor_shape_config.from_mode(mode);
let cursor_is_block = cursorkind == CursorKind::Block;
@ -615,6 +621,59 @@ impl EditorView {
}
}
/// Render bufferline at the top
pub fn render_bufferline(editor: &Editor, viewport: Rect, surface: &mut Surface) {
let scratch = PathBuf::from(SCRATCH_BUFFER_NAME); // default filename to use for scratch buffer
surface.clear_with(
viewport,
editor
.theme
.try_get("ui.bufferline.background")
.unwrap_or_else(|| editor.theme.get("ui.statusline")),
);
let bufferline_active = editor
.theme
.try_get("ui.bufferline.active")
.unwrap_or_else(|| editor.theme.get("ui.statusline.active"));
let bufferline_inactive = editor
.theme
.try_get("ui.bufferline")
.unwrap_or_else(|| editor.theme.get("ui.statusline.inactive"));
let mut x = viewport.x;
let current_doc = view!(editor).doc;
for doc in editor.documents() {
let fname = doc
.path()
.unwrap_or(&scratch)
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let style = if current_doc == doc.id() {
bufferline_active
} else {
bufferline_inactive
};
let text = format!(" {}{} ", fname, if doc.is_modified() { "[+]" } else { "" });
let used_width = viewport.x.saturating_sub(x);
let rem_width = surface.area.width.saturating_sub(used_width);
x = surface
.set_stringn(x, viewport.y, text, rem_width as usize, style)
.0;
if x >= surface.area.right() {
break;
}
}
}
pub fn render_gutter(
editor: &Editor,
doc: &Document,
@ -773,15 +832,50 @@ impl EditorView {
cxt: &mut commands::Context,
event: KeyEvent,
) -> Option<KeymapResult> {
let mut last_mode = mode;
let key_result = self.keymaps.get(mode, event);
cxt.editor.autoinfo = self.keymaps.sticky().map(|node| node.infobox());
let mut execute_command = |command: &commands::MappableCommand| {
command.execute(cxt);
let current_mode = cxt.editor.mode();
match (last_mode, current_mode) {
(Mode::Normal, Mode::Insert) => {
// HAXX: if we just entered insert mode from normal, clear key buf
// and record the command that got us into this mode.
// how we entered insert mode is important, and we should track that so
// we can repeat the side effect.
self.last_insert.0 = command.clone();
self.last_insert.1.clear();
commands::signature_help_impl(cxt, commands::SignatureHelpInvoked::Automatic);
}
(Mode::Insert, Mode::Normal) => {
// if exiting insert mode, remove completion
self.completion = None;
// TODO: Use an on_mode_change hook to remove signature help
cxt.jobs.callback(async {
let call: job::Callback = Box::new(|_editor, compositor| {
compositor.remove(SignatureHelp::ID);
});
Ok(call)
});
}
_ => (),
}
last_mode = current_mode;
};
match &key_result {
KeymapResult::Matched(command) => command.execute(cxt),
KeymapResult::Matched(command) => {
execute_command(command);
}
KeymapResult::Pending(node) => cxt.editor.autoinfo = Some(node.infobox()),
KeymapResult::MatchedSequence(commands) => {
for command in commands {
command.execute(cxt);
execute_command(command);
}
}
KeymapResult::NotFound | KeymapResult::Cancelled(_) => return Some(key_result),
@ -915,8 +1009,8 @@ impl EditorView {
pub fn handle_idle_timeout(&mut self, cx: &mut crate::compositor::Context) -> EventResult {
if self.completion.is_some()
|| cx.editor.mode != Mode::Insert
|| !cx.editor.config().auto_completion
|| doc!(cx.editor).mode != Mode::Insert
{
return EventResult::Ignored(None);
}
@ -938,7 +1032,7 @@ impl EditorView {
impl EditorView {
fn handle_mouse_event(
&mut self,
event: MouseEvent,
event: &MouseEvent,
cxt: &mut commands::Context,
) -> EventResult {
let config = cxt.editor.config();
@ -948,7 +1042,7 @@ impl EditorView {
column,
modifiers,
..
} = event;
} = *event;
let pos_and_view = |editor: &Editor, row, column| {
editor.tree.views().find_map(|(view, _focus)| {
@ -1117,7 +1211,7 @@ impl EditorView {
impl Component for EditorView {
fn handle_event(
&mut self,
event: Event,
event: &Event,
context: &mut crate::compositor::Context,
) -> EventResult {
if let Some(explore) = self.explorer.as_mut() {
@ -1135,6 +1229,24 @@ impl Component for EditorView {
};
match event {
Event::Paste(contents) => {
cx.count = cx.editor.count;
commands::paste_bracketed_value(&mut cx, contents.clone());
cx.editor.count = None;
let config = cx.editor.config();
let mode = cx.editor.mode();
let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, config.scrolloff);
// Store a history state if not in insert mode. Otherwise wait till we exit insert
// to include any edits to the paste in the history state.
if mode != Mode::Insert {
doc.append_changes_to_history(view.id);
}
EventResult::Consumed(None)
}
Event::Resize(_width, _height) => {
// Ignore this event, we handle resizing just before rendering to screen.
// Handling it here but not re-rendering will cause flashing
@ -1147,8 +1259,9 @@ impl Component for EditorView {
// clear status
cx.editor.status_msg = None;
let doc = doc!(cx.editor);
let mode = doc.mode();
let mode = cx.editor.mode();
let (view, _) = current!(cx.editor);
let focus = view.id;
if let Some(on_next_key) = self.on_next_key.take() {
// if there's a command waiting input, do that first
@ -1210,48 +1323,20 @@ impl Component for EditorView {
if cx.editor.should_close() {
return EventResult::Ignored(None);
}
let config = cx.editor.config();
let (view, doc) = current!(cx.editor);
view.ensure_cursor_in_view(doc, config.scrolloff);
// Store a history state if not in insert mode. This also takes care of
// committing changes when leaving insert mode.
if doc.mode() != Mode::Insert {
doc.append_changes_to_history(view.id);
}
// mode transitions
match (mode, doc.mode()) {
(Mode::Normal, Mode::Insert) => {
// HAXX: if we just entered insert mode from normal, clear key buf
// and record the command that got us into this mode.
// how we entered insert mode is important, and we should track that so
// we can repeat the side effect.
self.last_insert.0 = match self.keymaps.get(mode, key) {
KeymapResult::Matched(command) => command,
// FIXME: insert mode can only be entered through single KeyCodes
_ => unimplemented!(),
};
self.last_insert.1.clear();
commands::signature_help_impl(
&mut cx,
commands::SignatureHelpInvoked::Automatic,
);
}
(Mode::Insert, Mode::Normal) => {
// if exiting insert mode, remove completion
self.completion = None;
// TODO: Use an on_mode_change hook to remove signature help
context.jobs.callback(async {
let call: job::Callback = Box::new(|_editor, compositor| {
compositor.remove(SignatureHelp::ID);
});
Ok(call)
});
// if the focused view still exists and wasn't closed
if cx.editor.tree.contains(focus) {
let config = cx.editor.config();
let mode = cx.editor.mode();
let view = cx.editor.tree.get_mut(focus);
let doc = cx.editor.documents.get_mut(&view.doc).unwrap();
view.ensure_cursor_in_view(doc, config.scrolloff);
// Store a history state if not in insert mode. This also takes care of
// committing changes when leaving insert mode.
if mode != Mode::Insert {
doc.append_changes_to_history(view.id);
}
_ => (),
}
EventResult::Consumed(callback)
@ -1266,8 +1351,22 @@ impl Component for EditorView {
// clear with background color
surface.set_style(area, cx.editor.theme.get("ui.background"));
let config = cx.editor.config();
// if the terminal size suddenly changed, we need to trigger a resize
// check if bufferline should be rendered
use helix_view::editor::BufferLine;
let use_bufferline = match config.bufferline {
BufferLine::Always => true,
BufferLine::Multiple if cx.editor.documents.len() > 1 => true,
_ => false,
};
// -1 for commandline and -1 for bufferline
let mut editor_area = area.clip_bottom(1);
if use_bufferline {
editor_area = editor_area.clip_top(1);
}
// if the terminal size suddenly changed, we need to trigger a resize
if self.explorer.is_some() && (config.explorer.is_embed()) {
editor_area = editor_area.clip_left(config.explorer.column_width as u16 + 2);
}
@ -1278,6 +1377,11 @@ impl Component for EditorView {
explore.content.render(area, surface, cx);
}
}
cx.editor.resize(editor_area);
if use_bufferline {
Self::render_bufferline(cx.editor, area.with_height(1), surface);
}
for (view, is_focused) in cx.editor.tree.views() {
let doc = cx.editor.document(view.doc).unwrap();

@ -244,7 +244,7 @@ pub struct Explorer {
state: State,
prompt: Option<(PromptAction, Prompt)>,
#[allow(clippy::type_complexity)]
on_next_key: Option<Box<dyn FnMut(&mut Context, &mut Self, KeyEvent) -> EventResult>>,
on_next_key: Option<Box<dyn FnMut(&mut Context, &mut Self, &KeyEvent) -> EventResult>>,
#[allow(clippy::type_complexity)]
repeat_motion: Option<Box<dyn FnMut(&mut Self, PromptAction, &mut Context) + 'static>>,
}
@ -552,24 +552,26 @@ impl Explorer {
}
}
fn handle_filter_event(&mut self, event: KeyEvent, cx: &mut Context) -> EventResult {
fn handle_filter_event(&mut self, event: &KeyEvent, cx: &mut Context) -> EventResult {
let (action, mut prompt) = self.prompt.take().unwrap();
match event.into() {
match event {
key!(Tab) | key!(Down) | ctrl!('j') => {
self.tree.clean_recycle();
return self
.tree
.handle_event(Event::Key(event), cx, &mut self.state);
.handle_event(Event::Key(event.clone()), cx, &mut self.state);
}
key!(Enter) => {
self.tree.clean_recycle();
return self
.tree
.handle_event(Event::Key(event), cx, &mut self.state);
.handle_event(Event::Key(event.clone()), cx, &mut self.state);
}
key!(Esc) | ctrl!('c') => self.tree.restore_recycle(),
_ => {
if let EventResult::Consumed(_) = prompt.handle_event(Event::Key(event), cx) {
if let EventResult::Consumed(_) =
prompt.handle_event(&Event::Key(event.clone()), cx)
{
self.tree.filter(prompt.line(), cx, &mut self.state);
}
self.prompt = Some((action, prompt));
@ -578,17 +580,17 @@ impl Explorer {
EventResult::Consumed(None)
}
fn handle_search_event(&mut self, event: KeyEvent, cx: &mut Context) -> EventResult {
fn handle_search_event(&mut self, event: &KeyEvent, cx: &mut Context) -> EventResult {
let (action, mut prompt) = self.prompt.take().unwrap();
let search_next = match action {
PromptAction::Search(search_next) => search_next,
_ => return EventResult::Ignored(None),
};
match event.into() {
match event {
key!(Tab) | key!(Down) | ctrl!('j') => {
return self
.tree
.handle_event(Event::Key(event), cx, &mut self.state)
.handle_event(Event::Key(event.clone()), cx, &mut self.state)
}
key!(Enter) => {
let search_str = prompt.line().clone();
@ -612,11 +614,13 @@ impl Explorer {
}
return self
.tree
.handle_event(Event::Key(event), cx, &mut self.state);
.handle_event(Event::Key(event.clone()), cx, &mut self.state);
}
key!(Esc) | ctrl!('c') => self.tree.restore_view(),
_ => {
if let EventResult::Consumed(_) = prompt.handle_event(Event::Key(event), cx) {
if let EventResult::Consumed(_) =
prompt.handle_event(&Event::Key(event.clone()), cx)
{
if search_next {
self.tree.search_next(cx, prompt.line(), &mut self.state);
} else {
@ -629,7 +633,7 @@ impl Explorer {
EventResult::Consumed(None)
}
fn handle_prompt_event(&mut self, event: KeyEvent, cx: &mut Context) -> EventResult {
fn handle_prompt_event(&mut self, event: &KeyEvent, cx: &mut Context) -> EventResult {
match &self.prompt {
Some((PromptAction::Search(_), _)) => return self.handle_search_event(event, cx),
Some((PromptAction::Filter, _)) => return self.handle_filter_event(event, cx),
@ -640,7 +644,7 @@ impl Explorer {
_ => return EventResult::Ignored(None),
};
let line = prompt.line();
match (action, event.into()) {
match (action, event) {
(PromptAction::Mkdir, key!(Enter)) => {
if let Err(e) = self.new_path(line, true) {
cx.editor.set_error(format!("{e}"))
@ -672,7 +676,7 @@ impl Explorer {
}
(_, key!(Esc) | ctrl!('c')) => {}
_ => {
prompt.handle_event(Event::Key(event), cx);
prompt.handle_event(&Event::Key(event.clone()), cx);
self.prompt = Some((action, prompt));
}
}
@ -714,7 +718,7 @@ impl Explorer {
impl Component for Explorer {
/// Process input events, return true if handled.
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let key_event = match event {
Event::Key(event) => event,
Event::Resize(..) => return EventResult::Consumed(None),
@ -727,7 +731,7 @@ impl Component for Explorer {
return on_next_key(cx, self, key_event);
}
if let EventResult::Consumed(c) = self.handle_prompt_event(key_event, cx) {
if let EventResult::Consumed(c) = self.handle_prompt_event(&key_event, cx) {
return EventResult::Consumed(c);
}
@ -737,7 +741,7 @@ impl Component for Explorer {
}
})));
match key_event.into() {
match key_event {
key!(Esc) => self.unfocus(),
ctrl!('c') => return close_fn,
key!('n') => {
@ -768,7 +772,7 @@ impl Component for Explorer {
key!('?') => self.new_search_prompt(false),
key!('m') => {
self.on_next_key = Some(Box::new(|_, explorer, event| {
match event.into() {
match event {
key!('d') => explorer.new_mkdir_prompt(),
key!('f') => explorer.new_create_file_prompt(),
_ => return EventResult::Ignored(None),
@ -778,7 +782,7 @@ impl Component for Explorer {
}
key!('r') => {
self.on_next_key = Some(Box::new(|cx, explorer, event| {
match event.into() {
match event {
key!('d') => explorer.new_remove_dir_prompt(cx),
key!('f') => explorer.new_remove_file_prompt(cx),
_ => return EventResult::Ignored(None),
@ -788,7 +792,7 @@ impl Component for Explorer {
}
_ => {
self.tree
.handle_event(Event::Key(key_event), cx, &mut self.state);
.handle_event(Event::Key(key_event.clone()), cx, &mut self.state);
}
}

@ -225,9 +225,9 @@ impl<T: Item> Menu<T> {
use super::PromptEvent as MenuEvent;
impl<T: Item + 'static> Component for Menu<T> {
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let event = match event {
Event::Key(event) => event,
Event::Key(event) => *event,
_ => return EventResult::Ignored(None),
};

@ -39,10 +39,10 @@ pub fn prompt(
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
callback_fn: impl FnMut(&mut crate::compositor::Context, &str, PromptEvent) + 'static,
) {
show_prompt(
cx,
Prompt::new(prompt, history_register, completion_fn, callback_fn),
);
let mut prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn);
// Calculate the initial completion
prompt.recalculate_completion(cx.editor);
cx.push_layer(Box::new(prompt));
}
pub fn prompt_with_input(
@ -53,15 +53,8 @@ pub fn prompt_with_input(
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
callback_fn: impl FnMut(&mut crate::compositor::Context, &str, PromptEvent) + 'static,
) {
show_prompt(
cx,
Prompt::new(prompt, history_register, completion_fn, callback_fn).with_line(input),
);
}
fn show_prompt(cx: &mut crate::commands::Context, mut prompt: Prompt) {
// Calculate initial completion
prompt.recalculate_completion(cx.editor);
let prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn)
.with_line(input, cx.editor);
cx.push_layer(Box::new(prompt));
}
@ -385,7 +378,7 @@ pub mod completers {
}
// TODO: we could return an iter/lazy thing so it can fetch as many as it needs.
fn filename_impl<F>(editor: &Editor, input: &str, filter_fn: F) -> Vec<Completion>
fn filename_impl<F>(_editor: &Editor, input: &str, filter_fn: F) -> Vec<Completion>
where
F: Fn(&ignore::DirEntry) -> FileMatch,
{
@ -417,7 +410,7 @@ pub mod completers {
let mut files: Vec<_> = WalkBuilder::new(&dir)
.hidden(false)
.follow_links(editor.config().file_picker.follow_symlinks)
.follow_links(false) // We're scanning over depth 1
.max_depth(Some(1))
.build()
.filter_map(|file| {

@ -61,7 +61,7 @@ impl<T: Component + 'static> Component for Overlay<T> {
Some((width, height))
}
fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, ctx: &mut Context) -> EventResult {
self.content.handle_event(event, ctx)
}

@ -260,7 +260,7 @@ impl<T: Item + 'static> Component for FilePicker<T> {
}
}
fn handle_event(&mut self, event: Event, ctx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, ctx: &mut Context) -> EventResult {
// TODO: keybinds for scrolling preview
self.picker.handle_event(event, ctx)
}
@ -470,12 +470,20 @@ impl<T: Item> Picker<T> {
self.filters
.extend(self.matches.iter().map(|(index, _)| *index));
self.filters.sort_unstable(); // used for binary search later
self.prompt.clear(cx);
self.prompt.clear(cx.editor);
}
pub fn toggle_preview(&mut self) {
self.show_preview = !self.show_preview;
}
fn prompt_handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
// TODO: recalculate only if pattern changed
self.score();
}
EventResult::Consumed(None)
}
}
// process:
@ -489,9 +497,10 @@ impl<T: Item + 'static> Component for Picker<T> {
Some(viewport)
}
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let key_event = match event {
Event::Key(event) => event,
Event::Key(event) => *event,
Event::Paste(..) => return self.prompt_handle_event(event, cx),
Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored(None),
};
@ -548,10 +557,7 @@ impl<T: Item + 'static> Component for Picker<T> {
self.toggle_preview();
}
_ => {
if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
// TODO: recalculate only if pattern changed
self.score();
}
self.prompt_handle_event(event, cx);
}
}

@ -138,9 +138,9 @@ impl<T: Component> Popup<T> {
}
impl<T: Component> Component for Popup<T> {
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let key = match event {
Event::Key(event) => event,
Event::Key(event) => *event,
Event::Resize(_, _) => {
// TODO: calculate inner area, call component's handle_event with that area
return EventResult::Ignored(None);

@ -83,10 +83,11 @@ impl Prompt {
}
}
pub fn with_line(mut self, line: String) -> Self {
pub fn with_line(mut self, line: String, editor: &Editor) -> Self {
let cursor = line.len();
self.line = line;
self.cursor = cursor;
self.recalculate_completion(editor);
self
}
@ -95,6 +96,7 @@ impl Prompt {
}
pub fn recalculate_completion(&mut self, editor: &Editor) {
self.exit_selection();
self.completion = (self.completion_fn)(editor, &self.line);
}
@ -213,12 +215,12 @@ impl Prompt {
self.cursor = pos;
}
self.recalculate_completion(cx.editor);
self.exit_selection();
}
pub fn insert_str(&mut self, s: &str) {
pub fn insert_str(&mut self, s: &str, editor: &Editor) {
self.line.insert_str(self.cursor, s);
self.cursor += s.len();
self.recalculate_completion(editor);
}
pub fn move_cursor(&mut self, movement: Movement) {
@ -234,65 +236,65 @@ impl Prompt {
self.cursor = self.line.len();
}
pub fn delete_char_backwards(&mut self, cx: &Context) {
pub fn delete_char_backwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::BackwardChar(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}
pub fn delete_char_forwards(&mut self, cx: &Context) {
pub fn delete_char_forwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::ForwardChar(1));
self.line.replace_range(self.cursor..pos, "");
self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}
pub fn delete_word_backwards(&mut self, cx: &Context) {
pub fn delete_word_backwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::BackwardWord(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}
pub fn delete_word_forwards(&mut self, cx: &Context) {
pub fn delete_word_forwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::ForwardWord(1));
self.line.replace_range(self.cursor..pos, "");
self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}
pub fn kill_to_start_of_line(&mut self, cx: &Context) {
pub fn kill_to_start_of_line(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::StartOfLine);
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}
pub fn kill_to_end_of_line(&mut self, cx: &Context) {
pub fn kill_to_end_of_line(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::EndOfLine);
self.line.replace_range(self.cursor..pos, "");
self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}
pub fn clear(&mut self, cx: &Context) {
pub fn clear(&mut self, editor: &Editor) {
self.line.clear();
self.cursor = 0;
self.recalculate_completion(cx.editor);
self.exit_selection();
self.recalculate_completion(editor);
}
pub fn change_history(&mut self, register: &[String], direction: CompletionDirection) {
pub fn change_history(
&mut self,
cx: &mut Context,
register: char,
direction: CompletionDirection,
) {
let register = cx.editor.registers.get_mut(register).read();
if register.is_empty() {
return;
}
@ -312,6 +314,7 @@ impl Prompt {
self.history_pos = Some(index);
self.move_end();
self.recalculate_completion(cx.editor);
}
pub fn change_completion_selection(&mut self, direction: CompletionDirection) {
@ -466,9 +469,14 @@ impl Prompt {
}
impl Component for Prompt {
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
let event = match event {
Event::Key(event) => event,
Event::Paste(data) => {
self.insert_str(data, cx.editor);
self.recalculate_completion(cx.editor);
return EventResult::Consumed(None);
}
Event::Key(event) => *event,
Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored(None),
};
@ -489,16 +497,18 @@ impl Component for Prompt {
ctrl!('f') | key!(Right) => self.move_cursor(Movement::ForwardChar(1)),
ctrl!('e') | key!(End) => self.move_end(),
ctrl!('a') | key!(Home) => self.move_start(),
ctrl!('w') | alt!(Backspace) | ctrl!(Backspace) => self.delete_word_backwards(cx),
alt!('d') | alt!(Delete) | ctrl!(Delete) => self.delete_word_forwards(cx),
ctrl!('k') => self.kill_to_end_of_line(cx),
ctrl!('u') => self.kill_to_start_of_line(cx),
ctrl!('w') | alt!(Backspace) | ctrl!(Backspace) => {
self.delete_word_backwards(cx.editor)
}
alt!('d') | alt!(Delete) | ctrl!(Delete) => self.delete_word_forwards(cx.editor),
ctrl!('k') => self.kill_to_end_of_line(cx.editor),
ctrl!('u') => self.kill_to_start_of_line(cx.editor),
ctrl!('h') | key!(Backspace) => {
self.delete_char_backwards(cx);
self.delete_char_backwards(cx.editor);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('d') | key!(Delete) => {
self.delete_char_forwards(cx);
self.delete_char_forwards(cx.editor);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('s') => {
@ -515,27 +525,32 @@ impl Component for Prompt {
);
let line = text.slice(range.from()..range.to()).to_string();
if !line.is_empty() {
self.insert_str(line.as_str());
self.insert_str(line.as_str(), cx.editor);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
key!(Enter) => {
if self.selection.is_some() && self.line.ends_with(std::path::MAIN_SEPARATOR) {
self.recalculate_completion(cx.editor);
self.exit_selection();
} else {
let last_item = self
.history_register
.and_then(|reg| cx.editor.registers.last(reg).cloned())
.map(|entry| entry.into())
.unwrap_or_else(|| Cow::from(""));
// handle executing with last command in history if nothing entered
let input: Cow<str> = if self.line.is_empty() {
// latest value in the register list
self.history_register
.and_then(|reg| cx.editor.registers.last(reg).cloned())
.map(|entry| entry.into())
.unwrap_or_else(|| Cow::from(""))
last_item
} else {
if let Some(register) = self.history_register {
if last_item != self.line {
// store in history
let register = cx.editor.registers.get_mut(register);
register.push(self.line.clone());
if let Some(register) = self.history_register {
cx.editor
.registers
.get_mut(register)
.push(self.line.clone());
};
}
self.line.as_str().into()
@ -548,15 +563,13 @@ impl Component for Prompt {
}
ctrl!('p') | key!(Up) => {
if let Some(register) = self.history_register {
let register = cx.editor.registers.get_mut(register);
self.change_history(register.read(), CompletionDirection::Backward);
self.change_history(cx, register, CompletionDirection::Backward);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
ctrl!('n') | key!(Down) => {
if let Some(register) = self.history_register {
let register = cx.editor.registers.get_mut(register);
self.change_history(register.read(), CompletionDirection::Forward);
self.change_history(cx, register, CompletionDirection::Forward);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
@ -565,7 +578,6 @@ impl Component for Prompt {
// if single completion candidate is a directory list content in completion
if self.completion.len() == 1 && self.line.ends_with(std::path::MAIN_SEPARATOR) {
self.recalculate_completion(cx.editor);
self.exit_selection();
}
(self.callback_fn)(cx, &self.line, PromptEvent::Update)
}
@ -597,8 +609,8 @@ impl Component for Prompt {
.read(c)
.and_then(|r| r.first())
.map_or("", |r| r.as_str()),
context.editor,
);
prompt.recalculate_completion(context.editor);
}));
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
return EventResult::Consumed(None);

@ -160,7 +160,7 @@ where
format!(
" {} ",
if visible {
match context.doc.mode() {
match context.editor.mode() {
Mode::Insert => "INS",
Mode::Select => "SEL",
Mode::Normal => "NOR",
@ -171,7 +171,7 @@ where
}
),
if visible && context.editor.config().color_modes {
match context.doc.mode() {
match context.editor.mode() {
Mode::Insert => Some(context.editor.theme.get("ui.statusline.insert")),
Mode::Select => Some(context.editor.theme.get("ui.statusline.select")),
Mode::Normal => Some(context.editor.theme.get("ui.statusline.normal")),
@ -329,7 +329,7 @@ fn render_file_type<F>(context: &mut RenderContext, write: F)
where
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
{
let file_type = context.doc.language_id().unwrap_or("text");
let file_type = context.doc.language_name().unwrap_or("text");
write(context, format!(" {} ", file_type), None);
}

@ -21,5 +21,6 @@ mod test {
mod auto_pairs;
mod commands;
mod movement;
mod prompt;
mod write;
}

@ -0,0 +1,18 @@
use super::*;
use helix_term::application::Application;
#[tokio::test]
async fn test_history_completion() -> anyhow::Result<()> {
test_key_sequence(
&mut Application::new(Args::default(), Config::default())?,
Some(":asdf<ret>:theme d<C-n><tab>"),
Some(&|app| {
assert!(!app.editor.is_err());
}),
false,
)
.await?;
Ok(())
}

@ -90,8 +90,6 @@ pub struct Document {
path: Option<PathBuf>,
encoding: &'static encoding::Encoding,
/// Current editing mode.
pub mode: Mode,
pub restore_cursor: bool,
/// Current indent style.
@ -133,7 +131,6 @@ impl fmt::Debug for Document {
.field("selections", &self.selections)
.field("path", &self.path)
.field("encoding", &self.encoding)
.field("mode", &self.mode)
.field("restore_cursor", &self.restore_cursor)
.field("syntax", &self.syntax)
.field("language", &self.language)
@ -349,7 +346,6 @@ impl Document {
selections: HashMap::default(),
indent_style: DEFAULT_INDENT,
line_ending: DEFAULT_LINE_ENDING,
mode: Mode::Normal,
restore_cursor: false,
syntax: None,
language: None,
@ -440,17 +436,22 @@ impl Document {
.await
.map_err(|_| FormatterError::WaitForOutputFailed)?;
if !output.stderr.is_empty() {
return Err(FormatterError::Stderr(
String::from_utf8_lossy(&output.stderr).to_string(),
));
}
if !output.status.success() {
return Err(FormatterError::NonZeroExitStatus);
if !output.stderr.is_empty() {
let err = String::from_utf8_lossy(&output.stderr).to_string();
log::error!("Formatter error: {}", err);
return Err(FormatterError::NonZeroExitStatus(Some(err)));
}
return Err(FormatterError::NonZeroExitStatus(None));
} else if !output.stderr.is_empty() {
log::debug!(
"Formatter printed to stderr: {}",
String::from_utf8_lossy(&output.stderr).to_string()
);
}
let str = String::from_utf8(output.stdout)
let str = std::str::from_utf8(&output.stdout)
.map_err(|_| FormatterError::InvalidUtf8Output)?;
Ok(helix_core::diff::compare_ropes(&text, &Rope::from(str)))
@ -920,28 +921,33 @@ impl Document {
self.last_saved_revision = current_revision;
}
/// Current editing mode for the [`Document`].
pub fn mode(&self) -> Mode {
self.mode
}
/// Corresponding language scope name. Usually `source.<lang>`.
pub fn language(&self) -> Option<&str> {
pub fn language_scope(&self) -> Option<&str> {
self.language
.as_ref()
.map(|language| language.scope.as_str())
}
/// Language name for the document. Corresponds to the `name` key in
/// `languages.toml` configuration.
pub fn language_name(&self) -> Option<&str> {
self.language
.as_ref()
.map(|language| language.language_id.as_str())
}
/// Language ID for the document. Either the `language-id` from the
/// `language-server` configuration, or the document language if no
/// `language-id` has been specified.
pub fn language_id(&self) -> Option<&str> {
self.language_config()?
let language_config = self.language.as_deref()?;
language_config
.language_server
.as_ref()?
.language_id
.as_deref()
.or_else(|| Some(self.language()?.rsplit_once('.')?.1))
.or(Some(language_config.language_id.as_str()))
}
/// Corresponding [`LanguageConfiguration`].
@ -1092,10 +1098,9 @@ pub enum FormatterError {
},
BrokenStdin,
WaitForOutputFailed,
Stderr(String),
InvalidUtf8Output,
DiskReloadError(String),
NonZeroExitStatus,
NonZeroExitStatus(Option<String>),
}
impl std::error::Error for FormatterError {}
@ -1108,10 +1113,12 @@ impl Display for FormatterError {
}
Self::BrokenStdin => write!(f, "Could not write to formatter stdin"),
Self::WaitForOutputFailed => write!(f, "Waiting for formatter output failed"),
Self::Stderr(output) => write!(f, "Formatter error: {}", output),
Self::InvalidUtf8Output => write!(f, "Invalid UTF-8 formatter output"),
Self::DiskReloadError(error) => write!(f, "Error reloading file from disk: {}", error),
Self::NonZeroExitStatus => write!(f, "Formatter exited with non zero exit status:"),
Self::NonZeroExitStatus(Some(output)) => write!(f, "Formatter error: {}", output),
Self::NonZeroExitStatus(None) => {
write!(f, "Formatter exited with non zero exit status")
}
}
}
}

@ -225,6 +225,8 @@ pub struct Config {
pub rulers: Vec<u16>,
#[serde(default)]
pub whitespace: WhitespaceConfig,
/// Persistently display open buffers along the top
pub bufferline: BufferLine,
/// Vertical indent width guides.
pub indent_guides: IndentGuidesConfig,
/// Whether to color modes with different colors. Defaults to `false`.
@ -276,6 +278,13 @@ pub fn get_terminal_provider() -> Option<TerminalConfig> {
});
}
if env_var_is_set("WEZTERM_UNIX_SOCKET") && exists("wezterm") {
return Some(TerminalConfig {
command: "wezterm".to_string(),
args: vec!["cli".to_string(), "split-pane".to_string()],
});
}
None
}
@ -425,6 +434,24 @@ impl Default for CursorShapeConfig {
}
}
/// bufferline render modes
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum BufferLine {
/// Don't render bufferline
Never,
/// Always render
Always,
/// Only if multiple buffers are open
Multiple,
}
impl Default for BufferLine {
fn default() -> Self {
BufferLine::Never
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LineNumber {
@ -612,6 +639,7 @@ impl Default for Config {
terminal: get_terminal_provider(),
rulers: Vec::new(),
whitespace: WhitespaceConfig::default(),
bufferline: BufferLine::default(),
indent_guides: IndentGuidesConfig::default(),
color_modes: false,
explorer: ExplorerConfig::default(),
@ -654,6 +682,8 @@ pub struct Breakpoint {
}
pub struct Editor {
/// Current editing mode.
pub mode: Mode,
pub tree: Tree,
pub next_document_id: DocumentId,
pub documents: BTreeMap<DocumentId, Document>,
@ -736,6 +766,7 @@ impl Editor {
area.height -= 1;
Self {
mode: Mode::Normal,
tree: Tree::new(area),
next_document_id: DocumentId::default(),
documents: BTreeMap::new(),
@ -767,6 +798,11 @@ impl Editor {
}
}
/// Current editing mode for the [`Editor`].
pub fn mode(&self) -> Mode {
self.mode
}
pub fn config(&self) -> DynGuard<Config> {
self.config.load()
}
@ -1179,8 +1215,7 @@ impl Editor {
// if leaving the view: mode should reset
if prev_id != view_id {
let doc_id = self.tree.get(prev_id).doc;
self.documents.get_mut(&doc_id).unwrap().mode = Mode::Normal;
self.mode = Mode::Normal;
}
}
@ -1191,8 +1226,7 @@ impl Editor {
// if leaving the view: mode should reset
if prev_id != id {
let doc_id = self.tree.get(prev_id).doc;
self.documents.get_mut(&doc_id).unwrap().mode = Mode::Normal;
self.mode = Mode::Normal;
}
}
@ -1263,7 +1297,7 @@ impl Editor {
let inner = view.inner_area();
pos.col += inner.x as usize;
pos.row += inner.y as usize;
let cursorkind = config.cursor_shape.from_mode(doc.mode());
let cursorkind = config.cursor_shape.from_mode(self.mode);
(Some(pos), cursorkind)
} else {
(None, CursorKind::default())

@ -72,7 +72,7 @@ pub fn line_numbers<'doc>(
.char_to_line(doc.selection(view.id).primary().cursor(text));
let line_number = editor.config().line_number;
let mode = doc.mode;
let mode = editor.mode;
Box::new(move |line: usize, selected: bool, out: &mut String| {
if line == last_line && !draw_last {

@ -6,12 +6,13 @@ use std::fmt;
pub use crate::keyboard::{KeyCode, KeyModifiers};
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)]
pub enum Event {
FocusGained,
FocusLost,
Key(KeyEvent),
Mouse(MouseEvent),
Paste(String),
Resize(u16, u16),
}
@ -115,6 +116,8 @@ pub(crate) mod keys {
pub(crate) const ESC: &str = "esc";
pub(crate) const SPACE: &str = "space";
pub(crate) const MINUS: &str = "minus";
pub(crate) const LESS_THAN: &str = "lt";
pub(crate) const GREATER_THAN: &str = "gt";
}
impl fmt::Display for KeyEvent {
@ -155,6 +158,8 @@ impl fmt::Display for KeyEvent {
KeyCode::Esc => f.write_str(keys::ESC)?,
KeyCode::Char(' ') => f.write_str(keys::SPACE)?,
KeyCode::Char('-') => f.write_str(keys::MINUS)?,
KeyCode::Char('<') => f.write_str(keys::LESS_THAN)?,
KeyCode::Char('>') => f.write_str(keys::GREATER_THAN)?,
KeyCode::F(i) => f.write_fmt(format_args!("F{}", i))?,
KeyCode::Char(c) => f.write_fmt(format_args!("{}", c))?,
};
@ -227,6 +232,8 @@ impl std::str::FromStr for KeyEvent {
keys::ESC => KeyCode::Esc,
keys::SPACE => KeyCode::Char(' '),
keys::MINUS => KeyCode::Char('-'),
keys::LESS_THAN => KeyCode::Char('<'),
keys::GREATER_THAN => KeyCode::Char('>'),
single if single.chars().count() == 1 => KeyCode::Char(single.chars().next().unwrap()),
function if function.len() > 1 && function.starts_with('F') => {
let function: String = function.chars().skip(1).collect();
@ -276,9 +283,7 @@ impl From<crossterm::event::Event> for Event {
crossterm::event::Event::Resize(w, h) => Self::Resize(w, h),
crossterm::event::Event::FocusGained => Self::FocusGained,
crossterm::event::Event::FocusLost => Self::FocusLost,
crossterm::event::Event::Paste(_) => {
unreachable!("crossterm shouldn't emit Paste events without them being enabled")
}
crossterm::event::Event::Paste(s) => Self::Paste(s),
}
}
}
@ -307,7 +312,7 @@ impl From<crossterm::event::MouseEventKind> for MouseEventKind {
fn from(kind: crossterm::event::MouseEventKind) -> Self {
match kind {
crossterm::event::MouseEventKind::Down(button) => Self::Down(button.into()),
crossterm::event::MouseEventKind::Up(button) => Self::Down(button.into()),
crossterm::event::MouseEventKind::Up(button) => Self::Up(button.into()),
crossterm::event::MouseEventKind::Drag(button) => Self::Drag(button.into()),
crossterm::event::MouseEventKind::Moved => Self::Moved,
crossterm::event::MouseEventKind::ScrollDown => Self::ScrollDown,
@ -549,8 +554,6 @@ mod test {
#[test]
fn parsing_unsupported_named_keys() {
assert!(str::parse::<KeyEvent>("lt").is_err());
assert!(str::parse::<KeyEvent>("gt").is_err());
assert!(str::parse::<KeyEvent>("plus").is_err());
assert!(str::parse::<KeyEvent>("percent").is_err());
assert!(str::parse::<KeyEvent>("semicolon").is_err());

@ -270,10 +270,18 @@ impl Tree {
})
}
/// Get reference to a [`view`] by index.
/// # Panics
///
/// Panics if `index` is not in self.nodes, or if the node's content is not [`Content::View`] . This can be checked with [`contains`]
pub fn get(&self, index: ViewId) -> &View {
self.try_get(index).unwrap()
}
/// Try to get reference to a [`view`] by index. Returns `None` if node content is not a [`Content::View`]
/// # Panics
///
/// Panics if `index` is not in self.nodes. This can be checked with [`Self::contains`]
pub fn try_get(&self, index: ViewId) -> Option<&View> {
match &self.nodes[index] {
Node {
@ -284,6 +292,10 @@ impl Tree {
}
}
/// Get a mutable reference to a [`view`] by index.
/// # Panics
///
/// Panics if `index` is not in self.nodes, or if the node's content is not [`Content::View`] . This can be checked with [`Self::contains`]
pub fn get_mut(&mut self, index: ViewId) -> &mut View {
match &mut self.nodes[index] {
Node {
@ -294,6 +306,11 @@ impl Tree {
}
}
/// Check if tree contains a [`Node`] with a given index.
pub fn contains(&self, index: ViewId) -> bool {
self.nodes.contains_key(index)
}
pub fn is_empty(&self) -> bool {
match &self.nodes[self.root] {
Node {

@ -50,7 +50,7 @@ args = { attachCommands = [ "platform select remote-gdb-server", "platform conne
[[grammar]]
name = "rust"
source = { git = "https://github.com/tree-sitter/tree-sitter-rust", rev = "a360da0a29a19c281d08295a35ecd0544d2da211" }
source = { git = "https://github.com/tree-sitter/tree-sitter-rust", rev = "41e23b454f503e6fe63ec4b6d9f7f2cf7788ab8e" }
[[language]]
name = "toml"
@ -235,7 +235,7 @@ language-server = { command = "OmniSharp", args = [ "--languageserver" ] }
[[grammar]]
name = "c-sharp"
source = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "53a65a908167d6556e1fcdb67f1ee62aac101dda" }
source = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "9c494a503c8e2044bfffce57f70b480c01a82f03" }
[[language]]
name = "go"
@ -1118,6 +1118,14 @@ comment-token = "%%"
indent = { tab-width = 4, unit = " " }
language-server = { command = "erlang_ls" }
[language.auto-pairs]
'(' = ')'
'{' = '}'
'[' = ']'
'"' = '"'
"'" = "'"
'`' = "'"
[[grammar]]
name = "erlang"
source = { git = "https://github.com/the-mikedavis/tree-sitter-erlang", rev = "0e7d677d11a7379686c53c616825714ccb728059" }

@ -228,8 +228,19 @@
"let"
] @keyword
(preprocessor_call) @keyword.directive
(preprocessor_call (_) @keyword.directive)
(nullable_directive) @keyword.directive
(define_directive) @keyword.directive
(undef_directive) @keyword.directive
(if_directive) @keyword.directive
(else_directive) @keyword.directive
(elif_directive) @keyword.directive
(endif_directive) @keyword.directive
(region_directive) @keyword.directive
(endregion_directive) @keyword.directive
(error_directive) @keyword.directive
(warning_directive) @keyword.directive
(line_directive) @keyword.directive
(pragma_directive) @keyword.directive
;; Linq
(from_clause (identifier) @variable)

@ -0,0 +1,21 @@
[
(class_declaration body: (_) @class.inside)
(struct_declaration body: (_) @class.inside)
(interface_declaration body: (_) @class.inside)
(enum_declaration body: (_) @class.inside)
(delegate_declaration)
(record_declaration body: (_) @class.inside)
(record_struct_declaration body: (_) @class.inside)
] @class.around
(constructor_declaration body: (_) @function.inside) @function.around
(destructor_declaration body: (_) @function.inside) @function.around
(method_declaration body: (_) @function.inside) @function.around
(property_declaration (_) @function.inside) @function.around
(parameter (_) @parameter.inside) @parameter.around
(comment)+ @comment.around

@ -21,6 +21,7 @@
"union" @keyword
"volatile" @keyword
"while" @keyword
"const" @keyword
[
"#define"
@ -50,10 +51,14 @@
"==" @operator
">" @operator
"||" @operator
">=" @operator
"<=" @operator
"." @punctuation.delimiter
";" @punctuation.delimiter
[(true) (false)] @constant.builtin.boolean
(enumerator) @type.enum.variant
(string_literal) @string

@ -1,12 +0,0 @@
[
(node)
(byte_string_literal)
(parenthesized_expression)
(argument_list)
] @indent
[
"}"
"]"
")"
] @outdent

@ -18,7 +18,7 @@
.
[(atom) @type (macro)]
[
(tuple (atom) @variable.other.member)
(tuple (atom)? @variable.other.member)
(tuple
(binary_operator
left: (atom) @variable.other.member

@ -1,26 +0,0 @@
[
(_compound_statement)
(match_statement)
(parenthesized_expression)
(pattern_array)
(pattern_dictionary)
(argument_list)
(binary_operator)
(parameters)
(body)
(enumerator_list)
(function_definition)
(constructor_definition)
(class_definition)
] @indent
[
")",
"]",
"}",
(return_statement)
(pass_statement)
] @outdent

@ -1,21 +0,0 @@
[
(struct_union_fields)
(expression_list)
] @indent
[
"case"
"}"
"]"
")"
] @outdent
; [
; "{"
; "}"
; ] @branch
; [
; (comment)
; ] @ignore

@ -1,25 +0,0 @@
[
; Bracket like
(let)
(attrset)
(rec_attrset)
(let_attrset)
(parenthesized)
(list)
; Binding
(bind)
(inherit)
(inherit_from)
(formal)
; Binary operations
(binary)
(has_attr)
(select)
(app)
; Conditional
(if)
] @indent

@ -67,6 +67,7 @@
"]"
"{"
"}"
"#"
] @punctuation.bracket
(type_arguments
[

@ -1,58 +1,20 @@
(
[
(attribute_item)+
(line_comment)+
]*
.
(function_item
body: (_) @function.inside)) @function.around
(function_item
body: (_) @function.inside) @function.around(closure_expression body: (_) @function.inside) @function.around
(closure_expression body: (_) @function.inside) @function.around
(struct_item
body: (_) @class.inside) @class.around
(
[
(attribute_item)+
(line_comment)+
]*
.
(struct_item
body: (_) @class.inside)) @class.around
(enum_item
body: (_) @class.inside) @class.around
(
[
(attribute_item)+
(line_comment)+
]*
.
(enum_item
body: (_) @class.inside)) @class.around
(union_item
body: (_) @class.inside) @class.around
(
[
(attribute_item)+
(line_comment)+
]*
.
(union_item
body: (_) @class.inside)) @class.around
(trait_item
body: (_) @class.inside) @class.around
(
[
(attribute_item)+
(line_comment)+
]*
.
(trait_item
body: (_) @class.inside)) @class.around
(
[
(attribute_item)+
(line_comment)+
]*
.
(impl_item
body: (_) @class.inside)) @class.around
(impl_item
body: (_) @class.inside) @class.around
(parameters
((_) @parameter.inside . ","? @parameter.around) @parameter.around)

@ -1,19 +0,0 @@
; inherits: ecma
[
(element)
(if_statement)
(each_statement)
(await_statement)
] @indent
[
(end_tag)
(else_statement)
(if_end_expr)
(each_end_expr)
(await_end_expr)
">"
"/>"
] @outdent

@ -3,14 +3,20 @@
"ui.background" = {bg="acme_bg"}
"ui.text" = "black"
"ui.selection" = {bg="selected"}
"ui.statusline" = {bg="acme_bar_bg"}
"ui.statusline.inactive" = {bg="acme_bar_inactive"}
"ui.cursorline" = {bg="acme_bar_bg"}
"ui.statusline" = {fg="black", bg="acme_bar_bg"}
"ui.statusline.inactive" = {fg="black", bg="acme_bar_inactive"}
"ui.virtual" = "indent"
"ui.virtual.ruler" = { bg = "acme_bar_bg" }
"ui.cursor.match" = {bg="acme_bar_bg"}
"ui.cursor" = {bg="cursor", fg="white"}
"string" = "red"
"comment" = "green"
"ui.help" = {fg="black", bg="acme_bg"}
"ui.popup" = {fg="black", bg="acme_bg"}
"ui.menu" = {fg="black", bg="acme_bg"}
"ui.menu.selected" = {bg="selected"}
"ui.window" = {bg="acme_bg"}
"diagnostic.error" = {bg="white", modifiers=["bold"]}
"diagnostic.warning" = {bg="white", modifiers=["bold"]}
"diagnostic.hint" = {bg="white", modifiers=["bold"]}

@ -16,6 +16,7 @@
"ui.linenr.selected" = { fg = "my_gray6", bg = "my_gray1"}
"ui.selection" = { bg = "my_gray3" }
"comment" = { fg = "my_gray4", modifiers = ["italic"] }
"ui.cursorline" = { bg = "my_gray2" }
"ui.statusline" = { fg = "my_gray6", bg = "my_gray2" }
"ui.statusline.inactive" = { fg = 'my_gray4', bg = 'my_gray2' }
"ui.statusline.insert" = {fg = "my_black", bg = "my_gray5", modifiers = ["bold"]}

@ -16,6 +16,7 @@
"ui.linenr.selected" = { fg = "my_gray6", bg = "my_gray1"}
"ui.selection" = { bg = "my_gray3" }
"comment" = { fg = "my_gray4", modifiers = ["italic"] }
"ui.cursorline" = { bg = "my_gray2" }
"ui.statusline" = { fg = "my_gray6", bg = "my_gray2" }
"ui.statusline.inactive" = { fg = 'my_gray4', bg = 'my_gray2' }
"ui.statusline.insert" = {fg = "my_black", bg = "my_gray5", modifiers = ["bold"]}

@ -38,16 +38,17 @@
"ui.cursor.match" = { fg = "orange" }
"ui.linenr" = { fg = "dark_gray" }
"ui.linenr.selected" = { fg = "orange" }
"ui.statusline" = { bg = "black" }
"ui.popup" = { bg = "black" }
"ui.statusline" = { fg = "foreground", bg = "black" }
"ui.cursorline" = { bg = "black" }
"ui.popup" = { fg = "#7B91b3", bg = "black" }
"ui.window" = { fg = "dark_gray" }
"ui.help" = { bg = "black" }
"ui.help" = { fg = "#7B91b3", bg = "black" }
"ui.text" = { fg = "foreground" }
"ui.text.focus" = { bg = "dark_gray", fg = "foreground" }
"ui.text.info" = { fg = "foreground" }
"ui.virtual.whitespace" = { fg = "dark_gray" }
"ui.virtual.ruler" = { bg = "black" }
"ui.menu" = { bg = "black" }
"ui.menu" = { fg = "foreground", bg = "black" }
"ui.menu.selected" = { bg = "orange", fg = "background" }
"ui.selection" = { bg = "dark_gray" }
"warning" = { fg = "yellow" }

@ -38,16 +38,17 @@
"ui.cursor.match" = { fg = "orange" }
"ui.linenr" = { fg = "dark_gray" }
"ui.linenr.selected" = { fg = "orange" }
"ui.statusline" = { bg = "black" }
"ui.cursorline" = { bg = "black" }
"ui.statusline" = { fg = "foreground", bg = "black" }
"ui.popup" = { bg = "black" }
"ui.window" = { fg = "dark_gray" }
"ui.help" = { bg = "black" }
"ui.help" = { fg = "foreground", bg = "black" }
"ui.text" = { fg = "foreground" }
"ui.text.focus" = { bg = "dark_gray", fg = "foreground" }
"ui.text.info" = { fg = "foreground" }
"ui.virtual.whitespace" = { fg = "dark_gray" }
"ui.virtual.ruler" = { bg = "black" }
"ui.menu" = { bg = "black" }
"ui.menu" = { fg = "foreground", bg = "black" }
"ui.menu.selected" = { bg = "orange", fg = "background" }
"ui.selection" = { bg = "dark_gray" }
"warning" = { fg = "yellow" }

@ -38,16 +38,17 @@
"ui.cursor.match" = { fg = "orange" }
"ui.linenr" = { fg = "dark_gray" }
"ui.linenr.selected" = { fg = "orange" }
"ui.statusline" = { bg = "black" }
"ui.cursorline" = { bg = "black" }
"ui.statusline" = { fg = "foreground", bg = "black" }
"ui.popup" = { bg = "black" }
"ui.window" = { fg = "dark_gray" }
"ui.help" = { bg = "black" }
"ui.help" = { fg = "foreground", bg = "black" }
"ui.text" = { fg = "foreground" }
"ui.text.focus" = { bg = "dark_gray", fg = "foreground" }
"ui.text.info" = { fg = "foreground" }
"ui.virtual.whitespace" = { fg = "dark_gray" }
"ui.virtual.ruler" = { bg = "black" }
"ui.menu" = { bg = "black" }
"ui.menu" = { fg = "foreground", bg = "black" }
"ui.menu.selected" = { bg = "orange", fg = "background" }
"ui.selection" = { bg = "dark_gray" }
"warning" = { fg = "yellow" }

@ -45,6 +45,7 @@
"ui.background" = { bg = "#161c23" }
"ui.linenr" = { fg = "#415367" }
"ui.linenr.selected" = { fg = "#e5ded6" } # TODO
"ui.cursorline" = { bg = "#131920" }
"ui.statusline" = { fg = "#e5ded6", bg = "#232d38" }
"ui.statusline.inactive" = { fg = "#c6b8ad", bg = "#232d38" }
"ui.popup" = { bg = "#232d38" }
@ -54,6 +55,7 @@
"ui.text" = { fg = "#e5ded6" }
"ui.text.focus" = { fg = "#e5ded6", modifiers= ["bold"] }
"ui.virtual.whitespace" = "#627d9d"
"ui.virtual.ruler" = { bg = "#131920" }
"ui.selection" = { bg = "#313f4e" }
# "ui.cursor.match" # TODO might want to override this because dimmed is not widely supported

@ -34,6 +34,7 @@
"ui.cursor.insert" = { fg = "berry", bg = "mint" }
"ui.linenr" = { fg = "berry_desaturated" }
"ui.linenr.selected" = { fg = "lilac" }
"ui.cursorline" = { fg = "lilac", bg = "berry_dim" }
"ui.statusline" = { fg = "lilac", bg = "berry_saturated" }
"ui.statusline.inactive" = { fg = "berry_desaturated", bg = "berry_saturated" }
"ui.popup" = { fg = "lilac", bg = "berry_saturated" }
@ -45,7 +46,7 @@
"ui.menu.selected" = { fg = "mint", bg = "berry_saturated" }
"ui.selection" = { bg = "berry_saturated" }
"ui.virtual.whitespace" = { fg = "berry_desaturated" }
"ui.virtual.ruler" = { bg ="berry_desaturated" }
"ui.virtual.ruler" = { bg ="berry_dim" }
"diff.plus" = { fg = "mint" }
"diff.delta" = { fg = "gold" }
@ -65,6 +66,7 @@
[palette]
berry = "#3A2A4D"
berry_dim = "#47345E"
berry_saturated = "#2B1C3D"
berry_desaturated = "#886C9C"
bubblegum = "#D678B5"

@ -56,9 +56,9 @@
"ui.background" = { fg = "light_gray", bg = "dark_gray2" }
"ui.window" = { bg = "widget" }
"ui.popup" = { bg = "widget" }
"ui.help" = { bg = "widget" }
"ui.menu" = { bg = "widget" }
"ui.popup" = { fg = "text", bg = "widget" }
"ui.help" = { fg = "text", bg = "widget" }
"ui.menu" = { fg = "text", bg = "widget" }
"ui.menu.selected" = { bg = "dark_blue2" }
# TODO: Alternate bg colour for `ui.cursor.match` and `ui.selection`.

@ -49,19 +49,19 @@
'ui.cursorline' = { bg = 'bg-alt' }
'ui.linenr' = { fg = 'base4' }
'ui.linenr.selected' = { fg = 'orange', bg = 'bg-alt', modifiers = ['bold'] }
'ui.statusline' = { bg = 'base3' }
'ui.statusline' = { fg = 'fg', bg = 'base3' }
'ui.statusline.normal' = { bg = 'base3' }
'ui.statusline.insert' = { bg = 'base3' }
'ui.statusline.select' = { bg = 'base3' }
'ui.popup' = { bg = 'bg-alt' }
'ui.window' = { fg = 'gray' }
'ui.help' = { bg = 'base2' }
'ui.help' = { fg = 'fg', bg = 'base2' }
'ui.text' = { fg = 'fg' }
'ui.text.focus' = { bg = 'bg-alt', fg = 'fg' }
'ui.text.info' = { fg = 'fg' }
'ui.virtual.whitespace' = { fg = 'base2' }
'ui.virtual.ruler' = { bg = 'black' }
'ui.menu' = { bg = 'bg-alt' }
'ui.menu' = { fg = 'fg', bg = 'bg-alt' }
'ui.menu.selected' = { bg = 'base3', fg = 'fg' }
'ui.selection' = { bg = 'base2' }

@ -41,6 +41,7 @@
"ui.text.focus" = { fg = "cyan" }
"ui.window" = { fg = "foreground" }
"ui.virtual.whitespace" = { fg = "comment" }
"ui.virtual.ruler" = { bg = "background_dark"}
"error" = { fg = "red" }
"warning" = { fg = "cyan" }

@ -32,6 +32,7 @@
"ui.popup" = { fg = "foreground", bg = "background_dark" }
"ui.selection" = { fg = "background", bg = "purple", modifiers = ["dim"] }
"ui.selection.primary" = { fg = "background", bg = "pink" }
"ui.cursorline" = { bg = "background_dark" }
"ui.statusline" = { fg = "foreground", bg = "background_dark" }
"ui.statusline.inactive" = { fg = "comment", bg = "background_dark" }
"ui.statusline.normal" = { fg = "background_dark", bg = "cyan" }
@ -40,6 +41,7 @@
"ui.text" = { fg = "foreground" }
"ui.text.focus" = { fg = "cyan" }
"ui.window" = { fg = "foreground" }
"ui.virtual.ruler" = { bg = "background_dark" }
"error" = { fg = "red" }
"warning" = { fg = "cyan" }

@ -60,6 +60,7 @@
"ui.cursor.select" = { fg = "bg0", bg = "blue" }
"ui.linenr" = "grey0"
"ui.linenr.selected" = "fg"
"ui.cursorline" = { bg = "bg1" }
"ui.statusline" = { fg = "grey2", bg = "bg2" }
"ui.statusline.inactive" = { fg = "grey0", bg = "bg1" }
"ui.popup" = { fg = "grey2", bg = "bg1" }
@ -71,6 +72,7 @@
"ui.menu.selected" = { fg = "bg0", bg = "green" }
"ui.selection" = { bg = "bg3" }
"ui.virtual.whitespace" = "grey0"
"ui.virtual.ruler" = { bg = "bg1" }
"hint" = "blue"
"info" = "aqua"

@ -60,6 +60,7 @@
"ui.cursor.select" = { fg = "bg0", bg = "blue" }
"ui.linenr" = "grey0"
"ui.linenr.selected" = "fg"
"ui.cursorline" = { bg = "bg1" }
"ui.statusline" = { fg = "grey2", bg = "bg2" }
"ui.statusline.inactive" = { fg = "grey0", bg = "bg1" }
"ui.popup" = { fg = "grey2", bg = "bg1" }
@ -71,6 +72,7 @@
"ui.menu.selected" = { fg = "bg0", bg = "green" }
"ui.selection" = { bg = "bg3" }
"ui.virtual.whitespace" = "grey0"
"ui.virtual.ruler" = { bg = "bg1" }
"hint" = "blue"
"info" = "aqua"

@ -57,6 +57,7 @@
"ui.linenr.selected" = { bg = "base6", modifiers = ["reversed"] }
"ui.statusline" = { fg = "base7", bg = "base1", modifiers = ["bold"] }
"ui.statusline.inactive" = { fg = "base7", bg = "base3" }
"ui.statusline.normal" = { fg = "base7", bg = "base1", modifiers = ["bold"] }
"ui.statusline.insert" = { fg = "purple_text", bg = "purple_bg", modifiers = [
"bold",

@ -0,0 +1,116 @@
# Author: Kristoffer Flottorp <kr.fl@outlook.com>
# Based on a few screenshots of Jetbrains Fleet
"type" = { fg = "orange" } # .builtin
"constructor" = { fg = "orange" }
"constant" = { fg = "green" }
# "constant.builtin" = {} # .boolean
"constant.builtin.boolean" = { fg = "green" } # .boolean
# "constant.character" = {} #.escape
"constant.numeric" = { fg = "yellow" } # .integer / .float
"string" = { fg = "pink" } # .regexp
# "string.special" = {} #.path / .url / .symbol
"string.special" = { modifiers = ["underlined"] } #.path / .url / .symbol
"comment" = { fg = "dark_gray" } # .line
# "comment.block" = {} # .documentation
"variable" = { fg = "light" } # .builtin / .parameter
# "variable.other" = {} # .member
"variable.other.member" = { fg = "purple" }
"label" = { fg = "orange" }
# "punctuation" = {} # .delimiter / .bracket
"keyword" = { fg = "green" } # .operator / .directive / .function
# "keyword.control" = { fg = "orange" } # .conditional / .repeat / .import / .return / .exception
"operator" = { fg = "light" }
"function" = { fg = "blue" } # .builtin / .method / .macro / .special
"function.macro" = { fg = "yellow" }
"function.special" = { fg = "yellow" }
"tag" = { fg = "yellow" }
"special" = { fg = "yellow" }
"namespace" = { fg = "light" }
"markup" = { fg = "purple" } # .bold / .italic / .quote
"markup.heading" = { fg = "light" } # .marker / .1 / .2 / .3 / .4 / .5 / .6
"markup.heading.1" = { fg = "orange" }
"markup.heading.2" = { fg = "yellow" }
"markup.heading.3" = { fg = "pink" }
"markup.heading.4" = { fg = "purple" }
"markup.heading.5" = { fg = "green" }
"markup.heading.6" = { fg = "blue" }
"markup.list" = { fg = "green" } # .unnumbered / .numbered
"markup.link" = { fg = "yellow" } # .url / .label / .text
"markup.raw" = { fg = "pink" } # .inline / .block
# "diff" = {} # .plus / .minus
"diff.plus" = { fg = "green" }
"diff.minus" = { fg = "orange" }
"diff.delta" = { fg = "purple" } # .moved
# used in theming
# "markup.normal" = {} # .completion / .hover
# "markup.heading" = {} # .completion / .hover
# "markup.raw.inline" = {} # .completion / .hover
# ui specific
"ui.background" = { bg = "darkest" } # .separator
"ui.cursor" = { bg = "dark_gray", modifiers = ["reversed"] } # .insert / .select / .match / .primary
"ui.cursor.match" = { fg = "light", bg = "blue_accent" } # .insert / .select / .match / .primary
"ui.cursorline" = { bg = "darker" }
"ui.linenr" = { fg = "dark_gray" } # .selected
"ui.linenr.selected" = { fg = "light_gray", bg = "darker" }
"ui.statusline" = { fg = "light", bg = "darker" } # .inactive / .normal / .insert / .select
"ui.statusline.inactive" = { fg = "dark", bg = "darker" } # .inactive / .normal / .insert / .select
"ui.statusline.normal" = { fg = "lightest", bg = "darker"} # .inactive / .normal / .insert / .select
"ui.statusline.insert" = { fg = "lightest", bg = "blue_accent" } # .inactive / .normal / .insert / .select
"ui.statusline.select" = { fg = "lightest", bg = "orange_accent" } # .inactive / .normal / .insert / .select
"ui.popup" = { fg = "light", bg = "dark" } # .info
"ui.window" = { fg = "dark", bg = "darkest" }
"ui.help" = { fg = "light", bg = "dark" }
"ui.text" = { fg = "light" } # .focus / .info
# "ui.virtual" = {} # .ruler / .whitespace
"ui.virtual.ruler" = { bg = "darker"}
"ui.menu" = { fg = "light", bg = "dark" } # .selected
"ui.menu.selected" = { fg = "lightest", bg = "blue_accent" } # .selected
"ui.selection" = { bg = "select" } # .primary
"hint" = { fg = "blue_accent"}
"info" = { fg = "yellow_accent" }
"warning" = { fg = "orange_accent" }
"error" = { fg = "diff_red_accent" }
"diagnostic" = { fg = "orange", bg = "darkest" } # .hint / .info / .warning / .error
"diagnostic.hint" = { fg = "lightest", bg = "blue_accent" }
"diagnostic.info" = { fg = "lightest", bg = "purple_accent" }
"diagnostic.warning" = { fg = "lightest", bg = "yellow_accent" }
"diagnostic.error" = { fg = "lightest", bg = "orange_accent" }
[palette]
darkest = "#0F0F0F"
darker = "#222222"
dark = "#383838"
select = "#102F5B"
light = "#F0F0F0"
lightest = "#FFFFFF"
dark_gray = "#5B5B5B"
light_gray = "#757575"
purple = "#A096F9"
blue = "#52A7F6"
pink = "#E878DE"
green = "#78D0BD"
orange = "#ECA775"
yellow = "#F9CA6A"
purple_accent = "#6363EE"
blue_accent = "#2197F3"
pink_accent = "#E44C7A"
green_accent = "#00AF99"
orange_accent = "#EE7F25"
yellow_accent = "#DEA407"
# variables intended for future updates
checkmark = "#44B254"
diff_blue_accent = "#0079FF"
diff_blue_bg = "#072037"
diff_blue_fg = "#0079FF"
diff_red_accent = "#EE113C"
diff_red_bg = "#390B14"
diff_red_fg = "#EC123B"

@ -41,6 +41,7 @@
"ui.background" = { bg = "bg0" }
"ui.linenr" = { fg = "bg4" }
"ui.linenr.selected" = { fg = "yellow1" }
"ui.cursorline" = { bg = "bg1" }
"ui.statusline" = { fg = "fg1", bg = "bg2" }
"ui.statusline.normal" = { fg = "fg1", bg = "bg2" }
"ui.statusline.insert" = { fg = "fg1", bg = "blue0" }
@ -57,6 +58,7 @@
"ui.menu" = { fg = "fg1", bg = "bg2" }
"ui.menu.selected" = { fg = "bg2", bg = "blue1", modifiers = ["bold"] }
"ui.virtual.whitespace" = "bg2"
"ui.virtual.ruler" = { bg = "bg1" }
"diagnostic" = { modifiers = ["underlined"] }

@ -42,6 +42,7 @@
"ui.background" = { bg = "bg0" }
"ui.linenr" = { fg = "bg4" }
"ui.linenr.selected" = { fg = "yellow1" }
"ui.cursorline" = { bg = "bg1" }
"ui.statusline" = { fg = "fg1", bg = "bg2" }
"ui.statusline.normal" = { fg = "fg1", bg = "bg2" }
"ui.statusline.insert" = { fg = "fg1", bg = "blue0" }
@ -58,6 +59,7 @@
"ui.menu" = { fg = "fg1", bg = "bg2" }
"ui.menu.selected" = { fg = "bg2", bg = "blue1", modifiers = ["bold"] }
"ui.virtual.whitespace" = "bg2"
"ui.virtual.ruler" = { bg = "bg1" }
"diagnostic" = { modifiers = ["underlined"] }

@ -44,19 +44,22 @@
"ui.background" = { bg = "#FFFCFD" }
"ui.linenr" = { fg = "#bbbbbb" }
"ui.linenr.selected" = { fg = "#F3EAE9" } # TODO
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
"ui.cursorline" = { bg = "#F3EAE9" }
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
"ui.popup" = { bg = "#F3EAE9" }
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
"ui.window" = { bg = "#D8B8B3" }
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
"ui.text" = { fg = "#7B91B3" }
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
"ui.virtual.whitespace" = "#A6B6CE"
"ui.virtual.ruler" = { bg = "#F3EAE9" }
"ui.selection" = { bg = "#540099" }
# "ui.cursor.match" # TODO might want to override this because dimmed is not widely supported
"ui.selection" = { bg = "#F3EAE9" }
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }

@ -33,6 +33,7 @@
"comment" = { fg = "#88846F" }
"ui.virtual.whitespace" = "#88846F"
"ui.virtual.ruler" = { bg = "#24241e" }
"string" = { fg = "#e6db74" }
"constant.character" = { fg = "#e6db74" }
@ -57,9 +58,9 @@
"ui.background" = { fg = "text", bg = "background" }
"ui.window" = { bg = "widget" }
"ui.popup" = { bg = "widget" }
"ui.help" = { bg = "widget" }
"ui.menu" = { bg = "widget" }
"ui.popup" = { fg = "text", bg = "widget" }
"ui.help" = { fg = "text", bg = "widget" }
"ui.menu" = { fg = "text", bg = "widget" }
"ui.menu.selected" = { bg = "#414339" }
"ui.cursor" = { fg = "cursor", modifiers = ["reversed"] }
@ -72,6 +73,7 @@
"ui.linenr" = { fg = "#90908a" }
"ui.linenr.selected" = { fg = "#c2c2bf" }
"ui.cursorline" = { bg = "#24241e" }
"ui.statusline" = { fg = "active_text", bg = "#414339" }
"ui.statusline.inactive" = { fg = "active_text", bg = "#75715e" }

@ -7,23 +7,25 @@
"ui.menu" = { fg = "base8", bg = "base3" }
"ui.menu.selected" = { fg = "base2", bg = "yellow" }
"ui.virtual.whitespace" = "base5"
"ui.virtual.ruler" = { bg = "base1" }
"info" = "base8"
"hint" = "base8"
# background color
"ui.background" = { bg = "base2" }
"ui.statusline.inactive" = { fg = "base8", bg = "base8x0c" }
# status bars, panels, modals, autocompletion
"ui.statusline" = { bg = "base4" }
"ui.statusline" = { fg = "base8", bg = "base4" }
"ui.statusline.inactive" = { fg = "base8", bg = "base8x0c" }
"ui.popup" = { bg = "base3" }
"ui.window" = { bg = "base3" }
"ui.help" = { bg = "base3" }
"ui.help" = { fg = "base8", bg = "base3" }
# active line, highlighting
"ui.selection" = { bg = "base4" }
"ui.cursor.match" = { bg = "base4" }
"ui.cursorline" = { bg = "base1" }
# comments, nord3 based lighter color
"comment" = { fg = "base5", modifiers = ["italic"] }

@ -7,6 +7,7 @@
"ui.menu" = { fg = "base8", bg = "base3" }
"ui.menu.selected" = { fg = "base2", bg = "yellow" }
"ui.virtual.whitespace" = "base5"
"ui.virtual.ruler" = { bg = "base1" }
"info" = "base8"
"hint" = "base8"
@ -16,14 +17,15 @@
"ui.statusline.inactive" = { fg = "base8", bg = "base8x0c" }
# status bars, panels, modals, autocompletion
"ui.statusline" = { bg = "base4" }
"ui.statusline" = { fg = "base8", bg = "base4" }
"ui.popup" = { bg = "base3" }
"ui.window" = { bg = "base3" }
"ui.help" = { bg = "base3" }
"ui.help" = { fg = "base8", bg = "base3" }
# active line, highlighting
"ui.selection" = { bg = "base4" }
"ui.cursor.match" = { bg = "base4" }
"ui.cursorline" = { bg = "base1" }
# comments, nord3 based lighter color
"comment" = { fg = "base5", modifiers = ["italic"] }

@ -7,6 +7,7 @@
"ui.menu" = { fg = "base8", bg = "base3" }
"ui.menu.selected" = { fg = "base2", bg = "yellow" }
"ui.virtual.whitespace" = "base5"
"ui.virtual.ruler" = { bg = "base1" }
"info" = "base8"
"hint" = "base8"
@ -16,14 +17,15 @@
"ui.statusline.inactive" = { fg = "base8", bg = "base8x0c" }
# status bars, panels, modals, autocompletion
"ui.statusline" = { bg = "base4" }
"ui.statusline" = { fg = "base8", bg = "base4" }
"ui.popup" = { bg = "base3" }
"ui.window" = { bg = "base3" }
"ui.help" = { bg = "base3" }
"ui.help" = { fg = "base8", bg = "base3" }
# active line, highlighting
"ui.selection" = { bg = "base4" }
"ui.cursor.match" = { bg = "base4" }
"ui.cursorline" = { bg = "base1" }
# comments, nord3 based lighter color
"comment" = { fg = "base5", modifiers = ["italic"] }

@ -7,6 +7,7 @@
"ui.menu" = { fg = "base8", bg = "base3" }
"ui.menu.selected" = { fg = "base2", bg = "yellow" }
"ui.virtual.whitespace" = "base5"
"ui.virtual.ruler" = { bg = "base1" }
"info" = "base8"
"hint" = "base8"
@ -16,14 +17,15 @@
"ui.statusline.inactive" = { fg = "base8", bg = "base8x0c" }
# status bars, panels, modals, autocompletion
"ui.statusline" = { bg = "base4" }
"ui.statusline" = { fg = "base8", bg = "base4" }
"ui.popup" = { bg = "base3" }
"ui.window" = { bg = "base3" }
"ui.help" = { bg = "base3" }
"ui.help" = { fg = "base8", bg = "base3" }
# active line, highlighting
"ui.selection" = { bg = "base4" }
"ui.cursor.match" = { bg = "base4" }
"ui.cursorline" = { bg = "base1" }
# comments, nord3 based lighter color
"comment" = { fg = "base5", modifiers = ["italic"] }

@ -7,23 +7,25 @@
"ui.menu" = { fg = "base8", bg = "base3" }
"ui.menu.selected" = { fg = "base2", bg = "yellow" }
"ui.virtual.whitespace" = "base5"
"ui.virtual.ruler" = { bg = "base1" }
"info" = "base8"
"hint" = "base8"
# background color
"ui.background" = { bg = "base2" }
"ui.statusline.inactive" = { fg = "base8", bg = "base8x0c" }
# status bars, panels, modals, autocompletion
"ui.statusline" = { bg = "base4" }
"ui.statusline" = { fg = "base8", bg = "base4" }
"ui.statusline.inactive" = { fg = "base8", bg = "base8x0c" }
"ui.popup" = { bg = "base3" }
"ui.window" = { bg = "base3" }
"ui.help" = { bg = "base3" }
"ui.help" = { fg = "base8", bg = "base3" }
# active line, highlighting
"ui.selection" = { bg = "base4" }
"ui.cursor.match" = { bg = "base4" }
"ui.cursorline" = { bg = "base1" }
# comments, nord3 based lighter color
"comment" = { fg = "base5", modifiers = ["italic"] }
@ -63,7 +65,7 @@
"variable.parameter" = "#f59762"
# error
"error" = { bg = "red", fg = "yellow" }
"error" = { bg = "magenta", fg = "yellow" }
# annotations, decorators
"special" = "#f59762"

@ -24,6 +24,7 @@
"ui.background" = { bg = "base00" }
"ui.virtual" = "base03"
"ui.virtual.ruler" = { bg = "base01" }
"ui.menu" = { fg = "base05", bg = "base01" }
"ui.menu.selected" = { fg = "base0B", bg = "base01" }
"ui.popup" = { bg = "base01" }
@ -31,6 +32,7 @@
"ui.linenr" = { fg = "#715b63", bg = "base01" }
"ui.linenr.selected" = { fg = "base02", bg = "base01", modifiers = ["bold"] }
"ui.selection" = { fg = "base05", bg = "base02" }
"ui.cursorline" = { bg = "base01" }
"ui.statusline" = { fg = "base02", bg = "base01" }
"ui.cursor" = { fg = "base04", modifiers = ["reversed"] }
"ui.cursor.primary" = { fg = "base05", modifiers = ["reversed"] }

@ -6,6 +6,7 @@
"ui.menu" = { fg = "nord6", bg = "#232d38" }
"ui.menu.selected" = { fg = "nord8", bg = "nord2" }
"ui.virtual.whitespace" = "gray"
"ui.virtual.ruler" = { bg = "nord1" }
"info" = "nord8"
"hint" = "nord8"

@ -3,9 +3,11 @@
"ui.background" = {bg="nord6"}
"ui.text" = "nord0"
"ui.selection" = {bg="nord7", fg="nord6"}
"ui.cursorline" = {bg="nord4"}
"ui.statusline" = {bg="nord4", fg="nord0"}
"ui.statusline.inactive" = {bg="nord8"}
"ui.statusline.inactive" = {bg="nord5", fg="nord0"}
"ui.virtual" = "nord8"
"ui.virtual.ruler" = {bg="nord4"}
"ui.cursor.match" = {bg="nord8"}
"ui.cursor" = {bg="nord10", fg="nord6"}
"ui.cursorline.primary" = {bg="nord5"}
@ -16,6 +18,7 @@
"ui.popup" = {bg="nord4"}
"ui.popup.info" = {bg="nord4",fg="nord0"}
"ui.help" = {bg="nord4",fg="nord0"}
"ui.window" = {bg="nord4"}
"ui.statusline.normal" = { fg = "nord0", bg = "nord8" }

@ -71,7 +71,7 @@ diagnostic = { modifiers = ["underlined"] }
"ui.text" = { fg = "white" }
"ui.text.focus" = { fg = "white", bg = "light-black", modifiers = ["bold"] }
"ui.help" = { bg = "gray" }
"ui.help" = { fg = "white", bg = "gray" }
"ui.popup" = { bg = "gray" }
"ui.window" = { fg = "gray" }
"ui.menu" = { fg = "white", bg = "gray" }

@ -1,19 +1,34 @@
# Palette based on https://github.com/NLKNguyen/papercolor-theme
# Author: Soc Virnyl Estela <socvirnyl.estela@gmail.com>
"ui.linenr.selected" = { fg = "linenr_fg_selected" }
"ui.background" = {bg="background"}
"ui.text" = "foreground"
"ui.text.focus" = { fg = "selection_background", modifiers = ["bold"]}
"ui.selection" = {bg="selection_background", fg="selection_foreground"}
"ui.cursorline" = {bg="cursorline_background"}
"ui.statusline" = {bg="paper_bar_bg", fg="regular0"}
"ui.statusline.inactive" = {bg="background", fg="bright2"}
"ui.statusline.select" = {bg="background", fg="bright7"}
"ui.statusline.normal" = {bg="background", fg="bright3"}
"ui.statusline.inactive" = {bg="background", fg="bright0"}
"ui.virtual" = "indent"
"ui.virtual.whitespace" = { fg = "regular5" }
"ui.cursor.match" = {bg = "foreground", fg = "regular4"}
"ui.cursor" = {bg = "foreground", fg = "background"}
"ui.window" = {bg = "background", fg = "bright3"}
"ui.menu.selected" = {bg = "selection_background"}
"ui.virtual.ruler" = {bg="cursorline_background"}
"ui.cursor.match" = {bg = "regular5", fg = "regular0"}
"ui.cursor" = {bg = "regular5", fg = "background"}
"ui.window" = {bg = "#D0D0D0", fg = "bright2"}
"ui.help" = {bg = "background", fg = "bright2"}
"ui.popup" = {bg = "#D0D0D0", fg = "bright7"}
"ui.menu" = {bg = "#D0D0D0", fg = "bright7"}
"ui.menu.selected" = {bg = "selection_background", fg="selection_foreground"}
"markup.heading" = { fg = "bright2", modifiers = ["bold"] }
"markup.heading" = { fg = "bright7", modifiers = ["bold"] }
"markup.heading.1" = { fg = "bright2", modifiers = ["bold"] }
"markup.heading.2" = { fg = "bright4", modifiers = ["bold"] }
"markup.heading.3" = { fg = "bright3", modifiers = ["bold"] }
"markup.heading.4" = { fg = "bright4", modifiers = ["bold"] }
"markup.heading.5" = { fg = "bright4", modifiers = ["bold"] }
"markup.heading.6" = { fg = "bright4", modifiers = ["bold"] }
"markup.list" = "regular4"
"markup.bold" = { fg = "foreground", modifiers = ["bold"] }
"markup.italic" = { modifiers = ["italic"] }
@ -40,6 +55,7 @@
"function.builtin" = { fg = "regular4", modifiers = ["bold"]}
"function.macro" = { fg = "regular1" }
"comment" = { fg = "bright0", modifiers = ["dim"] }
"ui.linenr" = { fg = "bright0" }
"module" = "#af0000"
"constant" = "#5f8700"
"constant.builtin" = "#5f8700"
@ -80,6 +96,7 @@ bright6="#4c7a5d"
bright7="#005faf"
selection_foreground="#eeeeee"
selection_background="#0087af"
cursorline_background="#fdfdfd"
paper_bar_bg="#005F87"
black="#eeeeee"
red="#d70000"
@ -97,3 +114,4 @@ light-magenta="#d75f00"
light-cyan="#4c7a4d"
light-gray="#005faf"
white="#444444"
linenr_fg_selected="#AF634D"

@ -45,7 +45,7 @@ error = "red"
diagnostic = { modifiers = ["underlined"] }
"ui.background" = "sky+"
"ui.background" = { bg = "shade" }
"ui.background.separator" = "sky"
"ui.cursor" = { modifiers = ["reversed"] }
@ -72,13 +72,13 @@ diagnostic = { modifiers = ["underlined"] }
"ui.popup.info" = { bg = "shade" }
"ui.window" = "sky"
"ui.help" = "sky"
"ui.help" = { fg = "sky", bg = "shade-" }
"ui.text" = "sky"
"ui.text.focus" = "blue"
"ui.text.info" = "sky"
"ui.virtual.ruler" = { bg = "shade+" }
"ui.virtual.ruler" = { bg = "shade-" }
"ui.virtual.whitespace" = "sky-"
"ui.virtual.indent-guide" = "shade+"

@ -24,15 +24,16 @@ diagnostic = { fg = 'greyT', bg = 'redD' }
'ui.selection.primary' = { bg = 'blueD', fg = 'white' }
'ui.linenr' = { bg = "brownN", fg = 'greyL' }
'ui.linenr.selected' = { bg = 'brownH', fg = 'orangeH' }
'ui.statusline' = { bg = 'brownH' }
'ui.statusline.inactive' = { bg = 'brownN' }
'ui.help' = { bg = 'brownD' }
'ui.cursorline' = { bg = 'brownD' }
'ui.statusline' = { fg = "greyT", bg = 'brownH' }
'ui.statusline.inactive' = { fg = "greyT", bg = 'brownN' }
'ui.help' = { fg = "greyT", bg = 'brownD' }
'ui.highlight' = { bg = 'brownH' }
'ui.virtual' = { fg = 'brownV' }
'ui.virtual.ruler' = { bg = 'brownR' }
'ui.virtual.whitespace' = { fg = 'brownV' }
'ui.virtual.indent-guide' = { fg = 'brownR' }
'ui.menu' = { bg = 'brownD' }
'ui.menu' = { fg = "greyT", bg = 'brownD' }
'ui.menu.selected' = { fg = 'orangeH', bg = 'brownH' }
'ui.popup' = { bg = 'brownD' }
'ui.popup.info' = { bg = 'brownH', fg = 'greyT' }

@ -8,6 +8,7 @@
"ui.liner.selected" = "highlightOverlay"
"ui.selection" = { bg = "highlight" }
"comment" = "subtle"
"ui.cursorline" = { bg = "surface" }
"ui.statusline" = {fg = "foam", bg = "surface" }
"ui.statusline.insert" = {fg = "base", bg = "foam", modifiers = ["bold"]}
"ui.statusline.normal" = {fg = "base", bg = "rose", modifiers = ["bold"]}
@ -18,6 +19,7 @@
"ui.text.focus" = { fg = "foam", modifiers = ["bold"]}
"ui.text.info" = {fg = "pine", modifiers = ["bold"]}
"ui.virtual.whitespace" = "highlight"
"ui.virtual.ruler" = { bg = "surface" }
"operator" = "rose"
"variable" = "text"
"constant.numeric" = "iris"
@ -39,7 +41,6 @@
"ui.popup.info" = { bg = "surface" }
"ui.window" = { bg = "base" }
"ui.help" = { bg = "overlay", fg = "foam" }
"ui.text" = "text"
"diff.plus" = "foam"
"diff.delta" = "rose"
"diff.minus" = "love"

@ -8,6 +8,7 @@
"ui.liner.selected" = "highlightOverlay"
"ui.selection" = { bg = "highlight" }
"comment" = "subtle"
"ui.cursorline" = {bg = "base" }
"ui.statusline" = {fg = "foam", bg = "surface" }
"ui.statusline.inactive" = { fg = "iris", bg = "surface" }
"ui.cursor" = { fg = "rose", modifiers = ["reversed"] }
@ -15,6 +16,7 @@
"ui.text.focus" = { fg = "foam", modifiers = ["bold"]}
"ui.text.info" = {fg = "pine", modifiers = ["bold"]}
"ui.virtual.whitespace" = "highlight"
"ui.virtual.ruler" = { bg = "base" }
"operator" = "rose"
"variable" = "text"
"number" = "iris"
@ -36,7 +38,6 @@
"ui.popup.info" = { bg = "surface" }
"ui.window" = { bg = "base" }
"ui.help" = { bg = "overlay", fg = "foam" }
"ui.text" = "text"
"diff.plus" = "foam"
"diff.delta" = "rose"
"diff.minus" = "love"

@ -11,6 +11,7 @@
"ui.liner.selected" = "highlightOverlay"
"ui.selection" = { bg = "highlight" }
"comment" = "subtle"
"ui.cursorline" = {bg = "dimBase" }
"ui.statusline" = {fg = "foam", bg = "surface" }
"ui.statusline.insert" = {fg = "base", bg = "foam", modifiers = ["bold"]}
"ui.statusline.normal" = {fg = "base", bg = "rose", modifiers = ["bold"]}
@ -21,6 +22,7 @@
"ui.text.focus" = { fg = "foam", modifiers = ["bold"]}
"ui.text.info" = {fg = "pine", modifiers = ["bold"]}
"ui.virtual.whitespace" = "highlight"
"ui.virtual.ruler" = {bg = "dimBase" }
"operator" = "rose"
"variable" = "text"
"constant.numeric" = "iris"
@ -42,7 +44,6 @@
"ui.popup.info" = { bg = "surface" }
"ui.window" = { bg = "base" }
"ui.help" = { bg = "overlay", fg = "foam" }
"ui.text" = "text"
"diff.plus" = "foam"
"diff.delta" = "rose"
"diff.minus" = "love"
@ -69,6 +70,7 @@
"markup.raw" = { fg = "foam" }
[palette]
dimBase = "#201e30"
base = "#232136"
surface = "#2a273f"
overlay = "#393552"

@ -39,6 +39,7 @@
"ui.cursor.select" = { fg = "bg0", bg = "bg_yellow" }
"ui.linenr" = "yellow"
"ui.linenr.selected" = { fg = "fg", modifiers = ["bold", "underlined"] }
"ui.cursorline" = { fg = "grey1", bg = "bg2" }
"ui.statusline" = { fg = "grey1", bg = "bg2" }
"ui.statusline.inactive" = { fg = "grey2", bg = "bg1" }
"ui.popup" = { fg = "grey2", bg = "bg1" }
@ -49,13 +50,14 @@
"ui.menu" = { fg = "fg", bg = "bg2" }
"ui.menu.selected" = { fg = "bg0", bg = "bg_yellow" }
"ui.selection" = { bg = "bg3" }
"ui.virtual.whitespace" = "grey2"
"ui.virtual.whitespace" = "bg2"
"ui.virtual.ruler" = { bg = "grey2" }
"hint" = "blue"
"info" = "aqua"
"warning" = "yellow"
"error" = "nasty-red"
"diagnostic" = { fg = "dark-red", modifiers = ["underlined"] }
"diagnostic" = { fg = "nasty-red", modifiers = ["underlined"] }
"diff.plus" = { fg = "green" }
"diff.delta" = { fg = "orange" }

@ -39,6 +39,7 @@
"ui.cursor.select" = { fg = "bg0", bg = "bg_yellow" }
"ui.linenr" = "yellow"
"ui.linenr.selected" = { fg = "fg", modifiers = ["bold", "underlined"] }
"ui.cursorline" = { bg = "bg01" }
"ui.statusline" = { fg = "grey1", bg = "bg5" }
"ui.statusline.inactive" = { fg = "grey2", bg = "bg1" }
"ui.popup" = { fg = "bg0", bg = "bg5" }
@ -50,12 +51,13 @@
"ui.menu.selected" = { fg = "bg0", bg = "bg_yellow" }
"ui.selection" = { fg = "bg0", bg = "bg3" }
"ui.virtual.whitespace" = { fg = "bg2" }
"ui.virtual.ruler" = { bg = "bg01" }
"hint" = "blue"
"info" = "aqua"
"warning" = "yellow"
"error" = "nasty-red"
"diagnostic" = { fg = "dark-red", modifiers = ["underlined"] }
"diagnostic" = { fg = "nasty-red", modifiers = ["underlined"] }
"diff.plus" = { fg = "green" }
"diff.delta" = { fg = "orange" }
@ -79,6 +81,7 @@
[palette]
bg0 = "#e1e1e3"
bg01 = "#eaeaec"
bg1 = "#494c50"
bg2 = "#55585e"
bg3 = "#61656b"

@ -33,12 +33,14 @@
"ui.popup" = { fg = "foreground", bg = "background_dark" }
"ui.selection" = { bg = "secondary_highlight" }
"ui.selection.primary" = { bg = "primary_highlight" }
"ui.cursorline" = { bg = "background_dark" }
"ui.statusline" = { fg = "foreground", bg = "background_dark" }
"ui.statusline.inactive" = { fg = "comment", bg = "background_dark" }
"ui.text" = { fg = "foreground" }
"ui.text.focus" = { fg = "cyan" }
"ui.window" = { fg = "foreground" }
"ui.virtual.whitespace" = { fg = "comment" }
"ui.virtual.ruler" = { bg = "background_dark" }
"error" = { fg = "red" }
"warning" = { fg = "cyan" }

@ -45,6 +45,8 @@
"ui.linenr" = { fg = "base0", bg = "base02" }
# 当前行号栏
"ui.linenr.selected" = { fg = "blue", modifiers = ["bold"] }
#cursorline
"ui.cursorline" = { bg = "base03" }
# 状态栏
"ui.statusline" = { fg = "base03", bg = "base0" }

@ -48,6 +48,8 @@
# 当前行号栏
# current line number column
"ui.linenr.selected" = { fg = "blue", modifiers = ["bold"] }
# cursorline
"ui.cursorline" = { bg = "base0" }
# 状态栏
# status bar

@ -0,0 +1,106 @@
### Sublime Text Sonokai Theme
## Original Author: sainnhe
# URL: https://github.com/sainnhe/sonokai
## Modified by p4ymak
# URL: https://github.com/p4ymak
# License: MIT License
"type" = "blue"
"constant" = "purple"
"constant.numeric" = "purple"
"constant.character.escape" = "orange"
"string" = "yellow"
"comment" = "grey"
"variable" = "fg"
"variable.builtin" = "orange"
"variable.parameter" = "fg"
"variable.other.member" = "fg"
"label" = "orange"
"punctuation" = "grey"
"punctuation.delimiter" = "grey"
"punctuation.bracket" = "fg"
"keyword" = "red"
"operator" = "orange"
"function" = "green"
"function.builtin" = "blue"
"function.macro" = "purple"
"tag" = "yellow"
"namespace" = "blue"
"attribute" = "purple"
"constructor" = "blue"
"module" = "blue"
"special" = "orange"
"markup.heading.marker" = "grey"
"markup.heading.1" = { fg = "red", modifiers = ["bold"] }
"markup.heading.2" = { fg = "orange", modifiers = ["bold"] }
"markup.heading.3" = { fg = "yellow", modifiers = ["bold"] }
"markup.heading.4" = { fg = "green", modifiers = ["bold"] }
"markup.heading.5" = { fg = "blue", modifiers = ["bold"] }
"markup.heading.6" = { fg = "fg", modifiers = ["bold"] }
"markup.list" = "red"
"markup.bold" = { modifiers = ["bold"] }
"markup.italic" = { modifiers = ["italic"] }
"markup.link.url" = { fg = "blue", modifiers = ["underlined"] }
"markup.link.text" = "purple"
"markup.quote" = "grey"
"markup.raw" = "green"
"diff.plus" = "green"
"diff.delta" = "orange"
"diff.minus" = "red"
"ui.background" = { bg = "bg0" }
"ui.cursor" = { modifiers = ['reversed'] }
"ui.cursor.match" = { fg = "orange", bg = "diff_yellow" }
"ui.cursor.insert" = { fg = "black", bg = "grey" }
"ui.cursor.select" = { fg = "bg0", bg = "blue" }
"ui.selection" = { bg = "bg4" }
"ui.linenr" = "grey"
"ui.linenr.selected" = "fg"
"ui.cursorline.primary" = { bg = "bg2" }
"ui.statusline" = { fg = "fg", bg = "bg3" }
"ui.statusline.inactive" = { fg = "grey", bg = "bg1" }
"ui.popup" = { fg = "grey", bg = "bg2" }
"ui.window" = { fg = "grey", bg = "bg0" }
"ui.help" = { fg = "fg", bg = "bg1" }
"ui.text" = "fg"
"ui.text.focus" = "green"
"ui.menu" = { fg = "fg", bg = "bg2" }
"ui.menu.selected" = { fg = "bg0", bg = "green" }
"ui.virtual.whitespace" = { fg = "grey_dim" }
"ui.virtual.ruler" = { bg = "grey_dim" }
info = { fg = 'green', bg = 'bg2' }
hint = { fg = 'blue', bg = 'bg2', modifiers = ['bold'] }
warning = { fg = 'yellow', bg = 'bg2', modifiers = ['bold'] }
error = { fg = 'red', bg = 'bg2', modifiers = ['bold'] }
diagnostic = { modifiers = ['underlined'] }
[palette]
black = "#181819"
bg0 = "#2c2e34"
bg1 = "#33353f"
bg2 = "#363944"
bg3 = "#3b3e48"
bg4 = "#414550"
bg_red = "#ff6077"
diff_red = "#55393d"
bg_green = "#a7df78"
diff_green = "#394634"
bg_blue = "#85d3f2"
diff_blue = "#354157"
diff_yellow = "#4e432f"
fg = "#e2e2e3"
red = "#fc5d7c"
orange = "#f39660"
yellow = "#e7c664"
green = "#9ed072"
cyan = "#8dd0b6" # added for compatibility with `edge` scheme
blue = "#76cce0"
purple = "#b39df3"
grey = "#7f8490"
grey_dim = "#595f6f"

@ -51,6 +51,7 @@
"ui.background" = { bg = "bg0" }
"ui.linenr" = { fg = "bg3" }
"ui.linenr.selected" = { fg = "theme_yellow" }
"ui.cursorline" = { bg = "bg2" }
"ui.statusline" = { fg = "fg1", bg = "bg2" }
"ui.statusline.inactive" = { fg = "fg4", bg = "bg1" }
"ui.statusline.normal" = { fg = "bg1", bg = "theme_cyan" }

@ -0,0 +1,96 @@
# Varua: an easy on the eyes color palette based on Gruvbox Material and Everforest by sainnhe.
# Author : Kamek <b.kamek@gmail.com>
"constant.character.escape" = "orange"
"type" = "blue"
"constant" = "purple"
"constant.numeric" = "purple"
"string" = "green"
"comment" = "grey0"
"variable" = "fg"
"variable.builtin" = "blue"
"variable.parameter" = "fg"
"variable.other.member" = "fg"
"label" = "aqua"
"punctuation" = "grey2"
"punctuation.delimiter" = "grey2"
"punctuation.bracket" = "fg"
"keyword" = "purple"
"operator" = "orange"
"function" = "green"
"function.builtin" = "blue"
"function.macro" = "aqua"
"tag" = "yellow"
"namespace" = "aqua"
"attribute" = "aqua"
"constructor" = "aqua"
"module" = "blue"
"special" = "orange"
"markup.heading.marker" = "grey2"
"markup.heading.1" = { fg = "red", modifiers = ["bold"] }
"markup.heading.2" = { fg = "orange", modifiers = ["bold"] }
"markup.heading.3" = { fg = "yellow", modifiers = ["bold"] }
"markup.heading.4" = { fg = "green", modifiers = ["bold"] }
"markup.heading.5" = { fg = "blue", modifiers = ["bold"] }
"markup.heading.6" = { fg = "fg", modifiers = ["bold"] }
"markup.list" = "red"
"markup.link.url" = { fg = "blue", modifiers = ["underlined"] }
"markup.link.text" = "purple"
"markup.quote" = "grey2"
"markup.raw" = "green"
"diff.plus" = "green"
"diff.delta" = "orange"
"diff.minus" = "red"
"ui.background" = { bg = "bg0" }
"ui.cursorline.primary" = { bg = "bg2" }
"ui.cursor" = { fg = "bg0", bg = "fg" }
"ui.cursor.match" = { fg = "orange", bg = "bg_yellow" }
"ui.cursor.insert" = { fg = "bg0", bg = "grey1" }
"ui.cursor.select" = { fg = "bg0", bg = "blue" }
"ui.linenr" = "grey0"
"ui.linenr.selected" = "fg"
"ui.statusline" = { fg = "fg", bg = "bg2" }
"ui.statusline.inactive" = { fg = "grey0", bg = "bg1" }
"ui.popup" = { fg = "grey0", bg = "bg2" }
"ui.window" = { fg = "grey0", bg = "bg1" }
"ui.help" = { fg = "fg", bg = "bg1" }
"ui.text" = "fg"
"ui.text.focus" = "fg"
"ui.menu" = { fg = "fg", bg = "bg2" }
"ui.menu.selected" = { fg = "fg", bg = "bg1" }
"ui.selection" = { bg = "bg3" }
"ui.virtual.whitespace" = "grey0"
"hint" = "blue"
"info" = "aqua"
"warning" = "yellow"
"error" = "red"
"diagnostic" = { modifiers = ["underlined"] }
[palette]
bg0 = "#282828"
bg1 = "#45403d"
bg2 = "#32302f"
bg3 = "#3c3836"
bg4 = "#4c555b"
bg5 = "#53605c"
bg_visual = "#503946"
bg_red = "#4e3e43"
bg_green = "#404d44"
bg_blue = "#394f5a"
bg_yellow = "#4a4940"
fg = "#dfbf8e"
red = "#e67e80"
orange = "#e69875"
yellow = "#dbbc7f"
green = "#a7c080"
aqua = "#83c092"
blue = "#7fbbb3"
purple = "#d699b6"
grey0 = "#928374"
grey1 = "#859289"
grey2 = "#9da9a0"

@ -453,7 +453,7 @@ _________________________________________________________________
Like the select command, searching also uses regex.
Note: To search backwards, type ? (shift-/).
Note: Unlike Vim, N doesn't change the search direction.
Note: Unlike Vim, ? doesn't change the search direction.
N always goes backwards and n always goes forwards.

@ -8,4 +8,5 @@ edition = "2021"
[dependencies]
helix-term = { version = "0.6", path = "../helix-term" }
helix-core = { version = "0.6", path = "../helix-core" }
helix-loader = { version = "0.6", path = "../helix-loader" }
toml = "0.5"

@ -217,6 +217,41 @@ pub mod tasks {
Ok(())
}
pub fn query_check() -> Result<(), String> {
use crate::helpers::lang_config;
use helix_core::{syntax::read_query, tree_sitter::Query};
use helix_loader::grammar::get_language;
let query_files = [
"highlights.scm",
"locals.scm",
"injections.scm",
"textobjects.scm",
"indents.scm",
];
for language in lang_config().language {
let language_name = language.language_id.to_ascii_lowercase();
let grammar_name = language.grammar.unwrap_or(language.language_id);
for query_file in query_files {
let language = get_language(&grammar_name);
let query_text = read_query(&language_name, query_file);
if !query_text.is_empty() && language.is_ok() {
if let Err(reason) = Query::new(language.unwrap(), &query_text) {
return Err(format!(
"Failed to parse {} queries for {}: {}",
query_file, language_name, reason
));
}
}
}
}
println!("Query check succeeded");
Ok(())
}
pub fn print_help() {
println!(
"
@ -224,6 +259,7 @@ Usage: Run with `cargo xtask <task>`, eg. `cargo xtask docgen`.
Tasks:
docgen: Generate files to be included in the mdbook output.
query-check: Check that tree-sitter queries are valid.
"
);
}
@ -235,6 +271,7 @@ fn main() -> Result<(), DynError> {
None => tasks::print_help(),
Some(t) => match t.as_str() {
"docgen" => tasks::docgen()?,
"query-check" => tasks::query_check()?,
invalid => return Err(format!("Invalid task name: {}", invalid).into()),
},
};

Loading…
Cancel
Save