Merge branch 'master'

pull/6/head
trivernis 2 years ago
commit 3893898ffc
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -17,7 +17,7 @@ jobs:
uses: cachix/install-nix-action@v18
- name: Authenticate with Cachix
uses: cachix/cachix-action@v11
uses: cachix/cachix-action@v12
with:
name: helix
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}

77
Cargo.lock generated

@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
@ -22,9 +33,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.65"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
name = "arc-swap"
@ -81,9 +92,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cc"
version = "1.0.73"
version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574"
[[package]]
name = "cfg-if"
@ -277,15 +288,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.24"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
version = "0.3.24"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
@ -294,15 +305,15 @@ dependencies = [
[[package]]
name = "futures-task"
version = "0.3.24"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
version = "0.3.24"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-core",
"futures-task",
@ -383,6 +394,15 @@ dependencies = [
"memmap2",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
name = "helix-core"
version = "0.6.0"
@ -664,9 +684,9 @@ dependencies = [
[[package]]
name = "lsp-types"
version = "0.93.1"
version = "0.93.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3bcfee315dde785ba887edb540b08765fd7df75a7d948844be6bf5712246734"
checksum = "9be6e9c7e2d18f651974370d7aff703f9513e0df6e464fd795660edc77e6ca51"
dependencies = [
"bitflags",
"serde",
@ -733,9 +753,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.15.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "parking_lot"
@ -919,18 +939,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.145"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
dependencies = [
"proc-macro2",
"quote",
@ -939,9 +959,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.86"
version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
dependencies = [
"itoa",
"ryu",
@ -1103,18 +1123,18 @@ dependencies = [
[[package]]
name = "termini"
version = "0.1.2"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "394766021ef3dae8077f080518cdf5360831990f77f5708d5e3594c9b3efa2f9"
checksum = "8c0f7ecb9c2a380d2686a747e4fc574043712326e8d39fbd220ab3bd29768a12"
dependencies = [
"dirs-next",
]
[[package]]
name = "textwrap"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
dependencies = [
"smawk",
"unicode-linebreak",
@ -1264,10 +1284,11 @@ checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "unicode-linebreak"
version = "0.1.2"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f"
checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
dependencies = [
"hashbrown",
"regex",
]

@ -27,6 +27,7 @@
| elixir | ✓ | ✓ | | `elixir-ls` |
| elm | ✓ | | | `elm-language-server` |
| elvish | ✓ | | | `elvish` |
| env | ✓ | | | |
| erb | ✓ | | | |
| erlang | ✓ | ✓ | | `erlang_ls` |
| esdl | ✓ | | | |
@ -53,6 +54,7 @@
| html | ✓ | | | `vscode-html-language-server` |
| idris | | | | `idris2-lsp` |
| iex | ✓ | | | |
| ini | ✓ | | | |
| java | ✓ | | | `jdtls` |
| javascript | ✓ | ✓ | ✓ | `typescript-language-server` |
| jsdoc | ✓ | | | |
@ -60,6 +62,7 @@
| jsonnet | ✓ | | | `jsonnet-language-server` |
| jsx | ✓ | ✓ | ✓ | `typescript-language-server` |
| julia | ✓ | | | `julia` |
| kdl | ✓ | | | |
| kotlin | ✓ | | | `kotlin-language-server` |
| latex | ✓ | ✓ | | `texlab` |
| lean | ✓ | | | `lean` |
@ -120,10 +123,13 @@
| v | ✓ | | | `vls` |
| vala | ✓ | | | `vala-language-server` |
| verilog | ✓ | ✓ | | `svlangserver` |
| vhs | ✓ | | | |
| vue | ✓ | | | `vls` |
| wast | ✓ | | | |
| wat | ✓ | | | |
| wgsl | ✓ | | | `wgsl_analyzer` |
| wit | ✓ | | ✓ | |
| xit | ✓ | | | |
| xml | ✓ | | ✓ | |
| yaml | ✓ | | ✓ | `yaml-language-server` |
| zig | ✓ | ✓ | ✓ | `zls` |

@ -28,7 +28,7 @@
| `:quit-all!`, `:qa!` | Force close all views ignoring unsaved changes. |
| `:cquit`, `:cq` | Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2). |
| `:cquit!`, `:cq!` | Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2). |
| `:theme` | Change the editor theme. |
| `:theme` | Change the editor theme (show current theme if no name specified). |
| `:clipboard-yank` | Yank main selection into system clipboard. |
| `:clipboard-yank-join` | Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline. |
| `:primary-clipboard-yank` | Yank main selection into system primary clipboard. |
@ -44,6 +44,7 @@
| `:show-directory`, `:pwd` | Show the current working directory. |
| `:encoding` | Set encoding. Based on `https://encoding.spec.whatwg.org`. |
| `:reload` | Discard changes and reload from the source file. |
| `:update` | Write changes only if the file has been modified. |
| `:lsp-restart` | Restarts the Language Server that is in use by the current doc |
| `:tree-sitter-scopes` | Display tree sitter scopes, primarily for theming and development. |
| `:debug-start`, `:dbg` | Start a debug session from a given template with given parameters. |

@ -52,7 +52,8 @@ sudo xbps-install helix
## Windows
Helix can be installed using [Scoop](https://scoop.sh/) or [Chocolatey](https://chocolatey.org/).
Helix can be installed using [Scoop](https://scoop.sh/), [Chocolatey](https://chocolatey.org/)
or [MSYS2](https://msys2.org/).
**Scoop:**
@ -66,6 +67,23 @@ scoop install helix
choco install helix
```
**MSYS2:**
```
pacman -S mingw-w64-i686-helix
```
or
```
pacman -S mingw-w64-x86_64-helix
```
or
```
pacman -S mingw-w64-ucrt-x86_64-helix
```
## Build from source

@ -1,5 +1,22 @@
{
"nodes": {
"all-cabal-json": {
"flake": false,
"locked": {
"lastModified": 1665552503,
"narHash": "sha256-r14RmRSwzv5c+bWKUDaze6pXM7nOsiz1H8nvFHJvufc=",
"owner": "nix-community",
"repo": "all-cabal-json",
"rev": "d7c0434eebffb305071404edcf9d5cd99703878e",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "hackage",
"repo": "all-cabal-json",
"type": "github"
}
},
"crane": {
"flake": false,
"locked": {
@ -19,11 +36,11 @@
"devshell": {
"flake": false,
"locked": {
"lastModified": 1660811669,
"narHash": "sha256-V6lmsaLNFz41myppL0yxglta92ijkSvpZ+XVygAh+bU=",
"lastModified": 1667210711,
"narHash": "sha256-IoErjXZAkzYWHEpQqwu/DeRNJGFdR7X2OGbkhMqMrpw=",
"owner": "numtide",
"repo": "devshell",
"rev": "c2feacb46ee69949124c835419861143c4016fb5",
"rev": "96a9dd12b8a447840cc246e17a47b81a4268bba7",
"type": "github"
},
"original": {
@ -38,6 +55,7 @@
"nci",
"nixpkgs"
],
"all-cabal-json": "all-cabal-json",
"crane": "crane",
"devshell": [
"nci",
@ -47,6 +65,7 @@
"nci",
"nixpkgs"
],
"ghc-utils": "ghc-utils",
"gomod2nix": [
"nci",
"nixpkgs"
@ -69,11 +88,11 @@
]
},
"locked": {
"lastModified": 1662176993,
"narHash": "sha256-Sy7DsGAveDUFBb6YDsUSYZd/AcXfP/MOMIwMt/NgY84=",
"lastModified": 1667429039,
"narHash": "sha256-Lu6da25JioHzerkLHAHSO9suCQFzJ/XBjkcGCIbasLM=",
"owner": "nix-community",
"repo": "dream2nix",
"rev": "809bc5940214744eb29778a9a0b03f161979c1b2",
"rev": "5252794e58eedb02d607fa3187ffead7becc81b0",
"type": "github"
},
"original": {
@ -84,11 +103,11 @@
},
"flake-utils": {
"locked": {
"lastModified": 1656928814,
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
@ -97,6 +116,22 @@
"type": "github"
}
},
"ghc-utils": {
"flake": false,
"locked": {
"lastModified": 1662774800,
"narHash": "sha256-1Rd2eohGUw/s1tfvkepeYpg8kCEXiIot0RijapUjAkE=",
"ref": "refs/heads/master",
"rev": "bb3a2d3dc52ff0253fb9c2812bd7aa2da03e0fea",
"revCount": 1072,
"type": "git",
"url": "https://gitlab.haskell.org/bgamari/ghc-utils"
},
"original": {
"type": "git",
"url": "https://gitlab.haskell.org/bgamari/ghc-utils"
}
},
"nci": {
"inputs": {
"devshell": "devshell",
@ -109,11 +144,11 @@
]
},
"locked": {
"lastModified": 1662177071,
"narHash": "sha256-x6XF//RdZlw81tFAYM1TkjY+iQIpyMCWZ46r9o4wVQY=",
"lastModified": 1667542401,
"narHash": "sha256-mdWjP5tjSf8n6FAtpSgL23kX4+eWBwLrSYo9iY3mA8Q=",
"owner": "yusdacra",
"repo": "nix-cargo-integration",
"rev": "65270dea87bb82fc02102a15221677eea237680e",
"rev": "cd5e5cbd81c80dc219455dd3b1e0ddb55fae51ec",
"type": "github"
},
"original": {
@ -124,11 +159,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1662019588,
"narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=",
"lastModified": 1667482890,
"narHash": "sha256-pua0jp87iwN7NBY5/ypx0s9L9CG49Ju/NI4wGwurHc4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2da64a81275b68fdad38af669afeda43d401e94b",
"rev": "a2a777538d971c6b01c6e54af89ddd6567c055e8",
"type": "github"
},
"original": {
@ -153,11 +188,11 @@
]
},
"locked": {
"lastModified": 1662087605,
"narHash": "sha256-Gpf2gp2JenKGf+TylX/YJpttY2bzsnvAMLdLaxoZRyU=",
"lastModified": 1667487142,
"narHash": "sha256-bVuzLs1ZVggJAbJmEDVO9G6p8BH3HRaolK70KXvnWnU=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "60c2cfaa8b90ed8cebd18b214fac8682dcf222dd",
"rev": "cf668f737ac986c0a89e83b6b2e3c5ddbd8cf33b",
"type": "github"
},
"original": {

@ -21,57 +21,124 @@
...
}: let
lib = nixpkgs.lib;
ncl = nci.lib.nci-lib;
mkRootPath = rel:
builtins.path {
path = "${toString ./.}/${rel}";
name = rel;
};
filteredSource = let
pathsToIgnore = [
".envrc"
".ignore"
".github"
"runtime"
"screenshot.png"
"book"
"contrib"
"docs"
"README.md"
"CHANGELOG.md"
"shell.nix"
"default.nix"
"grammars.nix"
"flake.nix"
"flake.lock"
];
ignorePaths = path: type: let
# split the nix store path into its components
components = lib.splitString "/" path;
# drop off the `/nix/hash-source` section from the path
relPathComponents = lib.drop 4 components;
# reassemble the path components
relPath = lib.concatStringsSep "/" relPathComponents;
in
lib.all (p: ! (lib.hasPrefix p relPath)) pathsToIgnore;
in
builtins.path {
name = "helix-source";
path = toString ./.;
# filter out unnecessary paths
filter = ignorePaths;
};
outputs = nci.lib.makeOutputs {
root = ./.;
renameOutputs = {"helix-term" = "helix";};
# Set default app to hx (binary is from helix-term release build)
# Set default package to helix-term release build
defaultOutputs = {
app = "hx";
package = "helix";
config = common: {
outputs = {
# rename helix-term to helix since it's our main package
rename = {"helix-term" = "helix";};
# Set default app to hx (binary is from helix-term release build)
# Set default package to helix-term release build
defaults = {
app = "hx";
package = "helix";
};
};
cCompiler.package = with common.pkgs;
if stdenv.isLinux
then gcc
else clang;
shell = {
packages = with common.pkgs;
[lld_13 cargo-flamegraph rust-analyzer]
++ (lib.optional (stdenv.isx86_64 && stdenv.isLinux) cargo-tarpaulin)
++ (lib.optional stdenv.isLinux lldb);
env = [
{
name = "HELIX_RUNTIME";
eval = "$PWD/runtime";
}
{
name = "RUST_BACKTRACE";
value = "1";
}
{
name = "RUSTFLAGS";
value =
if common.pkgs.stdenv.isLinux
then "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment"
else "";
}
];
};
};
overrides = {
cCompiler = common:
with common.pkgs;
if stdenv.isLinux
then gcc
else clang;
crateOverrides = common: _: {
helix-term = prev: {
src = builtins.path {
name = "helix-source";
path = toString ./.;
# filter out unneeded stuff that cause rebuilds
filter = path: type:
lib.all
(n: builtins.baseNameOf path != n)
[
".envrc"
".ignore"
".github"
"runtime"
"screenshot.png"
"book"
"contrib"
"docs"
"README.md"
"shell.nix"
"default.nix"
"grammars.nix"
"flake.nix"
"flake.lock"
];
};
pkgConfig = common: {
helix-term = {
# Wrap helix with runtime
wrapper = _: old: let
inherit (common) pkgs;
makeOverridableHelix = old: config: let
grammars = pkgs.callPackage ./grammars.nix config;
runtimeDir = pkgs.runCommand "helix-runtime" {} ''
mkdir -p $out
ln -s ${mkRootPath "runtime"}/* $out
rm -r $out/grammars
ln -s ${grammars} $out/grammars
'';
helix-wrapped =
common.internal.pkgsSet.utils.wrapDerivation old
{
nativeBuildInputs = [pkgs.makeWrapper];
makeWrapperArgs = config.makeWrapperArgs or [];
}
''
rm -rf $out/bin
mkdir -p $out/bin
ln -sf ${old}/bin/* $out/bin/
wrapProgram "$out/bin/hx" ''${makeWrapperArgs[@]} --set HELIX_RUNTIME "${runtimeDir}"
'';
in
helix-wrapped
// {override = makeOverridableHelix old;};
in
makeOverridableHelix old {};
overrides.fix-build.overrideAttrs = prev: {
src = filteredSource;
# disable fetching and building of tree-sitter grammars in the helix-term build.rs
HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1";
buildInputs = (prev.buildInputs or []) ++ [common.cCompiler.cc.lib];
buildInputs = ncl.addBuildInputs prev [common.config.cCompiler.package.cc.lib];
# link languages and theme toml files since helix-term expects them (for tests)
preConfigure = ''
@ -87,88 +154,20 @@
meta.mainProgram = "hx";
};
};
shell = common: prev: {
packages =
prev.packages
++ (
with common.pkgs;
[lld_13 cargo-flamegraph rust-analyzer]
++ (lib.optional (stdenv.isx86_64 && stdenv.isLinux) cargo-tarpaulin)
++ (lib.optional stdenv.isLinux lldb)
);
env =
prev.env
++ [
{
name = "HELIX_RUNTIME";
eval = "$PWD/runtime";
}
{
name = "RUST_BACKTRACE";
value = "1";
}
{
name = "RUSTFLAGS";
value =
if common.pkgs.stdenv.isLinux
then "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment"
else "";
}
];
};
};
};
makeOverridableHelix = system: old: config: let
pkgs = nixpkgs.legacyPackages.${system};
grammars = pkgs.callPackage ./grammars.nix config;
runtimeDir = pkgs.runCommand "helix-runtime" {} ''
mkdir -p $out
ln -s ${mkRootPath "runtime"}/* $out
rm -r $out/grammars
ln -s ${grammars} $out/grammars
'';
helix-wrapped =
pkgs.runCommand "${old.name}-wrapped"
{
inherit (old) pname version meta;
nativeBuildInputs = [pkgs.makeWrapper];
makeWrapperArgs = config.makeWrapperArgs or [];
}
''
mkdir -p $out
cp -r --no-preserve=mode,ownership ${old}/* $out/
chmod +x $out/bin/*
wrapProgram "$out/bin/hx" ''${makeWrapperArgs[@]} --set HELIX_RUNTIME "${runtimeDir}"
'';
in
helix-wrapped
// {override = makeOverridableHelix system old;};
in
outputs
// {
apps =
lib.mapAttrs
(
system: apps: rec {
default = hx;
hx = {
type = "app";
program = lib.getExe self.${system}.packages.helix;
};
}
)
outputs.apps;
packages =
lib.mapAttrs
(
system: packages: rec {
default = helix;
helix = makeOverridableHelix system helix-unwrapped {};
helix-debug = makeOverridableHelix system helix-unwrapped-debug {};
helix-unwrapped = packages.helix;
helix-unwrapped-debug = packages.helix-debug;
}
system: packages:
packages
// {
helix-unwrapped = packages.helix.passthru.unwrapped;
helix-unwrapped-debug = packages.helix-debug.passthru.unwrapped;
}
)
outputs.packages;
};

@ -26,7 +26,7 @@ unicode-general-category = "0.6"
# slab = "0.4.2"
slotmap = "1.0"
tree-sitter = "0.20"
once_cell = "1.15"
once_cell = "1.16"
arc-swap = "1"
regex = "1"
bitflags = "1.3"
@ -43,7 +43,7 @@ encoding_rs = "0.8"
chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] }
etcetera = "0.4"
textwrap = "0.15.1"
textwrap = "0.16.0"
[dev-dependencies]
quickcheck = { version = "1", default-features = false }

@ -282,7 +282,7 @@ impl History {
}
/// Whether to undo by a number of edits or a duration of time.
#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum UndoKind {
Steps(usize),
TimePeriod(std::time::Duration),

@ -110,8 +110,8 @@ impl<'a> Increment for NumberIncrementor<'a> {
let (lower_count, upper_count): (usize, usize) =
old_text.chars().skip(2).fold((0, 0), |(lower, upper), c| {
(
lower + c.is_ascii_lowercase().then(|| 1).unwrap_or(0),
upper + c.is_ascii_uppercase().then(|| 1).unwrap_or(0),
lower + usize::from(c.is_ascii_lowercase()),
upper + usize::from(c.is_ascii_uppercase()),
)
});
if upper_count > lower_count {

@ -6,7 +6,7 @@ pub const DEFAULT_LINE_ENDING: LineEnding = LineEnding::Crlf;
pub const DEFAULT_LINE_ENDING: LineEnding = LineEnding::LF;
/// Represents one of the valid Unicode line endings.
#[derive(PartialEq, Copy, Clone, Debug)]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum LineEnding {
Crlf, // CarriageReturn followed by LineFeed
LF, // U+000A -- LineFeed

@ -1,5 +1,22 @@
use std::borrow::Cow;
/// Auto escape for shellwords usage.
pub fn escape(input: &str) -> Cow<'_, str> {
if !input.chars().any(|x| x.is_ascii_whitespace()) {
Cow::Borrowed(input)
} else if cfg!(unix) {
Cow::Owned(input.chars().fold(String::new(), |mut buf, c| {
if c.is_ascii_whitespace() {
buf.push('\\');
}
buf.push(c);
buf
}))
} else {
Cow::Owned(format!("\"{}\"", input))
}
}
/// Get the vec of escaped / quoted / doublequoted filenames from the input str
pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
enum State {
@ -226,4 +243,19 @@ mod test {
];
assert_eq!(expected, result);
}
#[test]
#[cfg(unix)]
fn test_escaping_unix() {
assert_eq!(escape("foobar"), Cow::Borrowed("foobar"));
assert_eq!(escape("foo bar"), Cow::Borrowed("foo\\ bar"));
assert_eq!(escape("foo\tbar"), Cow::Borrowed("foo\\\tbar"));
}
#[test]
#[cfg(windows)]
fn test_escaping_windows() {
assert_eq!(escape("foobar"), Cow::Borrowed("foobar"));
assert_eq!(escape("foo bar"), Cow::Borrowed("\"foo bar\""));
}
}

@ -13,7 +13,7 @@ pub const PAIRS: &[(char, char)] = &[
('', ''),
];
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
PairNotFound,
CursorOverlap,

@ -218,7 +218,7 @@ pub struct FormatterConfiguration {
pub args: Vec<String>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct AdvancedCompletion {
pub name: Option<String>,
@ -226,14 +226,14 @@ pub struct AdvancedCompletion {
pub default: Option<String>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case", untagged)]
pub enum DebugConfigCompletion {
Named(String),
Advanced(AdvancedCompletion),
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum DebugArgumentValue {
String(String),
@ -241,7 +241,7 @@ pub enum DebugArgumentValue {
Boolean(bool),
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct DebugTemplate {
pub name: String,
@ -250,7 +250,7 @@ pub struct DebugTemplate {
pub args: HashMap<String, DebugArgumentValue>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct DebugAdapterConfig {
pub name: String,
@ -266,7 +266,7 @@ pub struct DebugAdapterConfig {
}
// Different workarounds for adapters' differences
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct DebuggerQuirks {
#[serde(default)]
pub absolute_paths: bool,
@ -280,7 +280,7 @@ pub struct IndentationConfiguration {
}
/// Configuration for auto pairs
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, untagged)]
pub enum AutoPairConfig {
/// Enables or disables auto pairing. False means disabled. True means to use the default pairs.

@ -22,7 +22,7 @@ pub struct Request {
pub arguments: Option<Value>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub struct Response {
// seq is omitted as unused and is not sent by some implementations
pub request_seq: u64,

@ -22,7 +22,7 @@ pub trait Request {
const COMMAND: &'static str;
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ColumnDescriptor {
pub attribute_name: String,
@ -35,7 +35,7 @@ pub struct ColumnDescriptor {
pub width: Option<usize>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ExceptionBreakpointsFilter {
pub filter: String,
@ -50,7 +50,7 @@ pub struct ExceptionBreakpointsFilter {
pub condition_description: Option<String>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DebuggerCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
@ -131,14 +131,14 @@ pub struct DebuggerCapabilities {
pub supported_checksum_algorithms: Option<Vec<String>>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Checksum {
pub algorithm: String,
pub checksum: String,
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Source {
#[serde(skip_serializing_if = "Option::is_none")]
@ -159,7 +159,7 @@ pub struct Source {
pub checksums: Option<Vec<Checksum>>,
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SourceBreakpoint {
pub line: usize,
@ -173,7 +173,7 @@ pub struct SourceBreakpoint {
pub log_message: Option<String>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Breakpoint {
#[serde(skip_serializing_if = "Option::is_none")]
@ -197,7 +197,7 @@ pub struct Breakpoint {
pub offset: Option<usize>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackFrameFormat {
#[serde(skip_serializing_if = "Option::is_none")]
@ -216,7 +216,7 @@ pub struct StackFrameFormat {
pub include_all: Option<bool>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackFrame {
pub id: usize,
@ -239,14 +239,14 @@ pub struct StackFrame {
pub presentation_hint: Option<String>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Thread {
pub id: ThreadId,
pub name: String,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Scope {
pub name: String,
@ -270,14 +270,14 @@ pub struct Scope {
pub end_column: Option<usize>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ValueFormat {
#[serde(skip_serializing_if = "Option::is_none")]
pub hex: Option<bool>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VariablePresentationHint {
#[serde(skip_serializing_if = "Option::is_none")]
@ -288,7 +288,7 @@ pub struct VariablePresentationHint {
pub visibility: Option<String>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Variable {
pub name: String,
@ -308,7 +308,7 @@ pub struct Variable {
pub memory_reference: Option<String>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Module {
pub id: String, // TODO: || number
@ -333,7 +333,7 @@ pub struct Module {
pub mod requests {
use super::*;
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeArguments {
#[serde(rename = "clientID", skip_serializing_if = "Option::is_none")]
@ -409,7 +409,7 @@ pub mod requests {
const COMMAND: &'static str = "configurationDone";
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetBreakpointsArguments {
pub source: Source,
@ -420,7 +420,7 @@ pub mod requests {
pub source_modified: Option<bool>,
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetBreakpointsResponse {
#[serde(skip_serializing_if = "Option::is_none")]
@ -436,13 +436,13 @@ pub mod requests {
const COMMAND: &'static str = "setBreakpoints";
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ContinueArguments {
pub thread_id: ThreadId,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ContinueResponse {
#[serde(skip_serializing_if = "Option::is_none")]
@ -458,7 +458,7 @@ pub mod requests {
const COMMAND: &'static str = "continue";
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackTraceArguments {
pub thread_id: ThreadId,
@ -470,7 +470,7 @@ pub mod requests {
pub format: Option<StackFrameFormat>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StackTraceResponse {
#[serde(skip_serializing_if = "Option::is_none")]
@ -487,7 +487,7 @@ pub mod requests {
const COMMAND: &'static str = "stackTrace";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreadsResponse {
pub threads: Vec<Thread>,
@ -502,13 +502,13 @@ pub mod requests {
const COMMAND: &'static str = "threads";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScopesArguments {
pub frame_id: usize,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ScopesResponse {
pub scopes: Vec<Scope>,
@ -523,7 +523,7 @@ pub mod requests {
const COMMAND: &'static str = "scopes";
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VariablesArguments {
pub variables_reference: usize,
@ -537,7 +537,7 @@ pub mod requests {
pub format: Option<ValueFormat>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VariablesResponse {
pub variables: Vec<Variable>,
@ -552,7 +552,7 @@ pub mod requests {
const COMMAND: &'static str = "variables";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StepInArguments {
pub thread_id: ThreadId,
@ -571,7 +571,7 @@ pub mod requests {
const COMMAND: &'static str = "stepIn";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StepOutArguments {
pub thread_id: ThreadId,
@ -588,7 +588,7 @@ pub mod requests {
const COMMAND: &'static str = "stepOut";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NextArguments {
pub thread_id: ThreadId,
@ -605,7 +605,7 @@ pub mod requests {
const COMMAND: &'static str = "next";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PauseArguments {
pub thread_id: ThreadId,
@ -620,7 +620,7 @@ pub mod requests {
const COMMAND: &'static str = "pause";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EvaluateArguments {
pub expression: String,
@ -632,7 +632,7 @@ pub mod requests {
pub format: Option<ValueFormat>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EvaluateResponse {
pub result: String,
@ -658,7 +658,7 @@ pub mod requests {
const COMMAND: &'static str = "evaluate";
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetExceptionBreakpointsArguments {
pub filters: Vec<String>,
@ -666,7 +666,7 @@ pub mod requests {
// pub exceptionOptions: Option<Vec<ExceptionOptions>>, // needs capability
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SetExceptionBreakpointsResponse {
#[serde(skip_serializing_if = "Option::is_none")]
@ -684,7 +684,7 @@ pub mod requests {
// Reverse Requests
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RunInTerminalResponse {
#[serde(skip_serializing_if = "Option::is_none")]
@ -693,7 +693,7 @@ pub mod requests {
pub shell_process_id: Option<u32>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RunInTerminalArguments {
#[serde(skip_serializing_if = "Option::is_none")]
@ -745,7 +745,7 @@ pub mod events {
Memory(Memory),
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Stopped {
pub reason: String,
@ -763,7 +763,7 @@ pub mod events {
pub hit_breakpoint_ids: Option<Vec<usize>>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Continued {
pub thread_id: ThreadId,
@ -771,27 +771,27 @@ pub mod events {
pub all_threads_continued: Option<bool>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Exited {
pub exit_code: usize,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Terminated {
#[serde(skip_serializing_if = "Option::is_none")]
pub restart: Option<Value>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Thread {
pub reason: String,
pub thread_id: ThreadId,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Output {
pub output: String,
@ -811,28 +811,28 @@ pub mod events {
pub data: Option<Value>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Breakpoint {
pub reason: String,
pub breakpoint: super::Breakpoint,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Module {
pub reason: String,
pub module: super::Module,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LoadedSource {
pub reason: String,
pub source: super::Source,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Process {
pub name: String,
@ -846,13 +846,13 @@ pub mod events {
pub pointer_size: Option<usize>,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Capabilities {
pub capabilities: super::DebuggerCapabilities,
}
// #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
// #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
// #[serde(rename_all = "camelCase")]
// pub struct Invalidated {
// pub areas: Vec<InvalidatedArea>,
@ -860,7 +860,7 @@ pub mod events {
// pub stack_frame_id: Option<usize>,
// }
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Memory {
pub memory_reference: String,

@ -19,7 +19,7 @@ serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
etcetera = "0.4"
tree-sitter = "0.20"
once_cell = "1.15"
once_cell = "1.16"
log = "0.4"
# TODO: these two should be on !wasm32 only

@ -67,7 +67,7 @@ pub fn get_language(name: &str) -> Result<Language> {
#[cfg(not(target_arch = "wasm32"))]
pub fn get_language(name: &str) -> Result<Language> {
use libloading::{Library, Symbol};
let mut library_path = crate::runtime_dir().join("grammars").join(&name);
let mut library_path = crate::runtime_dir().join("grammars").join(name);
library_path.set_extension(DYLIB_EXTENSION);
let library = unsafe { Library::new(&library_path) }
@ -429,7 +429,7 @@ fn build_tree_sitter_library(
if cfg!(all(windows, target_env = "msvc")) {
command
.args(&["/nologo", "/LD", "/I"])
.args(["/nologo", "/LD", "/I"])
.arg(header_path)
.arg("/Od")
.arg("/utf-8");

@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
// https://www.jsonrpc.org/specification#error_object
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ErrorCode {
ParseError,
InvalidRequest,
@ -68,7 +68,7 @@ impl Serialize for ErrorCode {
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Error {
pub code: ErrorCode,
pub message: String,
@ -100,7 +100,7 @@ impl std::error::Error for Error {}
// https://www.jsonrpc.org/specification#request_object
/// Request ID
#[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Hash, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Id {
Null,
@ -109,7 +109,7 @@ pub enum Id {
}
/// Protocol Version
#[derive(Debug, PartialEq, Clone, Copy, Hash, Eq)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Version {
V2,
}
@ -153,7 +153,7 @@ impl<'de> Deserialize<'de> for Version {
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Params {
None,
@ -182,7 +182,7 @@ impl From<Params> for Value {
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MethodCall {
pub jsonrpc: Option<Version>,
@ -192,7 +192,7 @@ pub struct MethodCall {
pub id: Id,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Notification {
pub jsonrpc: Option<Version>,
@ -201,7 +201,7 @@ pub struct Notification {
pub params: Params,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum Call {
@ -235,7 +235,7 @@ impl From<Notification> for Call {
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
#[serde(untagged)]
pub enum Request {
@ -245,7 +245,7 @@ pub enum Request {
// https://www.jsonrpc.org/specification#response_object
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Success {
#[serde(skip_serializing_if = "Option::is_none")]
pub jsonrpc: Option<Version>,
@ -253,7 +253,7 @@ pub struct Success {
pub id: Id,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
pub struct Failure {
#[serde(skip_serializing_if = "Option::is_none")]
pub jsonrpc: Option<Version>,
@ -264,7 +264,7 @@ pub struct Failure {
// Note that failure comes first because we're not using
// #[serde(deny_unknown_field)]: we want a request that contains
// both `result` and `error` to be a `Failure`.
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Output {
Failure(Failure),
@ -280,7 +280,7 @@ impl From<Output> for Result<Value, Error> {
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Response {
Single(Output),

@ -32,7 +32,7 @@ helix-dap = { version = "0.6", path = "../helix-dap" }
helix-loader = { version = "0.6", path = "../helix-loader" }
anyhow = "1"
once_cell = "1.15"
once_cell = "1.16"
which = "4.2"

@ -6,7 +6,7 @@ const VERSION: &str = include_str!("../VERSION");
fn main() {
let git_hash = Command::new("git")
.args(&["rev-parse", "HEAD"])
.args(["rev-parse", "HEAD"])
.output()
.ok()
.filter(|output| output.status.success())

@ -180,7 +180,7 @@ impl Application {
} else if !args.files.is_empty() {
let first = &args.files[0].0; // we know it's not empty
if first.is_dir() {
std::env::set_current_dir(&first).context("set current dir")?;
std::env::set_current_dir(first).context("set current dir")?;
editor.new_file(Action::VerticalSplit);
let picker = ui::file_picker(".".into(), &config.load().editor);
compositor.push(Box::new(overlayed(picker)));
@ -240,7 +240,7 @@ impl Application {
#[cfg(windows)]
let signals = futures_util::stream::empty();
#[cfg(not(windows))]
let signals = Signals::new(&[signal::SIGTSTP, signal::SIGCONT, signal::SIGUSR1])
let signals = Signals::new([signal::SIGTSTP, signal::SIGCONT, signal::SIGUSR1])
.context("build signal handler")?;
let app = Self {

@ -8,7 +8,7 @@ use tui::text::Spans;
pub use typed::*;
use helix_core::{
comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes,
comment, coords_at_pos, encoding, find_first_non_whitespace_char, find_root, graphemes,
history::UndoKind,
increment::date_time::DateTimeIncrementor,
increment::{number::NumberIncrementor, Increment},
@ -1027,6 +1027,7 @@ fn goto_file_vsplit(cx: &mut Context) {
goto_file_impl(cx, Action::VerticalSplit);
}
/// Goto files in selection.
fn goto_file_impl(cx: &mut Context, action: Action) {
let (view, doc) = current_ref!(cx.editor);
let text = doc.text();
@ -1036,15 +1037,25 @@ fn goto_file_impl(cx: &mut Context, action: Action) {
.map(|r| text.slice(r.from()..r.to()).to_string())
.collect();
let primary = selections.primary();
if selections.len() == 1 && primary.to() - primary.from() == 1 {
let current_word = movement::move_next_long_word_start(
text.slice(..),
movement::move_prev_long_word_start(text.slice(..), primary, 1),
1,
// Checks whether there is only one selection with a width of 1
if selections.len() == 1 && primary.len() == 1 {
let count = cx.count();
let text_slice = text.slice(..);
// In this case it selects the WORD under the cursor
let current_word = textobject::textobject_word(
text_slice,
primary,
textobject::TextObject::Inside,
count,
true,
);
// Trims some surrounding chars so that the actual file is opened.
let surrounding_chars: &[_] = &['\'', '"', '(', ')'];
paths.clear();
paths.push(
text.slice(current_word.from()..current_word.to())
current_word
.fragment(text_slice)
.trim_matches(surrounding_chars)
.to_string(),
);
}
@ -1297,6 +1308,7 @@ fn replace(cx: &mut Context) {
});
apply_transaction(&transaction, doc, view);
exit_select_mode(cx);
}
})
}
@ -1353,7 +1365,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
let range = doc.selection(view.id).primary();
let text = doc.text().slice(..);
let cursor = coords_at_pos(text, range.cursor(text));
let cursor = visual_coords_at_pos(text, range.cursor(text), doc.tab_width());
let doc_last_line = doc.text().len_lines().saturating_sub(1);
let last_line = view.last_line(doc);
@ -1385,7 +1397,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
// If cursor needs moving, replace primary selection
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 head = pos_at_visual_coords(text, Position::new(line, cursor.col), doc.tab_width()); // this func will properly truncate to line end
let anchor = if cx.editor.mode == Mode::Select {
range.anchor
@ -2473,8 +2485,8 @@ impl ui::menu::Item for MappableCommand {
match self {
MappableCommand::Typable { doc, name, .. } => match keymap.get(name as &String) {
Some(bindings) => format!("{} ({}) [{}]", doc, fmt_binding(bindings), name).into(),
None => format!("{} [{}]", doc, name).into(),
Some(bindings) => format!("{} ({}) [:{}]", doc, fmt_binding(bindings), name).into(),
None => format!("{} [:{}]", doc, name).into(),
},
MappableCommand::Static { doc, name, .. } => match keymap.get(*name) {
Some(bindings) => format!("{} ({}) [{}]", doc, fmt_binding(bindings), name).into(),
@ -3252,6 +3264,7 @@ pub mod insert {
(Some(_x), Some(_y), Some(ap))
if range.is_single_grapheme(text)
&& ap.get(_x).is_some()
&& ap.get(_x).unwrap().open == _x
&& ap.get(_x).unwrap().close == _y =>
// delete both autopaired characters
{
@ -3499,7 +3512,12 @@ enum Paste {
}
fn paste_impl(values: &[String], doc: &mut Document, view: &mut View, action: Paste, count: usize) {
if values.is_empty() {
return;
}
let repeat = std::iter::repeat(
// `values` is asserted to have at least one entry above.
values
.last()
.map(|value| Tendril::from(value.repeat(count)))
@ -3523,6 +3541,8 @@ fn paste_impl(values: &[String], doc: &mut Document, view: &mut View, action: Pa
let text = doc.text();
let selection = doc.selection(view.id);
let mut ranges = SmallVec::with_capacity(selection.len());
let transaction = Transaction::change_by_selection(text, selection, |range| {
let pos = match (action, linewise) {
// paste linewise before
@ -3539,8 +3559,21 @@ fn paste_impl(values: &[String], doc: &mut Document, view: &mut View, action: Pa
// paste at cursor
(Paste::Cursor, _) => range.cursor(text.slice(..)),
};
(pos, pos, values.next())
let value = values.next();
let value_len = value
.as_ref()
.map(|content| content.chars().count())
.unwrap_or_default();
ranges.push(Range::new(pos, pos + value_len));
(pos, pos, value)
});
let transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
apply_transaction(&transaction, doc, view);
}
@ -3634,18 +3667,19 @@ fn replace_with_yanked(cx: &mut Context) {
});
apply_transaction(&transaction, doc, view);
exit_select_mode(cx);
}
}
}
fn replace_selections_with_clipboard_impl(
editor: &mut Editor,
cx: &mut Context,
clipboard_type: ClipboardType,
count: usize,
) -> anyhow::Result<()> {
let (view, doc) = current!(editor);
let count = cx.count();
let (view, doc) = current!(cx.editor);
match editor.clipboard_provider.get_contents(clipboard_type) {
match cx.editor.clipboard_provider.get_contents(clipboard_type) {
Ok(contents) => {
let selection = doc.selection(view.id);
let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
@ -3658,18 +3692,20 @@ fn replace_selections_with_clipboard_impl(
apply_transaction(&transaction, doc, view);
doc.append_changes_to_history(view.id);
Ok(())
}
Err(e) => Err(e.context("Couldn't get system clipboard contents")),
Err(e) => return Err(e.context("Couldn't get system clipboard contents")),
}
exit_select_mode(cx);
Ok(())
}
fn replace_selections_with_clipboard(cx: &mut Context) {
let _ = replace_selections_with_clipboard_impl(cx.editor, ClipboardType::Clipboard, cx.count());
let _ = replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard);
}
fn replace_selections_with_primary_clipboard(cx: &mut Context) {
let _ = replace_selections_with_clipboard_impl(cx.editor, ClipboardType::Selection, cx.count());
let _ = replace_selections_with_clipboard_impl(cx, ClipboardType::Selection);
}
fn paste(cx: &mut Context, pos: Paste) {
@ -4005,11 +4041,13 @@ pub fn completion(cx: &mut Context) {
};
if !prefix.is_empty() {
items.retain(|item| match &item.filter_text {
Some(t) => t.starts_with(&prefix),
None => item.label.starts_with(&prefix),
items.retain(|item| {
item.filter_text
.as_ref()
.unwrap_or(&item.label)
.starts_with(&prefix)
});
};
}
if items.is_empty() {
// editor.set_error("No completion available".to_string());
@ -4703,7 +4741,7 @@ fn shell_keep_pipe(cx: &mut Context) {
for (i, range) in selection.ranges().iter().enumerate() {
let fragment = range.slice(text);
let (_output, success) = match shell_impl(shell, input, Some(fragment)) {
let (_output, success) = match shell_impl(shell, input, Some(fragment.into())) {
Ok(result) => result,
Err(err) => {
cx.editor.set_error(err.to_string());
@ -4731,13 +4769,17 @@ fn shell_keep_pipe(cx: &mut Context) {
);
}
fn shell_impl(
fn shell_impl(shell: &[String], cmd: &str, input: Option<Rope>) -> anyhow::Result<(Tendril, bool)> {
tokio::task::block_in_place(|| helix_lsp::block_on(shell_impl_async(shell, cmd, input)))
}
async fn shell_impl_async(
shell: &[String],
cmd: &str,
input: Option<RopeSlice>,
input: Option<Rope>,
) -> anyhow::Result<(Tendril, bool)> {
use std::io::Write;
use std::process::{Command, Stdio};
use std::process::Stdio;
use tokio::process::Command;
ensure!(!shell.is_empty(), "No shell set");
let mut process = Command::new(&shell[0]);
@ -4749,6 +4791,8 @@ fn shell_impl(
if input.is_some() || cfg!(windows) {
process.stdin(Stdio::piped());
} else {
process.stdin(Stdio::null());
}
let mut process = match process.spawn() {
@ -4758,13 +4802,22 @@ fn shell_impl(
return Err(e.into());
}
};
if let Some(input) = input {
let mut stdin = process.stdin.take().unwrap();
for chunk in input.chunks() {
stdin.write_all(chunk.as_bytes())?;
}
}
let output = process.wait_with_output()?;
let output = if let Some(mut stdin) = process.stdin.take() {
let input_task = tokio::spawn(async move {
if let Some(input) = input {
helix_view::document::to_writer(&mut stdin, encoding::UTF_8, &input).await?;
}
Ok::<_, anyhow::Error>(())
});
let (output, _) = tokio::join! {
process.wait_with_output(),
input_task,
};
output?
} else {
// Process has no stdin, so we just take the output
process.wait_with_output().await?
};
if !output.status.success() {
if !output.stderr.is_empty() {
@ -4798,11 +4851,12 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
let selection = doc.selection(view.id);
let mut changes = Vec::with_capacity(selection.len());
let mut ranges = SmallVec::with_capacity(selection.len());
let text = doc.text().slice(..);
for range in selection.ranges() {
let fragment = range.slice(text);
let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment)) {
let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment.into())) {
Ok(result) => result,
Err(err) => {
cx.editor.set_error(err.to_string());
@ -4821,11 +4875,13 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
ShellBehavior::Append => (range.to(), range.to()),
_ => (range.from(), range.from()),
};
ranges.push(Range::new(to, to + output.chars().count()));
changes.push((from, to, Some(output)));
}
if behavior != &ShellBehavior::Ignore {
let transaction = Transaction::change(doc.text(), changes.into_iter());
let transaction = Transaction::change(doc.text(), changes.into_iter())
.with_selection(Selection::new(ranges, selection.primary_index()));
apply_transaction(&transaction, doc, view);
doc.append_changes_to_history(view.id);
}

@ -9,7 +9,7 @@ use tui::text::{Span, Spans};
use super::{align_view, push_jump, Align, Context, Editor, Open};
use helix_core::{path, Selection};
use helix_view::{apply_transaction, editor::Action, theme::Style};
use helix_view::{apply_transaction, document::Mode, editor::Action, theme::Style};
use crate::{
compositor::{self, Compositor},
@ -57,7 +57,7 @@ impl ui::menu::Item for lsp::Location {
// allocation, for `to_file_path`, else there will be two (2), with `to_string_lossy`.
let mut write_path_to_res = || -> Option<()> {
let path = self.uri.to_file_path().ok()?;
res.push_str(&path.strip_prefix(&cwdir).unwrap_or(&path).to_string_lossy());
res.push_str(&path.strip_prefix(cwdir).unwrap_or(&path).to_string_lossy());
Some(())
};
write_path_to_res();
@ -670,7 +670,7 @@ pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> {
// Create directory if it does not exist
if let Some(dir) = path.parent() {
if !dir.is_dir() {
fs::create_dir_all(&dir)?;
fs::create_dir_all(dir)?;
}
}
@ -946,7 +946,7 @@ pub fn goto_reference(cx: &mut Context) {
);
}
#[derive(PartialEq)]
#[derive(PartialEq, Eq)]
pub enum SignatureHelpInvoked {
Manual,
Automatic,
@ -993,6 +993,14 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
return;
}
// If the signature help invocation is automatic, don't show it outside of Insert Mode:
// it very probably means the server was a little slow to respond and the user has
// already moved on to something else, making a signature help popup will just be an
// annoyance, see https://github.com/helix-editor/helix/issues/3112
if !was_manually_invoked && editor.mode != Mode::Insert {
return;
}
let response = match response {
// According to the spec the response should be None if there
// are no signatures, but some servers don't follow this.

@ -797,16 +797,21 @@ fn theme(
};
}
PromptEvent::Validate => {
let theme_name = args.first().with_context(|| "Theme name not provided")?;
let theme = cx
.editor
.theme_loader
.load(theme_name)
.with_context(|| "Theme does not exist")?;
if !(true_color || theme.is_16_color()) {
bail!("Unsupported theme: theme requires true color support");
if let Some(theme_name) = args.first() {
let theme = cx
.editor
.theme_loader
.load(theme_name)
.with_context(|| "Theme does not exist")?;
if !(true_color || theme.is_16_color()) {
bail!("Unsupported theme: theme requires true color support");
}
cx.editor.set_theme(theme);
} else {
let name = cx.editor.theme.name().to_string();
cx.editor.set_status(name);
}
cx.editor.set_theme(theme);
}
};
@ -1054,6 +1059,24 @@ fn reload(
})
}
/// Update the [`Document`] if it has been modified.
fn update(
cx: &mut compositor::Context,
args: &[Cow<str>],
event: PromptEvent,
) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}
let (_view, doc) = current!(cx.editor);
if doc.is_modified() {
write(cx, args, event)
} else {
Ok(())
}
}
fn lsp_restart(
cx: &mut compositor::Context,
_args: &[Cow<str>],
@ -1880,7 +1903,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "theme",
aliases: &[],
doc: "Change the editor theme.",
doc: "Change the editor theme (show current theme if no name specified).",
fun: theme,
completer: Some(completers::theme),
},
@ -1989,6 +2012,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
fun: reload,
completer: None,
},
TypableCommand {
name: "update",
aliases: &[],
doc: "Write changes only if the file has been modified.",
fun: update,
completer: None,
},
TypableCommand {
name: "lsp-restart",
aliases: &[],
@ -2192,12 +2222,10 @@ pub(super) fn command_mode(cx: &mut Context) {
static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
// we use .this over split_whitespace() because we care about empty segments
let parts = input.split(' ').collect::<Vec<&str>>();
// simple heuristic: if there's no just one part, complete command name.
// if there's a space, per command completion kicks in.
if parts.len() <= 1 {
// we use .this over split_whitespace() because we care about empty segments
if input.split(' ').count() <= 1 {
let mut matches: Vec<_> = typed::TYPABLE_COMMAND_LIST
.iter()
.filter_map(|command| {
@ -2213,12 +2241,13 @@ pub(super) fn command_mode(cx: &mut Context) {
.map(|(name, _)| (0.., name.into()))
.collect()
} else {
let parts = shellwords::shellwords(input);
let part = parts.last().unwrap();
if let Some(typed::TypableCommand {
completer: Some(completer),
..
}) = typed::TYPABLE_COMMAND_MAP.get(parts[0])
}) = typed::TYPABLE_COMMAND_MAP.get(&parts[0] as &str)
{
completer(editor, part)
.into_iter()

@ -92,11 +92,15 @@ impl Completion {
pub fn new(
editor: &Editor,
items: Vec<CompletionItem>,
mut items: Vec<CompletionItem>,
offset_encoding: helix_lsp::OffsetEncoding,
start_offset: usize,
trigger_offset: usize,
) -> Self {
// Sort completion items according to their preselect status (given by the LSP server)
items.sort_by_key(|item| !item.preselect.unwrap_or(false));
// Then create the menu
let menu = Menu::new(items, true, (), move |editor: &mut Editor, item, event| {
fn item_to_transaction(
doc: &Document,

@ -25,7 +25,7 @@ use helix_view::{
keyboard::{KeyCode, KeyModifiers},
Document, Editor, Theme, View,
};
use std::{borrow::Cow, cmp::min, path::PathBuf};
use std::{borrow::Cow, cmp::min, num::NonZeroUsize, path::PathBuf};
use tui::buffer::Buffer as Surface;
@ -229,16 +229,16 @@ impl EditorView {
_theme: &Theme,
) -> Box<dyn Iterator<Item = HighlightEvent> + 'doc> {
let text = doc.text().slice(..);
let last_line = std::cmp::min(
// Saturating subs to make it inclusive zero indexing.
(offset.row + height as usize).saturating_sub(1),
doc.text().len_lines().saturating_sub(1),
);
let range = {
// calculate viewport byte ranges
let start = text.line_to_byte(offset.row);
let end = text.line_to_byte(last_line + 1);
// Calculate viewport byte ranges:
// Saturating subs to make it inclusive zero indexing.
let last_line = doc.text().len_lines().saturating_sub(1);
let last_visible_line = (offset.row + height as usize)
.saturating_sub(1)
.min(last_line);
let start = text.line_to_byte(offset.row.min(last_line));
let end = text.line_to_byte(last_visible_line + 1);
start..end
};
@ -1025,37 +1025,40 @@ impl EditorView {
}
// special handling for repeat operator
(key!('.'), _) if self.keymaps.pending().is_empty() => {
// first execute whatever put us into insert mode
self.last_insert.0.execute(cxt);
// then replay the inputs
for key in self.last_insert.1.clone() {
match key {
InsertEvent::Key(key) => self.insert_mode(cxt, key),
InsertEvent::CompletionApply(compl) => {
let (view, doc) = current!(cxt.editor);
doc.restore(view);
let text = doc.text().slice(..);
let cursor = doc.selection(view.id).primary().cursor(text);
let shift_position =
|pos: usize| -> usize { pos + cursor - compl.trigger_offset };
let tx = Transaction::change(
doc.text(),
compl.changes.iter().cloned().map(|(start, end, t)| {
(shift_position(start), shift_position(end), t)
}),
);
apply_transaction(&tx, doc, view);
}
InsertEvent::TriggerCompletion => {
let (_, doc) = current!(cxt.editor);
doc.savepoint();
for _ in 0..cxt.editor.count.map_or(1, NonZeroUsize::into) {
// first execute whatever put us into insert mode
self.last_insert.0.execute(cxt);
// then replay the inputs
for key in self.last_insert.1.clone() {
match key {
InsertEvent::Key(key) => self.insert_mode(cxt, key),
InsertEvent::CompletionApply(compl) => {
let (view, doc) = current!(cxt.editor);
doc.restore(view);
let text = doc.text().slice(..);
let cursor = doc.selection(view.id).primary().cursor(text);
let shift_position =
|pos: usize| -> usize { pos + cursor - compl.trigger_offset };
let tx = Transaction::change(
doc.text(),
compl.changes.iter().cloned().map(|(start, end, t)| {
(shift_position(start), shift_position(end), t)
}),
);
apply_transaction(&tx, doc, view);
}
InsertEvent::TriggerCompletion => {
let (_, doc) = current!(cxt.editor);
doc.savepoint();
}
}
}
}
cxt.editor.count = None;
}
_ => {
// set the count

@ -40,7 +40,7 @@ impl Item for PathBuf {
type Data = PathBuf;
fn label(&self, root_path: &Self::Data) -> Spans {
self.strip_prefix(&root_path)
self.strip_prefix(root_path)
.unwrap_or(self)
.to_string_lossy()
.into()
@ -118,7 +118,8 @@ impl<T: Item> Menu<T> {
.map(|score| (index, score))
}),
);
self.matches.sort_unstable_by_key(|(_, score)| -score);
// Order of equal elements needs to be preserved as LSP preselected items come in order of high to low priority
self.matches.sort_by_key(|(_, score)| -score);
// reset cursor position
self.cursor = None;

@ -248,8 +248,14 @@ impl<T: Item + 'static> Component for FilePicker<T> {
let offset = Position::new(first_line, 0);
let highlights =
let mut highlights =
EditorView::doc_syntax_highlights(doc, offset, area.height, &cx.editor.theme);
for spans in EditorView::doc_diagnostics_highlights(doc, &cx.editor.theme) {
if spans.is_empty() {
continue;
}
highlights = Box::new(helix_core::syntax::merge(highlights, spans));
}
EditorView::render_text_highlights(
doc,
offset,

@ -1,5 +1,6 @@
use crate::compositor::{Component, Compositor, Context, Event, EventResult};
use crate::{alt, ctrl, key, shift, ui};
use helix_core::shellwords;
use helix_view::input::KeyEvent;
use helix_view::keyboard::KeyCode;
use std::{borrow::Cow, ops::RangeFrom};
@ -31,7 +32,7 @@ pub struct Prompt {
next_char_handler: Option<PromptCharHandler>,
}
#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PromptEvent {
/// The prompt input has been updated.
Update,
@ -335,7 +336,10 @@ impl Prompt {
let (range, item) = &self.completion[index];
self.line.replace_range(range.clone(), item);
// since we are using shellwords to parse arguments, make sure
// that whitespace in files is properly escaped.
let item = shellwords::escape(item);
self.line.replace_range(range.clone(), &item);
self.move_end();
}
@ -404,7 +408,7 @@ impl Prompt {
surface.set_stringn(
area.x + col * (1 + col_width),
area.y + row,
&completion,
completion,
col_width.saturating_sub(1) as usize,
color,
);

@ -1,6 +1,7 @@
use std::ops::RangeInclusive;
use helix_core::diagnostic::Severity;
use helix_term::application::Application;
use super::*;
@ -133,3 +134,62 @@ async fn test_selection_duplication() -> anyhow::Result<()> {
.await?;
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn test_goto_file_impl() -> anyhow::Result<()> {
let file = tempfile::NamedTempFile::new()?;
fn match_paths(app: &Application, matches: Vec<&str>) -> usize {
app.editor
.documents()
.filter_map(|d| d.path()?.file_name())
.filter(|n| matches.iter().any(|m| *m == n.to_string_lossy()))
.count()
}
// Single selection
test_key_sequence(
&mut AppBuilder::new().with_file(file.path(), None).build()?,
Some("ione.js<esc>%gf"),
Some(&|app| {
assert_eq!(1, match_paths(app, vec!["one.js"]));
}),
false,
)
.await?;
// Multiple selection
test_key_sequence(
&mut AppBuilder::new().with_file(file.path(), None).build()?,
Some("ione.js<ret>two.js<esc>%<A-s>gf"),
Some(&|app| {
assert_eq!(2, match_paths(app, vec!["one.js", "two.js"]));
}),
false,
)
.await?;
// Cursor on first quote
test_key_sequence(
&mut AppBuilder::new().with_file(file.path(), None).build()?,
Some("iimport 'one.js'<esc>B;gf"),
Some(&|app| {
assert_eq!(1, match_paths(app, vec!["one.js"]));
}),
false,
)
.await?;
// Cursor on last quote
test_key_sequence(
&mut AppBuilder::new().with_file(file.path(), None).build()?,
Some("iimport 'one.js'<esc>bgf"),
Some(&|app| {
assert_eq!(1, match_paths(app, vec!["one.js"]));
}),
false,
)
.await?;
Ok(())
}

@ -6,7 +6,7 @@ use unicode_segmentation::UnicodeSegmentation;
use helix_view::graphics::{Color, Modifier, Rect, Style, UnderlineStyle};
/// A buffer cell
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cell {
pub symbol: String,
pub fg: Color,
@ -119,7 +119,7 @@ impl Default for Cell {
/// buf[(5, 0)].set_char('x');
/// assert_eq!(buf[(5, 0)].symbol, "x");
/// ```
#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Buffer {
/// The area represented by this buffer
pub area: Rect,
@ -137,7 +137,7 @@ impl Buffer {
/// Returns a Buffer with all cells initialized with the attributes of the given Cell
pub fn filled(area: Rect, cell: &Cell) -> Buffer {
let size = area.area() as usize;
let size = area.area();
let mut content = Vec::with_capacity(size);
for _ in 0..size {
content.push(cell.clone());
@ -239,7 +239,7 @@ impl Buffer {
y,
self.area
);
((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
((y - self.area.y) as usize) * (self.area.width as usize) + ((x - self.area.x) as usize)
}
/// Returns the index in the Vec<Cell> for the given global (x, y) coordinates,
@ -278,8 +278,8 @@ impl Buffer {
self.content.len()
);
(
self.area.x + i as u16 % self.area.width,
self.area.y + i as u16 / self.area.width,
(self.area.x as usize + (i % self.area.width as usize)) as u16,
(self.area.y as usize + (i / self.area.width as usize)) as u16,
)
}
@ -480,7 +480,7 @@ impl Buffer {
/// Resize the buffer so that the mapped area matches the given area and that the buffer
/// length is equal to area.width * area.height
pub fn resize(&mut self, area: Rect) {
let length = area.area() as usize;
let length = area.area();
if self.content.len() > length {
self.content.truncate(length);
} else {
@ -587,8 +587,8 @@ impl Buffer {
let mut to_skip: usize = 0;
for (i, (current, previous)) in next_buffer.iter().zip(previous_buffer.iter()).enumerate() {
if (current != previous || invalidated > 0) && to_skip == 0 {
let x = i as u16 % width;
let y = i as u16 / width;
let x = (i % width as usize) as u16;
let y = (i / width as usize) as u16;
updates.push((x, y, &next_buffer[i]));
}

@ -46,7 +46,7 @@ impl Constraint {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Alignment {
Left,
Center,

@ -53,14 +53,14 @@ use std::borrow::Cow;
use unicode_segmentation::UnicodeSegmentation;
/// A grapheme associated to a style.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StyledGrapheme<'a> {
pub symbol: &'a str,
pub style: Style,
}
/// A string where all graphemes have the same style.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Span<'a> {
pub content: Cow<'a, str>,
pub style: Style,
@ -209,7 +209,7 @@ impl<'a> From<Cow<'a, str>> for Span<'a> {
}
/// A string composed of clusters of graphemes, each with their own style.
#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Spans<'a>(pub Vec<Span<'a>>);
impl<'a> Spans<'a> {
@ -297,7 +297,7 @@ impl<'a> From<&Spans<'a>> for String {
/// text.extend(Text::styled("Some more lines\nnow with more style!", style));
/// assert_eq!(6, text.height());
/// ```
#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Text<'a> {
pub lines: Vec<Spans<'a>>,
}

@ -7,7 +7,7 @@ use crate::{
use helix_view::graphics::{Rect, Style};
/// Border render type. Defaults to [`BorderType::Plain`].
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BorderType {
Plain,
Rounded,
@ -47,7 +47,7 @@ impl Default for BorderType {
/// .border_type(BorderType::Rounded)
/// .style(Style::default().bg(Color::Black));
/// ```
#[derive(Debug, Default, Clone, PartialEq)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Block<'a> {
/// Optional title place on the upper left of the block
title: Option<Spans<'a>>,
@ -187,16 +187,8 @@ impl<'a> Widget for Block<'a> {
}
if let Some(title) = self.title {
let lx = if self.borders.intersects(Borders::LEFT) {
1
} else {
0
};
let rx = if self.borders.intersects(Borders::RIGHT) {
1
} else {
0
};
let lx = u16::from(self.borders.intersects(Borders::LEFT));
let rx = u16::from(self.borders.intersects(Borders::RIGHT));
let width = area.width.saturating_sub(lx).saturating_sub(rx);
buf.set_spans(area.left() + lx, area.top(), &title, width);
}

@ -34,7 +34,7 @@ use std::collections::HashMap;
///
/// You can apply a [`Style`] on the entire [`Cell`] using [`Cell::style`] or rely on the styling
/// capabilities of [`Text`].
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Cell<'a> {
pub content: Text<'a>,
style: Style,
@ -79,7 +79,7 @@ where
/// ```
///
/// By default, a row has a height of 1 but you can change this using [`Row::height`].
#[derive(Debug, Clone, PartialEq, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Row<'a> {
pub cells: Vec<Cell<'a>>,
height: u16,
@ -179,7 +179,7 @@ impl<'a> Row<'a> {
/// // ...and potentially show a symbol in front of the selection.
/// .highlight_symbol(">>");
/// ```
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Table<'a> {
/// A block to wrap the widget in
block: Option<Block<'a>>,

@ -4,12 +4,12 @@ use helix_tui::{
};
#[test]
fn terminal_buffer_size_should_be_limited() {
fn terminal_buffer_size_should_not_be_limited() {
let backend = TestBackend::new(400, 400);
let terminal = Terminal::new(backend).unwrap();
let size = terminal.backend().size().unwrap();
assert_eq!(size.width, 255);
assert_eq!(size.height, 255);
assert_eq!(size.width, 400);
assert_eq!(size.height, 400);
}
// #[test]

@ -23,7 +23,7 @@ helix-dap = { version = "0.6", path = "../helix-dap" }
crossterm = { version = "0.25", optional = true }
# Conversion traits
once_cell = "1.15"
once_cell = "1.16"
url = "2"
arc-swap = { version = "1.5.1" }

@ -0,0 +1,163 @@
// A minimal base64 implementation to keep from pulling in a crate for just that. It's based on
// https://github.com/marshallpierce/rust-base64 but without all the customization options.
// The biggest portion comes from
// https://github.com/marshallpierce/rust-base64/blob/a675443d327e175f735a37f574de803d6a332591/src/engine/naive.rs#L42
// Thanks, rust-base64!
// The MIT License (MIT)
// Copyright (c) 2015 Alice Maz
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
use std::ops::{BitAnd, BitOr, Shl, Shr};
const PAD_BYTE: u8 = b'=';
const ENCODE_TABLE: &[u8] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".as_bytes();
const LOW_SIX_BITS: u32 = 0x3F;
pub fn encode(input: &[u8]) -> String {
let rem = input.len() % 3;
let complete_chunks = input.len() / 3;
let remainder_chunk = if rem == 0 { 0 } else { 1 };
let encoded_size = (complete_chunks + remainder_chunk) * 4;
let mut output = vec![0; encoded_size];
// complete chunks first
let complete_chunk_len = input.len() - rem;
let mut input_index = 0_usize;
let mut output_index = 0_usize;
while input_index < complete_chunk_len {
let chunk = &input[input_index..input_index + 3];
// populate low 24 bits from 3 bytes
let chunk_int: u32 =
(chunk[0] as u32).shl(16) | (chunk[1] as u32).shl(8) | (chunk[2] as u32);
// encode 4x 6-bit output bytes
output[output_index] = ENCODE_TABLE[chunk_int.shr(18) as usize];
output[output_index + 1] = ENCODE_TABLE[chunk_int.shr(12_u8).bitand(LOW_SIX_BITS) as usize];
output[output_index + 2] = ENCODE_TABLE[chunk_int.shr(6_u8).bitand(LOW_SIX_BITS) as usize];
output[output_index + 3] = ENCODE_TABLE[chunk_int.bitand(LOW_SIX_BITS) as usize];
input_index += 3;
output_index += 4;
}
// then leftovers
if rem == 2 {
let chunk = &input[input_index..input_index + 2];
// high six bits of chunk[0]
output[output_index] = ENCODE_TABLE[chunk[0].shr(2) as usize];
// bottom 2 bits of [0], high 4 bits of [1]
output[output_index + 1] = ENCODE_TABLE
[(chunk[0].shl(4_u8).bitor(chunk[1].shr(4_u8)) as u32).bitand(LOW_SIX_BITS) as usize];
// bottom 4 bits of [1], with the 2 bottom bits as zero
output[output_index + 2] =
ENCODE_TABLE[(chunk[1].shl(2_u8) as u32).bitand(LOW_SIX_BITS) as usize];
output[output_index + 3] = PAD_BYTE;
} else if rem == 1 {
let byte = input[input_index];
output[output_index] = ENCODE_TABLE[byte.shr(2) as usize];
output[output_index + 1] =
ENCODE_TABLE[(byte.shl(4_u8) as u32).bitand(LOW_SIX_BITS) as usize];
output[output_index + 2] = PAD_BYTE;
output[output_index + 3] = PAD_BYTE;
}
String::from_utf8(output).expect("Invalid UTF8")
}
#[cfg(test)]
mod tests {
fn compare_encode(expected: &str, target: &[u8]) {
assert_eq!(expected, super::encode(target));
}
#[test]
fn encode_rfc4648_0() {
compare_encode("", b"");
}
#[test]
fn encode_rfc4648_1() {
compare_encode("Zg==", b"f");
}
#[test]
fn encode_rfc4648_2() {
compare_encode("Zm8=", b"fo");
}
#[test]
fn encode_rfc4648_3() {
compare_encode("Zm9v", b"foo");
}
#[test]
fn encode_rfc4648_4() {
compare_encode("Zm9vYg==", b"foob");
}
#[test]
fn encode_rfc4648_5() {
compare_encode("Zm9vYmE=", b"fooba");
}
#[test]
fn encode_rfc4648_6() {
compare_encode("Zm9vYmFy", b"foobar");
}
#[test]
fn encode_all_ascii() {
let mut ascii = Vec::<u8>::with_capacity(128);
for i in 0..128 {
ascii.push(i);
}
compare_encode(
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7P\
D0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8\
=",
&ascii,
);
}
#[test]
fn encode_all_bytes() {
let mut bytes = Vec::<u8>::with_capacity(256);
for i in 0..255 {
bytes.push(i);
}
bytes.push(255); //bug with "overflowing" ranges?
compare_encode(
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7P\
D0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn\
+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6\
/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==",
&bytes,
);
}
}

@ -3,6 +3,7 @@
use anyhow::Result;
use std::borrow::Cow;
#[derive(Clone, Copy, Debug)]
pub enum ClipboardType {
Clipboard,
Selection,
@ -72,45 +73,48 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
#[cfg(target_os = "macos")]
pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
use provider::command::exists;
use crate::env::binary_exists;
if exists("pbcopy") && exists("pbpaste") {
if binary_exists("pbcopy") && binary_exists("pbpaste") {
command_provider! {
paste => "pbpaste";
copy => "pbcopy";
}
} else {
Box::new(provider::NopProvider::new())
Box::new(provider::FallbackProvider::new())
}
}
#[cfg(target_os = "wasm32")]
pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
// TODO:
Box::new(provider::NopProvider::new())
Box::new(provider::FallbackProvider::new())
}
#[cfg(not(any(windows, target_os = "wasm32", target_os = "macos")))]
pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
use provider::command::{env_var_is_set, exists, is_exit_success};
use crate::env::{binary_exists, env_var_is_set};
use provider::command::is_exit_success;
// TODO: support for user-defined provider, probably when we have plugin support by setting a
// variable?
if env_var_is_set("WAYLAND_DISPLAY") && exists("wl-copy") && exists("wl-paste") {
if env_var_is_set("WAYLAND_DISPLAY") && binary_exists("wl-copy") && binary_exists("wl-paste") {
command_provider! {
paste => "wl-paste", "--no-newline";
copy => "wl-copy", "--type", "text/plain";
primary_paste => "wl-paste", "-p", "--no-newline";
primary_copy => "wl-copy", "-p", "--type", "text/plain";
}
} else if env_var_is_set("DISPLAY") && exists("xclip") {
} else if env_var_is_set("DISPLAY") && binary_exists("xclip") {
command_provider! {
paste => "xclip", "-o", "-selection", "clipboard";
copy => "xclip", "-i", "-selection", "clipboard";
primary_paste => "xclip", "-o";
primary_copy => "xclip", "-i";
}
} else if env_var_is_set("DISPLAY") && exists("xsel") && is_exit_success("xsel", &["-o", "-b"])
} else if env_var_is_set("DISPLAY")
&& binary_exists("xsel")
&& is_exit_success("xsel", &["-o", "-b"])
{
// FIXME: check performance of is_exit_success
command_provider! {
@ -119,43 +123,78 @@ pub fn get_clipboard_provider() -> Box<dyn ClipboardProvider> {
primary_paste => "xsel", "-o";
primary_copy => "xsel", "-i";
}
} else if exists("win32yank.exe") {
} else if binary_exists("win32yank.exe") {
command_provider! {
paste => "win32yank.exe", "-o", "--lf";
copy => "win32yank.exe", "-i", "--crlf";
}
} else if exists("termux-clipboard-set") && exists("termux-clipboard-get") {
} else if binary_exists("termux-clipboard-set") && binary_exists("termux-clipboard-get") {
command_provider! {
paste => "termux-clipboard-get";
copy => "termux-clipboard-set";
}
} else if env_var_is_set("TMUX") && exists("tmux") {
} else if env_var_is_set("TMUX") && binary_exists("tmux") {
command_provider! {
paste => "tmux", "save-buffer", "-";
copy => "tmux", "load-buffer", "-";
}
} else {
Box::new(provider::NopProvider::new())
Box::new(provider::FallbackProvider::new())
}
}
#[cfg(not(target_os = "windows"))]
pub mod provider {
use super::{ClipboardProvider, ClipboardType};
use anyhow::Result;
use std::borrow::Cow;
#[cfg(not(target_os = "windows"))]
#[cfg(feature = "term")]
mod osc52 {
use {super::ClipboardType, crate::base64, crossterm};
#[derive(Debug)]
pub struct SetClipboardCommand {
encoded_content: String,
clipboard_type: ClipboardType,
}
impl SetClipboardCommand {
pub fn new(content: &str, clipboard_type: ClipboardType) -> Self {
Self {
encoded_content: base64::encode(content.as_bytes()),
clipboard_type,
}
}
}
impl crossterm::Command for SetClipboardCommand {
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
let kind = match &self.clipboard_type {
ClipboardType::Clipboard => "c",
ClipboardType::Selection => "p",
};
// Send an OSC 52 set command: https://terminalguide.namepad.de/seq/osc-52/
write!(f, "\x1b]52;{};{}\x1b\\", kind, &self.encoded_content)
}
}
}
#[derive(Debug)]
pub struct NopProvider {
pub struct FallbackProvider {
buf: String,
primary_buf: String,
}
#[cfg(not(target_os = "windows"))]
impl NopProvider {
impl FallbackProvider {
pub fn new() -> Self {
#[cfg(feature = "term")]
log::debug!(
"No native clipboard provider found. Yanking by OSC 52 and pasting will be internal to Helix"
);
#[cfg(not(feature = "term"))]
log::warn!(
"No clipboard provider found! Yanking and pasting will be internal to Helix"
"No native clipboard provider found! Yanking and pasting will be internal to Helix"
);
Self {
buf: String::new(),
@ -164,20 +203,27 @@ pub mod provider {
}
}
#[cfg(not(target_os = "windows"))]
impl Default for NopProvider {
impl Default for FallbackProvider {
fn default() -> Self {
Self::new()
}
}
#[cfg(not(target_os = "windows"))]
impl ClipboardProvider for NopProvider {
impl ClipboardProvider for FallbackProvider {
#[cfg(feature = "term")]
fn name(&self) -> Cow<str> {
Cow::Borrowed("termcode")
}
#[cfg(not(feature = "term"))]
fn name(&self) -> Cow<str> {
Cow::Borrowed("none")
}
fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
// This is the same noop if term is enabled or not.
// We don't use the get side of OSC 52 as it isn't often enabled, it's a security hole,
// and it would require this to be async to listen for the response
let value = match clipboard_type {
ClipboardType::Clipboard => self.buf.clone(),
ClipboardType::Selection => self.primary_buf.clone(),
@ -187,6 +233,12 @@ pub mod provider {
}
fn set_contents(&mut self, content: String, clipboard_type: ClipboardType) -> Result<()> {
#[cfg(feature = "term")]
crossterm::execute!(
std::io::stdout(),
osc52::SetClipboardCommand::new(&content, clipboard_type)
)?;
// Set our internal variables to use in get_content regardless of using OSC 52
match clipboard_type {
ClipboardType::Clipboard => self.buf = content,
ClipboardType::Selection => self.primary_buf = content,
@ -195,52 +247,11 @@ pub mod provider {
}
}
#[cfg(target_os = "windows")]
#[derive(Default, Debug)]
pub struct WindowsProvider;
#[cfg(target_os = "windows")]
impl ClipboardProvider for WindowsProvider {
fn name(&self) -> Cow<str> {
log::info!("Using clipboard-win to interact with the system clipboard");
Cow::Borrowed("clipboard-win")
}
fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
match clipboard_type {
ClipboardType::Clipboard => {
let contents = clipboard_win::get_clipboard(clipboard_win::formats::Unicode)?;
Ok(contents)
}
ClipboardType::Selection => Ok(String::new()),
}
}
fn set_contents(&mut self, contents: String, clipboard_type: ClipboardType) -> Result<()> {
match clipboard_type {
ClipboardType::Clipboard => {
clipboard_win::set_clipboard(clipboard_win::formats::Unicode, contents)?;
}
ClipboardType::Selection => {}
};
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
pub mod command {
use super::*;
use anyhow::{bail, Context as _, Result};
pub fn exists(executable_name: &str) -> bool {
which::which(executable_name).is_ok()
}
#[cfg(not(windows))]
pub fn env_var_is_set(env_var_name: &str) -> bool {
std::env::var_os(env_var_name).is_some()
}
#[cfg(not(any(windows, target_os = "macos")))]
pub fn is_exit_success(program: &str, args: &[&str]) -> bool {
std::process::Command::new(program)
@ -343,3 +354,40 @@ pub mod provider {
}
}
}
#[cfg(target_os = "windows")]
mod provider {
use super::{ClipboardProvider, ClipboardType};
use anyhow::Result;
use std::borrow::Cow;
#[derive(Default, Debug)]
pub struct WindowsProvider;
impl ClipboardProvider for WindowsProvider {
fn name(&self) -> Cow<str> {
log::info!("Using clipboard-win to interact with the system clipboard");
Cow::Borrowed("clipboard-win")
}
fn get_contents(&self, clipboard_type: ClipboardType) -> Result<String> {
match clipboard_type {
ClipboardType::Clipboard => {
let contents = clipboard_win::get_clipboard(clipboard_win::formats::Unicode)?;
Ok(contents)
}
ClipboardType::Selection => Ok(String::new()),
}
}
fn set_contents(&mut self, contents: String, clipboard_type: ClipboardType) -> Result<()> {
match clipboard_type {
ClipboardType::Clipboard => {
clipboard_win::set_clipboard(clipboard_win::formats::Unicode, contents)?;
}
ClipboardType::Selection => {}
};
Ok(())
}
}
}

@ -111,21 +111,21 @@ impl Default for FilePickerConfig {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ExplorerStyle {
Tree,
List,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ExplorerPosition {
Embed,
Overlay,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
pub struct ExplorerConfig {
pub style: ExplorerStyle,
@ -162,7 +162,7 @@ impl Default for ExplorerConfig {
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
pub struct Config {
/// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
@ -241,9 +241,9 @@ pub struct TerminalConfig {
#[cfg(windows)]
pub fn get_terminal_provider() -> Option<TerminalConfig> {
use crate::clipboard::provider::command::exists;
use crate::env::binary_exists;
if exists("wt") {
if binary_exists("wt") {
return Some(TerminalConfig {
command: "wt".to_string(),
args: vec![
@ -264,16 +264,16 @@ pub fn get_terminal_provider() -> Option<TerminalConfig> {
#[cfg(not(any(windows, target_os = "wasm32")))]
pub fn get_terminal_provider() -> Option<TerminalConfig> {
use crate::clipboard::provider::command::{env_var_is_set, exists};
use crate::env::{binary_exists, env_var_is_set};
if env_var_is_set("TMUX") && exists("tmux") {
if env_var_is_set("TMUX") && binary_exists("tmux") {
return Some(TerminalConfig {
command: "tmux".to_string(),
args: vec!["split-window".to_string()],
});
}
if env_var_is_set("WEZTERM_UNIX_SOCKET") && exists("wezterm") {
if env_var_is_set("WEZTERM_UNIX_SOCKET") && binary_exists("wezterm") {
return Some(TerminalConfig {
command: "wezterm".to_string(),
args: vec!["cli".to_string(), "split-pane".to_string()],
@ -409,7 +409,7 @@ pub enum StatusLineElement {
// Cursor shape is read and used on every rendered frame and so needs
// to be fast. Therefore we avoid a hashmap and use an enum indexed array.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CursorShapeConfig([CursorKind; 3]);
impl CursorShapeConfig {
@ -1328,9 +1328,11 @@ impl Editor {
pub fn focus(&mut self, view_id: ViewId) {
let prev_id = std::mem::replace(&mut self.tree.focus, view_id);
// if leaving the view: mode should reset
// if leaving the view: mode should reset and the cursor should be
// within view
if prev_id != view_id {
self.mode = Mode::Normal;
self.ensure_cursor_in_view(view_id);
}
}
@ -1339,9 +1341,11 @@ impl Editor {
self.tree.focus_next();
let id = self.tree.focus;
// if leaving the view: mode should reset
// if leaving the view: mode should reset and the cursor should be
// within view
if prev_id != id {
self.mode = Mode::Normal;
self.ensure_cursor_in_view(id);
}
}

@ -0,0 +1,8 @@
pub fn binary_exists(binary_name: &str) -> bool {
which::which(binary_name).is_ok()
}
#[cfg(not(windows))]
pub fn env_var_is_set(env_var_name: &str) -> bool {
std::env::var_os(env_var_name).is_some()
}

@ -5,7 +5,7 @@ use std::{
str::FromStr,
};
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
/// UNSTABLE
pub enum CursorKind {
@ -95,31 +95,19 @@ pub struct Rect {
}
impl Rect {
/// Creates a new rect, with width and height limited to keep the area under max u16.
/// If clipped, aspect ratio will be preserved.
/// Creates a new rect, with width and height
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Rect {
let max_area = u16::max_value();
let (clipped_width, clipped_height) =
if u32::from(width) * u32::from(height) > u32::from(max_area) {
let aspect_ratio = f64::from(width) / f64::from(height);
let max_area_f = f64::from(max_area);
let height_f = (max_area_f / aspect_ratio).sqrt();
let width_f = height_f * aspect_ratio;
(width_f as u16, height_f as u16)
} else {
(width, height)
};
Rect {
x,
y,
width: clipped_width,
height: clipped_height,
width,
height,
}
}
#[inline]
pub fn area(self) -> u16 {
self.width * self.height
pub fn area(self) -> usize {
(self.width as usize) * (self.height as usize)
}
#[inline]
@ -262,7 +250,7 @@ impl Rect {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Color {
Reset,
@ -315,7 +303,7 @@ impl From<Color> for crossterm::style::Color {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnderlineStyle {
Reset,
Line,
@ -461,7 +449,7 @@ impl FromStr for Modifier {
/// buffer[(0, 0)].style(),
/// );
/// ```
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Style {
pub fg: Option<Color>,
@ -630,33 +618,6 @@ impl Style {
mod tests {
use super::*;
#[test]
fn test_rect_size_truncation() {
for width in 256u16..300u16 {
for height in 256u16..300u16 {
let rect = Rect::new(0, 0, width, height);
rect.area(); // Should not panic.
assert!(rect.width < width || rect.height < height);
// The target dimensions are rounded down so the math will not be too precise
// but let's make sure the ratios don't diverge crazily.
assert!(
(f64::from(rect.width) / f64::from(rect.height)
- f64::from(width) / f64::from(height))
.abs()
< 1.0
)
}
}
// One dimension below 255, one above. Area above max u16.
let width = 900;
let height = 100;
let rect = Rect::new(0, 0, width, height);
assert_ne!(rect.width, 900);
assert_ne!(rect.height, 100);
assert!(rect.width < width || rect.height < height);
}
#[test]
fn test_rect_size_preservation() {
for width in 0..256u16 {

@ -4,12 +4,14 @@ pub mod macros;
pub mod clipboard;
pub mod document;
pub mod editor;
pub mod env;
pub mod graphics;
pub mod gutter;
pub mod handlers {
pub mod dap;
pub mod lsp;
}
pub mod base64;
pub mod info;
pub mod input;
pub mod keyboard;

@ -14,19 +14,14 @@ use toml::{map::Map, Value};
use crate::graphics::UnderlineStyle;
pub use crate::graphics::{Color, Modifier, Style};
pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| {
// let raw_theme: Value = toml::from_slice(include_bytes!("../../theme.toml"))
// .expect("Failed to parse default theme");
// Theme::from(raw_theme)
toml::from_slice(include_bytes!("../../theme.toml")).expect("Failed to parse default theme")
pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| Theme {
name: "default".into(),
..toml::from_slice(include_bytes!("../../theme.toml")).expect("Failed to parse default theme")
});
pub static BASE16_DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| {
// let raw_theme: Value = toml::from_slice(include_bytes!("../../base16_theme.toml"))
// .expect("Failed to parse base 16 default theme");
// Theme::from(raw_theme)
toml::from_slice(include_bytes!("../../base16_theme.toml"))
pub static BASE16_DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| Theme {
name: "base16_theme".into(),
..toml::from_slice(include_bytes!("../../base16_theme.toml"))
.expect("Failed to parse base 16 default theme")
});
@ -53,10 +48,12 @@ impl Loader {
return Ok(self.base16_default());
}
let value = self.load_theme(name, name, false)?;
let theme = Theme::deserialize(value)?;
let theme = self.load_theme(name, name, false).map(Theme::from)?;
Ok(theme)
Ok(Theme {
name: name.into(),
..theme
})
}
// load the theme and its parent recursively and merge them
@ -183,8 +180,10 @@ impl Loader {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct Theme {
name: String,
// UI styles are stored in a HashMap
styles: HashMap<String, Style>,
// tree-sitter highlight styles are stored in a Vec to optimize lookups
@ -193,25 +192,26 @@ pub struct Theme {
rainbow_length: usize,
}
// impl From<Value> for Theme {
// fn from(value: Value) -> Self {
// let values: Result<HashMap<String, Value>> =
// toml::from_str(&value.to_string()).context("Failed to load theme");
// let (styles, scopes, highlights) = build_theme_values(values);
// let rainbow_length = styles
// .iter()
// .filter(|s| s.0.starts_with("rainbow."))
// .count();
// Self {
// styles,
// scopes,
// highlights,
// rainbow_length,
// }
// }
// }
impl From<Value> for Theme {
fn from(value: Value) -> Self {
let values: Result<HashMap<String, Value>> =
toml::from_str(&value.to_string()).context("Failed to load theme");
let (styles, scopes, highlights) = build_theme_values(values);
let rainbow_length = styles
.iter()
.filter(|s| s.0.starts_with("rainbow."))
.count();
Self {
styles,
scopes,
highlights,
rainbow_length,
..Default::default()
}
}
}
impl<'de> Deserialize<'de> for Theme {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
@ -277,16 +277,59 @@ impl<'de> Deserialize<'de> for Theme {
scopes,
highlights,
rainbow_length,
..Default::default()
})
}
}
fn build_theme_values(
values: Result<HashMap<String, Value>>,
) -> (HashMap<String, Style>, Vec<String>, Vec<Style>) {
let mut styles = HashMap::new();
let mut scopes = Vec::new();
let mut highlights = Vec::new();
if let Ok(mut colors) = values {
// TODO: alert user of parsing failures in editor
let palette = colors
.remove("palette")
.map(|value| {
ThemePalette::try_from(value).unwrap_or_else(|err| {
warn!("{}", err);
ThemePalette::default()
})
})
.unwrap_or_default();
// remove inherits from value to prevent errors
let _ = colors.remove("inherits");
styles.reserve(colors.len());
scopes.reserve(colors.len());
highlights.reserve(colors.len());
for (name, style_value) in colors {
let mut style = Style::default();
if let Err(err) = palette.parse_style(&mut style, style_value) {
warn!("{}", err);
}
// these are used both as UI and as highlights
styles.insert(name.clone(), style);
scopes.push(name);
highlights.push(style);
}
}
(styles, scopes, highlights)
}
impl Theme {
#[inline]
pub fn highlight(&self, index: usize) -> Style {
self.highlights[index]
}
pub fn name(&self) -> &str {
&self.name
}
pub fn get(&self, scope: &str) -> Style {
self.try_get(scope).unwrap_or_default()
}
@ -324,7 +367,11 @@ impl Theme {
}
pub fn rainbow_length(&self) -> usize {
self.rainbow_length
if self.rainbow_length == 0 {
1
} else {
self.rainbow_length
}
}
pub fn default_rainbow() -> Vec<Style> {

@ -522,7 +522,7 @@ name = "bash"
scope = "source.bash"
injection-regex = "(shell|bash|zsh|sh)"
file-types = ["sh", "bash", "zsh", ".bash_login", ".bash_logout", ".bash_profile", ".bashrc", ".profile", ".zshenv", ".zlogin", ".zlogout", ".zprofile", ".zshrc", "APKBUILD", "PKGBUILD", "eclass", "ebuild", "bazelrc"]
shebangs = ["sh", "bash", "dash"]
shebangs = ["sh", "bash", "dash", "zsh"]
roots = []
comment-token = "#"
language-server = { command = "bash-language-server", args = ["start"] }
@ -570,7 +570,7 @@ indent = { tab-width = 4, unit = "\t" }
[[grammar]]
name = "latex"
source = { git = "https://github.com/latex-lsp/tree-sitter-latex", rev = "b3b2cf27f33e71438ebe46934900b1153901c6f2" }
source = { git = "https://github.com/latex-lsp/tree-sitter-latex", rev = "8c75e93cd08ccb7ce1ccab22c1fbd6360e3bcea6" }
[[language]]
name = "lean"
@ -593,7 +593,7 @@ injection-regex = "julia"
file-types = ["jl"]
roots = ["Manifest.toml", "Project.toml"]
comment-token = "#"
language-server = { command = "julia", args = [
language-server = { command = "julia", timeout = 60, args = [
"--startup-file=no",
"--history-file=no",
"--quiet",
@ -604,7 +604,7 @@ indent = { tab-width = 4, unit = " " }
[[grammar]]
name = "julia"
source = { git = "https://github.com/tree-sitter/tree-sitter-julia", rev = "fc60b7cce87da7a1b7f8cb0f9371c3dc8b684500" }
source = { git = "https://github.com/tree-sitter/tree-sitter-julia", rev = "8fb38abff74652c4faddbf04d2d5bbbc6b4bae25" }
[[language]]
name = "java"
@ -767,6 +767,29 @@ language-server = { command = "zls" }
indent = { tab-width = 4, unit = " " }
formatter = { command = "zig" , args = ["fmt", "--stdin"] }
[language.debugger]
name = "lldb-vscode"
transport = "stdio"
command = "lldb-vscode"
[[language.debugger.templates]]
name = "binary"
request = "launch"
completion = [ { name = "binary", completion = "filename" } ]
args = { console = "internalConsole", program = "{0}" }
[[language.debugger.templates]]
name = "attach"
request = "attach"
completion = [ "pid" ]
args = { console = "internalConsole", pid = "{0}" }
[[language.debugger.templates]]
name = "gdbserver attach"
request = "attach"
completion = [ { name = "lldb connect url", default = "connect://localhost:3333" }, { name = "file", completion = "filename" }, "pid" ]
args = { console = "internalConsole", attachCommands = [ "platform select remote-gdb-server", "platform connect {0}", "file {1}", "attach {2}" ] }
[[grammar]]
name = "zig"
source = { git = "https://github.com/maxxnino/tree-sitter-zig", rev = "8d3224c3bd0890fe08358886ebf54fca2ed448a6" }
@ -943,7 +966,7 @@ indent = { tab-width = 2, unit = " " }
[[grammar]]
name = "markdown"
source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "d5740f0fe4b8e4603f2229df107c5c9ef5eec389", subpath = "tree-sitter-markdown" }
source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "a7de4be29783a6e25f3240c90afea52f2417faa3", subpath = "tree-sitter-markdown" }
[[language]]
name = "markdown.inline"
@ -955,7 +978,7 @@ grammar = "markdown_inline"
[[grammar]]
name = "markdown_inline"
source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "d5740f0fe4b8e4603f2229df107c5c9ef5eec389", subpath = "tree-sitter-markdown-inline" }
source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "a7de4be29783a6e25f3240c90afea52f2417faa3", subpath = "tree-sitter-markdown-inline" }
[[language]]
name = "dart"
@ -982,7 +1005,7 @@ language-server = { command = "metals" }
[[grammar]]
name = "scala"
source = { git = "https://github.com/tree-sitter/tree-sitter-scala", rev = "0a3dd53a7fc4b352a538397d054380aaa28be54c" }
source = { git = "https://github.com/tree-sitter/tree-sitter-scala", rev = "140c96cf398693189d4e50f76d19ddfcd8a018f8" }
[[language]]
name = "dockerfile"
@ -1348,7 +1371,7 @@ injection-regex = "sql"
[[grammar]]
name = "sql"
source = { git = "https://github.com/DerekStride/tree-sitter-sql", rev = "0caa7fa2ee00e0b770493a79d4efacc1fc376cc5" }
source = { git = "https://github.com/DerekStride/tree-sitter-sql", rev = "2743c7b5e710e6854d4e8c14c302548b436e2a1f" }
[[language]]
name = "gdscript"
@ -1391,7 +1414,7 @@ indent = { tab-width = 2, unit = " " }
[[grammar]]
name = "nu"
source = { git = "https://github.com/LhKipp/tree-sitter-nu", rev = "db4e990b78824c8abef3618e0f93b7fe1e8f4c0d" }
source = { git = "https://github.com/LhKipp/tree-sitter-nu", rev = "eb95bdac3abd73ef47e53f19c63e74a31405ebd2" }
[[language]]
name = "vala"
@ -1495,7 +1518,7 @@ roots = []
[[grammar]]
name = "sshclientconfig"
source = { git = "https://github.com/metio/tree-sitter-ssh-client-config", rev = "769d7a01a2e5493b4bb5a51096c6bf4be130b024" }
source = { git = "https://github.com/metio/tree-sitter-ssh-client-config", rev = "e45c6d5c71657344d4ecaf87dafae7736f776c57" }
[[language]]
name = "scheme"
@ -1849,3 +1872,95 @@ formatter = { command = "dfmt" }
[[grammar]]
name = "d"
source = { git = "https://github.com/gdamore/tree-sitter-d", rev="601c4a1e8310fb2f3c43fa8a923d0d27497f3c04" }
[[language]]
name = "vhs"
scope = "source.vhs"
file-types = ["tape"]
roots = []
comment-token = "#"
indent = { tab-width = 2, unit = " " }
grammar = "vhs"
[[grammar]]
name = "vhs"
source = { git = "https://github.com/charmbracelet/tree-sitter-vhs", rev = "c6d81f34c011c29ee86dd73b45a8ecc9f2e2bdaf" }
[[language]]
name = "kdl"
scope = "source.kdl"
file-types = ["kdl"]
roots = []
comment-token = "//"
injection-regex = "kdl"
[[grammar]]
name = "kdl"
source = { git = "https://github.com/Unoqwy/tree-sitter-kdl", rev = "e1cd292c6d15df6610484e1d4b5c987ecad52373" }
[[language]]
name = "xml"
scope = "source.xml"
injection-regex = "xml"
file-types = ["xml"]
indent = { tab-width = 2, unit = " " }
roots = []
[language.auto-pairs]
'(' = ')'
'{' = '}'
'[' = ']'
'"' = '"'
"'" = "'"
"<" = ">"
[[grammar]]
name = "xml"
source = { git = "https://github.com/RenjiSann/tree-sitter-xml", rev = "422528a43630db6dcc1e222d1c5ee3babd559473" }
[[language]]
name = "wit"
scope = "source.wit"
injection-regex = "wit"
file-types = ["wit"]
roots = []
comment-token = "//"
indent = { tab-width = 2, unit = " " }
[language.auto-pairs]
'(' = ')'
'{' = '}'
'[' = ']'
'"' = '"'
"'" = "'"
"<" = ">"
[[grammar]]
name = "wit"
source = { git = "https://github.com/hh9527/tree-sitter-wit", rev = "c917790ab9aec50c5fd664cbfad8dd45110cfff3" }
[[language]]
name = "env"
scope = "source.env"
file-types = [".env", ".env.local", ".env.development", ".env.production"]
injection-regex = "env"
comment-token = "#"
indent = { tab-width = 4, unit = "\t" }
roots = []
[[grammar]]
name = "env"
source = { git = "https://github.com/seshotake/tree-sitter-env", rev = "e6c6bb1e7b51d481cba463fe949f083cf22d81f7" }
[[language]]
name = "ini"
scope = "source.ini"
file-types = ["ini"]
injection-regex = "ini"
comment-token = "#"
indent = { tab-width = 4, unit = "\t" }
roots = []
[[grammar]]
name = "ini"
source = { git = "https://github.com/justinmk/tree-sitter-ini", rev = "4d247fb876b4ae6b347687de4a179511bf67fcbc" }

@ -4,6 +4,6 @@
(class_declaration (aggregate_body) @class.inside) @class.around
(interface_declaration (aggregate_body) @class.inside) @class.around
(struct_declaration (aggregate_body) @class.inside) @class.around
(unittest_declaration (block_statement) @test.insid) @test.around
(unittest_declaration (block_statement) @test.inside) @test.around
(parameter) @parameter.inside
(template_parameter) @parameter.inside
(template_parameter) @parameter.inside

@ -0,0 +1,19 @@
(env_variable (quoted_string)) @string
(env_variable (unquoted_string)) @string
(env_key) @keyword
((variable) @keyword
(#match? @keyword "^([A-Z][A-Z_0-9]*)$"))
[
"{"
"}"
] @punctuation.bracket
[
"$"
"="
] @operator
(comment) @comment

@ -0,0 +1,6 @@
(section_name) @namespace
(setting_name) @keyword
(setting_value) @string
(comment) @comment

@ -7,13 +7,22 @@
(block_comment)
] @comment
(((identifier) @constant.builtin) (match? @constant.builtin "^(nothing|missing|Inf|NaN)$"))
(((identifier) @constant.builtin.boolean) (#eq? @constant.builtin.boolean "true"))
(((identifier) @constant.builtin.boolean) (#eq? @constant.builtin.boolean "false"))
(
((identifier) @constant.builtin)
(#match? @constant.builtin "^(nothing|missing|undef)$"))
[
(true)
(false)
] @constant.builtin.boolean
(integer_literal) @constant.numeric.integer
(float_literal) @constant.numeric.float
(
((identifier) @constant.numeric.float)
(#match? @constant.numeric.float "^((Inf|NaN)(16|32|64)?)$"))
(character_literal) @constant.character
(escape_sequence) @constant.character.escape
@ -66,7 +75,7 @@
(type_parameter_list
(identifier) @type)
(constrained_parameter
(constrained_type_parameter
(identifier) @type)
(subtype_clause
@ -81,13 +90,32 @@
(type_argument_list
(identifier) @type)
(where_clause
(identifier) @type)
; -------------------
; Function definition
; -------------------
(
(function_definition
name: (identifier) @function)
name: [
(identifier) @function
(scoped_identifier
(identifier) @namespace
(identifier) @function)
])
; prevent constructors (PascalCase) to be highlighted as functions
(#match? @function "^[^A-Z]"))
(
(short_function_definition
name: [
(identifier) @function
(scoped_identifier
(identifier) @namespace
(identifier) @function)
])
; prevent constructors (PascalCase) to be highlighted as functions
(#match? @function "^[^A-Z]"))
@ -101,7 +129,7 @@
(optional_parameter
. (identifier) @variable.parameter)
(spread_parameter
(slurp_parameter
(identifier) @variable.parameter)
(function_expression
@ -185,6 +213,7 @@
[
"abstract"
"baremodule"
"begin"
"const"
"do"
@ -198,6 +227,7 @@
"return"
"struct"
"type"
"where"
] @keyword
; TODO: fix this

@ -21,7 +21,7 @@
(optional_parameter .
(identifier) @local.definition)
(spread_parameter
(slurp_parameter
(identifier) @local.definition)
(function_expression
@ -33,6 +33,7 @@
[
(function_definition)
(short_function_definition)
(macro_definition)
] @local.scope

@ -0,0 +1,22 @@
(comment) @comment
(single_line_comment) @comment
(node
name: (identifier) @function)
(prop (identifier) @attribute)
(type) @type
(bare_identifier) @variable.other.member
(keyword) @keyword
(string) @string
(number) @constant.numeric
(boolean) @constant.builtin.boolean
"." @punctuation.delimiter
"=" @operator
"{" @punctuation.bracket
"}" @punctuation.bracket

@ -29,6 +29,12 @@
(#eq? @punctuation.delimiter "&"))
["[" "]" "{" "}"] @punctuation.bracket ; "(" ")" has no syntactical meaning in LaTeX
(math_delimiter
left_command: _ @punctuation.delimiter
left_delimiter: _ @punctuation.delimiter
right_command: _ @punctuation.delimiter
right_delimiter: _ @punctuation.delimiter
)
;; General environments
(begin

@ -1,2 +1,2 @@
((comment) @injection.content
((line_comment) @injection.content
(#set! injection.language "comment"))

@ -1,2 +1,2 @@
((html_tag) @injection.content (#set! injection.language "html"))
((html_tag) @injection.content (#set! injection.language "html") (#set! injection.include-unnamed-children))

@ -2,7 +2,7 @@
(dotted_name
(identifier)* @namespace)
(aliased_import
alias: (identifier) @namespace)
@ -67,7 +67,7 @@
(parameters
(dictionary_splat_pattern ; **kwargs
(identifier) @variable.parameter))
(lambda_parameters
(identifier) @variable.parameter)
@ -97,7 +97,7 @@
(#match? @constant "^[A-Z_]{2,}$"))
((identifier) @type
(#match? @type "^[A-Z]"))
(#match? @type "^[A-Z]"))
(attribute attribute: (identifier) @variable.other.member)
(identifier) @variable
@ -168,6 +168,8 @@
"if"
"elif"
"else"
"match"
"case"
] @keyword.control.conditional
[

@ -9,6 +9,8 @@
(while_statement)
(with_statement)
(try_statement)
(match_statement)
(case_clause)
(import_from_statement)
(parenthesized_expression)
@ -33,6 +35,8 @@
(while_statement)
(with_statement)
(try_statement)
(match_statement)
(case_clause)
(function_definition)
(class_definition)

@ -111,23 +111,31 @@
; -------
(for_expression
"for" @keyword.control)
"for" @keyword.control.repeat)
((identifier) @keyword.control
(#match? @keyword.control "^yield$"))
"in" @keyword.control
[
"match"
"if"
"else"
] @keyword.control.conditional
[
"while"
"loop"
"in"
] @keyword.control.repeat
[
"break"
"continue"
"match"
"if"
"else"
"return"
"await"
] @keyword.control
] @keyword.control.return
"use" @keyword.control.import
(mod_item "mod" @keyword.control.import !body)
@ -143,24 +151,28 @@
"mod"
"extern"
"struct"
"enum"
"impl"
"where"
"trait"
"for"
"type"
"union"
"unsafe"
"default"
"macro_rules!"
"let"
"async"
] @keyword
[
"struct"
"enum"
"union"
"type"
] @keyword.storage.type
"let" @keyword.storage
"fn" @keyword.function
(mutable_specifier) @keyword.storage.modifier.mut

@ -1,3 +1,34 @@
(keyword_gist) @function.builtin
(keyword_btree) @function.builtin
(keyword_btree) @function.builtin
(keyword_hash) @function.builtin
(keyword_spgist) @function.builtin
(keyword_gin) @function.builtin
(keyword_brin) @function.builtin
(keyword_float) @function.builtin
(invocation
name: (identifier) @function.builtin
parameter: [(field)]? @variable.other.member)
(count
name: (identifier) @function.builtin
parameter: [(field)]? @variable.other.member)
(table_reference
name: (identifier) @namespace)
(relation
table_alias: (identifier) @variable.parameter)
(field
name: (identifier) @variable.other.member)
(field
table_alias: (identifier) @variable.parameter
name: (identifier) @variable.other.member)
(comment) @comment
[
@ -5,6 +36,12 @@
")"
] @punctuation.bracket
[
";"
","
"."
] @punctuation.delimiter
[
"*"
"+"
@ -29,11 +66,8 @@
(literal) @string
(set_schema schema: (identifier) @namespace)
(table_reference schema: (identifier) @namespace)
(table_expression schema: (identifier) @namespace)
(all_fields schema: (identifier) @namespace)
(field schema: (identifier) @namespace)
((literal) @constant.numeric
(#match? @constant.numeric "^(-?\d*\.?\d*)$"))
[
(keyword_select)
@ -54,8 +88,10 @@
(keyword_lateral)
(keyword_on)
(keyword_not)
(keyword_order_by)
(keyword_group_by)
(keyword_order)
(keyword_group)
(keyword_partition)
(keyword_by)
(keyword_having)
(keyword_desc)
(keyword_asc)
@ -89,6 +125,8 @@
(keyword_auto_increment)
(keyword_default)
(keyword_cascade)
(keyword_between)
(keyword_window)
(keyword_with)
(keyword_no)
(keyword_data)

@ -1,17 +1,17 @@
(host) @keyword
(host_value) @identifier
(host) @namespace
(host_value) @string
(match) @keyword
(match_value) @identifier
(match) @namespace
(match_value) @string
(add_keys_to_agent) @keyword
(add_keys_to_agent_value) @boolean
(add_keys_to_agent_value) @constant.builtin.boolean
(address_family) @keyword
(address_family_value) @type
(address_family_value) @constant.builtin
(batch_mode) @keyword
(batch_mode_value) @boolean
(batch_mode_value) @constant.builtin.boolean
(bind_address) @keyword
(bind_address_value) @string
@ -20,165 +20,165 @@
(bind_interface_value) @string
(canonical_domains) @keyword
(canonical_domains_value) @identifier
(canonical_domains_value) @string
(canonicalize_fallback_local) @keyword
(canonicalize_fallback_local_value) @boolean
(canonicalize_fallback_local_value) @constant.builtin.boolean
(canonicalize_hostname) @keyword
(canonicalize_hostname_value) @boolean
(canonicalize_hostname_value) @constant.builtin
(canonicalize_max_dots) @keyword
(canonicalize_max_dots_value) @number
(canonicalize_max_dots_value) @constant.numeric.integer
(canonicalize_permitted_cnames) @keyword
(canonicalize_permitted_cnames_value) @identifier
(canonicalize_permitted_cnames_value) @string
(ca_signature_algorithms) @keyword
(ca_signature_algorithms_value) @identifier
(ca_signature_algorithms_value) @string
(certificate_file) @keyword
(certificate_file_value) @file
(certificate_file_value) @string.special.path
(challenge_response_authentication) @keyword
(challenge_response_authentication_value) @boolean
(challenge_response_authentication_value) @constant.builtin.boolean
(check_host_ip) @keyword
(check_host_ip_value) @boolean
(check_host_ip_value) @constant.builtin.boolean
(cipher) @keyword
(cipher_value) @identifier
(cipher_value) @string
(ciphers) @keyword
(ciphers_value) @identifier
(ciphers_value) @string
(clear_all_forwardings) @keyword
(clear_all_forwardings_value) @boolean
(clear_all_forwardings_value) @constant.builtin.boolean
(comment) @comment
(compression) @keyword
(compression_value) @boolean
(compression_value) @constant.builtin.boolean
(connect_timeout) @keyword
(connect_timeout_value) @number
(connect_timeout_value) @constant.numeric.integer
(connection_attempts) @keyword
(connection_attempts_value) @number
(connection_attempts_value) @constant.numeric.integer
(control_master) @keyword
(control_master_value) @type
(control_master_value) @constant.builtin
(control_path) @keyword
(control_path_value) @file
(control_path_value) @string.special.path
(control_persist) @keyword
(control_persist_value) @type
(control_persist_value) @constant.builtin
(dynamic_forward) @keyword
(dynamic_forward_value) @string
(enable_ssh_keysign) @keyword
(enable_ssh_keysign_value) @boolean
(enable_ssh_keysign_value) @constant.builtin.boolean
(escape_char) @keyword
(escape_char_value) @string
(escape_char_value) @constant.character.escape
(exit_on_forward_failure) @keyword
(exit_on_forward_failure_value) @boolean
(exit_on_forward_failure_value) @constant.builtin.boolean
(fingerprint_hash) @keyword
(fingerprint_hash_value) @identifier
(fingerprint_hash_value) @constant.builtin
(fork_after_authentication) @keyword
(fork_after_authentication_value) @boolean
(fork_after_authentication_value) @constant.builtin.boolean
(forward_agent) @keyword
(forward_agent_value) @boolean
(forward_agent_value) @string
(forward_x11) @keyword
(forward_x11_value) @boolean
(forward_x11_value) @constant.builtin.boolean
(forward_x11_timeout) @keyword
(forward_x11_timeout_value) @time
(forward_x11_timeout_value) @constant.numeric.integer
(forward_x11_trusted) @keyword
(forward_x11_trusted_value) @boolean
(forward_x11_trusted_value) @constant.builtin.boolean
(gateway_ports) @keyword
(gateway_ports_value) @boolean
(gateway_ports_value) @constant.builtin.boolean
(global_known_hosts_file) @keyword
(global_known_hosts_file_value) @file
(global_known_hosts_file_value) @string.special.path
(gssapi_authentication) @keyword
(gssapi_authentication_value) @boolean
(gssapi_authentication_value) @constant.builtin.boolean
(gssapi_client_identity) @keyword
(gssapi_client_identity_value) @string
(gssapi_delegate_credentials) @keyword
(gssapi_delegate_credentials_value) @boolean
(gssapi_delegate_credentials_value) @constant.builtin.boolean
(gssapi_kex_algorithms) @keyword
(gssapi_kex_algorithms_value) @identifier
(gssapi_kex_algorithms_value) @string
(gssapi_key_exchange) @keyword
(gssapi_key_exchange_value) @boolean
(gssapi_key_exchange_value) @constant.builtin.boolean
(gssapi_renewal_forces_rekey) @keyword
(gssapi_renewal_forces_rekey_value) @boolean
(gssapi_renewal_forces_rekey_value) @constant.builtin.boolean
(gssapi_server_identity) @keyword
(gssapi_server_identity_value) @string
(gssapi_trust_dns) @keyword
(gssapi_trust_dns_value) @boolean
(gssapi_trust_dns_value) @constant.builtin.boolean
(hash_known_hosts) @keyword
(hash_known_hosts_value) @boolean
(hash_known_hosts_value) @constant.builtin.boolean
(host_key_algorithms) @keyword
(host_key_algorithms_value) @identifier
(host_key_algorithms_value) @string
(host_key_alias) @keyword
(host_key_alias_value) @string
(hostbased_accepted_algorithms) @keyword
(hostbased_accepted_algorithms_value) @identifier
(hostbased_accepted_algorithms_value) @string
(hostbased_authentication) @keyword
(hostbased_authentication_value) @boolean
(hostbased_authentication_value) @constant.builtin.boolean
(hostname) @keyword
(hostname_value) @string
(identities_only) @keyword
(identities_only_value) @boolean
(identities_only_value) @constant.builtin.boolean
(identity_agent) @keyword
(identity_agent_value) @string
(identity_file) @keyword
(identity_file_value) @file
(identity_file_value) @string.special.path
(ignore_unknown) @keyword
(ignore_unknown_value) @string
(include) @keyword
(include_value) @file
(include) @function.macro
(include_value) @string.special.path
(ip_qos) @keyword
(ip_qos_value) @type
(ip_qos_value) @constant.builtin
(kbd_interactive_authentication) @keyword
(kbd_interactive_authentication_value) @boolean
(kbd_interactive_authentication_value) @constant.builtin.boolean
(kbd_interactive_devices) @keyword
(kbd_interactive_devices_value) @type
(kbd_interactive_devices_value) @string
(kex_algorithms) @keyword
(kex_algorithms_value) @identifier
(kex_algorithms_value) @string
(known_hosts_command) @keyword
(known_hosts_command_value) @string
@ -190,25 +190,25 @@
(local_forward_value) @string
(log_level) @keyword
(log_level_value) @type
(log_level_value) @constant.builtin
(log_verbose) @keyword
(log_verbose_value) @string
(macs) @keyword
(macs_value) @identifier
(macs_value) @string
(no_host_authentication_for_localhost) @keyword
(no_host_authentication_for_localhost_value) @boolean
(no_host_authentication_for_localhost_value) @constant.builtin.boolean
(number_of_password_prompts) @keyword
(number_of_password_prompts_value) @number
(number_of_password_prompts_value) @constant.numeric.integer
(password_authentication) @keyword
(password_authentication_value) @boolean
(password_authentication_value) @constant.builtin.boolean
(permit_local_command) @keyword
(permit_local_command_value) @boolean
(permit_local_command_value) @constant.builtin.boolean
(permit_remote_open) @keyword
(permit_remote_open_value) @string
@ -217,13 +217,13 @@
(pkcs11_provider_value) @string
(port) @keyword
(port_value) @number
(port_value) @constant.numeric.integer
(preferred_authentications) @keyword
(preferred_authentications_value) @type
(preferred_authentications_value) @string
(protocol) @keyword
(protocol_value) @number
(protocol_value) @constant.numeric.integer
(proxy_command) @keyword
(proxy_command_value) @string
@ -232,16 +232,16 @@
(proxy_jump_value) @string
(proxy_use_fdpass) @keyword
(proxy_use_fdpass_value) @boolean
(proxy_use_fdpass_value) @constant.builtin.boolean
(pubkey_accepted_algorithms) @keyword
(pubkey_accepted_algorithms_value) @identifier
(pubkey_accepted_algorithms_value) @string
(pubkey_accepted_key_types) @keyword
(pubkey_accepted_key_types_value) @identifier
(pubkey_accepted_key_types_value) @string
(pubkey_authentication) @keyword
(pubkey_authentication_value) @boolean
(pubkey_authentication_value) @constant.builtin
(rekey_limit) @keyword
(rekey_limit_value) @string
@ -253,10 +253,10 @@
(remote_forward_value) @string
(request_tty) @keyword
(request_tty_value) @type
(request_tty_value) @constant.builtin
(revoked_host_keys) @keyword
(revoked_host_keys_value) @file
(revoked_host_keys_value) @string.special.path
(security_key_provider) @keyword
(security_key_provider_value) @string
@ -265,60 +265,60 @@
(send_env_value) @string
(server_alive_count_max) @keyword
(server_alive_count_max_value) @number
(server_alive_count_max_value) @constant.numeric.integer
(server_alive_interval) @keyword
(server_alive_interval_value) @number
(server_alive_interval_value) @constant.numeric.integer
(session_type) @keyword
(session_type_value) @type
(session_type_value) @constant.builtin
(set_env) @keyword
(set_env_value) @string
(stdin_null) @keyword
(stdin_null_value) @boolean
(stdin_null_value) @constant.builtin.boolean
(stream_local_bind_mask) @keyword
(stream_local_bind_mask_value) @string
(stream_local_bind_unlink) @keyword
(stream_local_bind_unlink_value) @boolean
(stream_local_bind_unlink_value) @constant.builtin.boolean
(strict_host_key_checking) @keyword
(strict_host_key_checking_value) @type
(strict_host_key_checking_value) @constant.builtin
(syslog_facility) @keyword
(syslog_facility_value) @type
(syslog_facility_value) @constant.builtin
(tcp_keep_alive) @keyword
(tcp_keep_alive_value) @boolean
(tcp_keep_alive_value) @constant.builtin.boolean
(keep_alive) @keyword
(keep_alive_value) @boolean
(keep_alive_value) @constant.builtin.boolean
(tunnel) @keyword
(tunnel_value) @type
(tunnel_value) @constant.builtin
(tunnel_device) @keyword
(tunnel_device_value) @string
(update_host_keys) @keyword
(update_host_keys_value) @type
(update_host_keys_value) @constant.builtin
(use_keychain) @keyword
(use_keychain_value) @boolean
(use_keychain_value) @constant.builtin.boolean
(user) @keyword
(user_value) @string
(user_known_hosts_file) @keyword
(user_known_hosts_file_value) @file
(user_known_hosts_file_value) @string.special.path
(verify_host_key_dns) @keyword
(verify_host_key_dns_value) @type
(verify_host_key_dns_value) @constant.builtin
(visual_host_key) @keyword
(visual_host_key_value) @boolean
(visual_host_key_value) @constant.builtin.boolean
(xauth_location) @keyword
(xauth_location_value) @file
(xauth_location_value) @string.special.path

@ -0,0 +1,36 @@
[
"Output"
"Backspace"
"Down"
"Enter"
"Escape"
"Left"
"Right"
"Space"
"Tab"
"Up"
"Set"
"Type"
"Sleep"
"Hide"
"Show" ] @keyword
[ "FontFamily"
"FontSize"
"Framerate"
"Height"
"LetterSpacing"
"TypingSpeed"
"LineHeight"
"Padding"
"Theme"
"Width" ] @type
[ "@" ] @operator
(control) @function.macro
(float) @constant.numeric.float
(integer) @constant.numeric.integer
(comment) @comment
(path) @string.special.path
[(string) (json)] @string
(time) @string.special.symbol

@ -1,4 +1,7 @@
["module" "func" "param" "result" "type" "memory" "elem" "data" "table" "global"] @keyword
[
"module" "func" "param" "result" "type" "memory" "elem" "data" "table" "global"
"if" "then" "else" "block" "loop" "end" "mut"
] @keyword
["import" "export"] @keyword.control.import

@ -0,0 +1,67 @@
(line_comment) @comment.line
(block_comment) @comment.block
(ty (ident) @type)
(item_type name: (ident) @type)
(item_record name: (ident) @type)
(item_variant name: (ident) @type)
(item_flags name: (ident) @type)
(item_enum name: (ident) @type)
(item_union name: (ident) @type)
(item_resource name: (ident) @type)
(item_use from: (ident) @namespace)
(use_item name: (ident) @type)
(item_func name: (ident) @function)
(method name: (ident) @function.method)
(fields (named_ty name: (ident) @variable.other.member))
(input (args (named_ty name: (ident) @variable.parameter)))
(output (args (named_ty name: (ident) @variable.other.member)))
(flags (ident) @constant)
(enum_items (ident) @constant)
(variant_item tag: (ident) @type.enum.variant)
[
(unit)
"u8" "u16" "u32" "u64"
"s8" "s16" "s32" "s64"
"float32" "float64"
"char" "bool" "string"
] @type.builtin
[
"list"
"option"
"result"
"tuple"
"future"
"stream"
] @function.macro
[ "," ":" ] @punctuation.delimiter
[ "(" ")" "{" "}" "<" ">" ] @punctuation.bracket
[ "=" "->" ] @operator
[
"record"
"flags"
"variant"
"enum"
"union"
"type"
"resource"
] @keyword.storage.type
"func" @keyword
[
"static"
] @keyword.storage.modifier
[
(star)
"use"
"as"
"from"
] @keyword.control.import

@ -0,0 +1,13 @@
[
(use_items)
(fields)
(variant_items)
(variant_payload)
(flags)
(enum_items)
(union_items)
(args)
(resource_items)
] @indent
[ "}" ")" ] @outdent

@ -1,3 +1,4 @@
<<<<<<< HEAD
(attribute_name) @attribute
(attribute_value) @string
(comment) @comment
@ -8,4 +9,49 @@
"encoding"
"standalone"
] @attribute
(xml_decl) @constant
(xml_decl) @constant
||||||| ce469abf
=======
(comment) @comment
[
"DOCTYPE"
"ELEMENT"
"ATTLIST"
] @keyword
[
"#REQUIRED"
"#IMPLIED"
"#FIXED"
"#PCDATA"
] @keyword.directive
[
"EMPTY"
"ANY"
"SYSTEM"
"PUBLIC"
] @constant
(doctype) @variable
(element_name) @variable
"xml" @tag
(tag_name) @tag
[
"encoding"
"version"
"standalone"
] @attribute
(attribute_name) @attribute
(system_literal) @string
(pubid_literal) @string
(attribute_value) @string
[
"<" ">" "</" "/>" "<?" "?>" "<!"
] @punctuation.bracket
>>>>>>> master

@ -0,0 +1 @@
(element) @indent

@ -0,0 +1,2 @@
((comment) @injection.content
(#set! injection.language "comment"))

@ -1,11 +1,11 @@
# Author: Kristoffer Flottorp <kr.fl@outlook.com>
# Based on a few screenshots of Jetbrains Fleet
# A take on the JetBrains Fleet theme sprinkled with some creative freedom
"type" = { fg = "orange" } # .builtin
"constructor" = { fg = "orange" }
"constant" = { fg = "green" }
"type" = { fg = "yellow" } # .builtin
"constructor" = { fg = "yellow" }
"constant" = { fg = "cyan" }
# "constant.builtin" = {} # .boolean
"constant.builtin.boolean" = { fg = "green" } # .boolean
"constant.builtin.boolean" = { fg = "cyan" } # .boolean
# "constant.character" = {} #.escape
"constant.numeric" = { fg = "yellow" } # .integer / .float
"string" = { fg = "pink" } # .regexp
@ -16,31 +16,31 @@
"variable" = { fg = "light" } # .builtin / .parameter
# "variable.other" = {} # .member
"variable.other.member" = { fg = "purple" }
"label" = { fg = "orange" }
"label" = { fg = "yellow" }
# "punctuation" = {} # .delimiter / .bracket
"keyword" = { fg = "green" } # .operator / .directive / .function
"keyword" = { fg = "cyan" } # .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" }
"function.macro" = { fg = "green" }
"function.special" = { fg = "green" }
"tag" = { fg = "green"}
"special" = { fg = "green" }
"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.1" = { fg = "yellow" }
"markup.heading.2" = { fg = "green" }
"markup.heading.3" = { fg = "pink" }
"markup.heading.4" = { fg = "purple" }
"markup.heading.5" = { fg = "green" }
"markup.heading.5" = { fg = "cyan" }
"markup.heading.6" = { fg = "blue" }
"markup.list" = { fg = "green" } # .unnumbered / .numbered
"markup.link" = { fg = "yellow" } # .url / .label / .text
"markup.list" = { fg = "cyan" } # .unnumbered / .numbered
"markup.link" = { fg = "green" } # .url / .label / .text
"markup.raw" = { fg = "pink" } # .inline / .block
# "diff" = {} # .plus / .minus
"diff.plus" = { fg = "green" }
"diff.minus" = { fg = "orange" }
"diff.plus" = { fg = "cyan" }
"diff.minus" = { fg = "yellow" }
"diff.delta" = { fg = "purple" } # .moved
# used in theming
@ -74,11 +74,7 @@
"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" }
"diagnostic". underline = { style = "curl" }
[palette]
darkest = "#0F0F0F"
@ -91,12 +87,13 @@ lightest = "#FFFFFF"
dark_gray = "#5B5B5B"
light_gray = "#757575"
purple = "#A096F9"
blue = "#52A7F6"
pink = "#E878DE"
green = "#78D0BD"
purple = "#AC9CF9"
blue = "#52A7F6" #"#94C1FA"
pink = "#D898D8"
green = "#AFCB85"
cyan = "#78D0BD"
orange = "#ECA775"
yellow = "#F9CA6A"
yellow = "#E5C995"
purple_accent = "#6363EE"
blue_accent = "#2197F3"

Loading…
Cancel
Save