From 71220da1392e373f1f7dc6370def9cd3c5c41ff9 Mon Sep 17 00:00:00 2001 From: Skyler Hawthorne Date: Sun, 19 Feb 2023 12:34:47 -0500 Subject: [PATCH 0001/1169] Update tree-sitter-git-rebase (#6030) --- runtime/queries/git-rebase/highlights.scm | 41 ++++++++++++++++++----- runtime/queries/git-rebase/injections.scm | 9 ++--- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/runtime/queries/git-rebase/highlights.scm b/runtime/queries/git-rebase/highlights.scm index 4f007037d..5a03dadf8 100644 --- a/runtime/queries/git-rebase/highlights.scm +++ b/runtime/queries/git-rebase/highlights.scm @@ -1,11 +1,36 @@ -(operation operator: ["p" "pick" "r" "reword" "e" "edit" "s" "squash" "m" "merge" "d" "drop" "b" "break" "x" "exec"] @keyword) -(operation operator: ["l" "label" "t" "reset"] @function) -(operation operator: ["f" "fixup"] @function.special) +; a rough translation: +; * constant.builtin - git hash +; * constant - a git label +; * keyword - command that acts on commits commits +; * function - command that acts only on labels +; * comment - discarded commentary on a command, has no effect on the rebase +; * string - text used in the rebase operation +; * operator - a 'switch' (used in fixup and merge), either -c or -C at time of writing + +(((command) @keyword + (label) @constant.builtin + (message)? @comment) + (#match? @keyword "^(p|pick|r|reword|e|edit|s|squash|d|drop)$")) + +(((command) @function + (label) @constant + (message)? @comment) + (#match? @function "^(l|label|t|reset|u|update-ref)$")) + +((command) @keyword + (#match? @keyword "^(x|exec|b|break)$")) + +(((command) @attribute + (label) @constant.builtin + (message)? @comment) + (#match? @attribute "^(f|fixup)$")) + +(((command) @keyword + (label) @constant.builtin + (label) @constant + (message) @string) + (#match? @keyword "^(m|merge)$")) (option) @operator -(label) @string.special.symbol -(commit) @constant -"#" @punctuation.delimiter -(comment) @comment -(ERROR) @error +(comment) @comment diff --git a/runtime/queries/git-rebase/injections.scm b/runtime/queries/git-rebase/injections.scm index 070129b63..90f1f9416 100644 --- a/runtime/queries/git-rebase/injections.scm +++ b/runtime/queries/git-rebase/injections.scm @@ -1,4 +1,5 @@ -((operation - operator: ["x" "exec"] - (command) @injection.content) - (#set! injection.language "bash")) +(((command) @attribute + (message)? @injection.content) + (#match? @attribute "^(x|exec)$") + (#set! injection.language "bash") +) From 5ff2cb24e2ec5c8caf8972699e7ae69a15b710d1 Mon Sep 17 00:00:00 2001 From: Jummit Date: Sun, 19 Feb 2023 19:32:42 +0100 Subject: [PATCH 0002/1169] Add support for the uxntal language (#6047) --- book/src/generated/lang-support.md | 1 + languages.toml | 13 +++++++++++++ runtime/queries/uxntal/highlights.scm | 15 +++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 runtime/queries/uxntal/highlights.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index c31ddd3d2..b9442693d 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -134,6 +134,7 @@ | twig | ✓ | | | | | typescript | ✓ | ✓ | ✓ | `typescript-language-server` | | ungrammar | ✓ | | | | +| uxntal | ✓ | | | | | v | ✓ | | | `v` | | vala | ✓ | | | `vala-language-server` | | verilog | ✓ | ✓ | | `svlangserver` | diff --git a/languages.toml b/languages.toml index b521cd128..6aac13970 100644 --- a/languages.toml +++ b/languages.toml @@ -2167,3 +2167,16 @@ comment-token = "#" [[grammar]] name = "hosts" source = { git = "https://github.com/ath3/tree-sitter-hosts", rev = "301b9379ce7dfc8bdbe2c2699a6887dcb73953f9" } + +[[language]] +name = "uxntal" +scope = "source.tal" +injection-regex = "tal" +file-types = ["tal"] +roots = [] +auto-format = false +comment-token = "(" + +[[grammar]] +name = "uxntal" +source = { git = "https://github.com/Jummit/tree-sitter-uxntal", rev = "9297e95ef74380b0ad84c4fd98f91e9f6e4319e6" } diff --git a/runtime/queries/uxntal/highlights.scm b/runtime/queries/uxntal/highlights.scm new file mode 100644 index 000000000..a6ff18528 --- /dev/null +++ b/runtime/queries/uxntal/highlights.scm @@ -0,0 +1,15 @@ +; highlights.scm + +(identifier) @keyword +(number) @constant.numeric +(comment) @comment +(raw_character) @constant.character +(literal_hex) @constant.numeric.integer +(macro_definition) @function +(label_definition) @label +(sub_label_definition) @label +(relative_pad) @constant +(label) @label +(sub_label) @label +(ERROR) @error +["[" "]" "{" "}"] @punctuation.bracket From 31b0c75832c56179e9fcb2301f1e92ac2fa116ff Mon Sep 17 00:00:00 2001 From: Mathieu Agopian Date: Sun, 19 Feb 2023 19:52:12 +0100 Subject: [PATCH 0003/1169] Expand contributing docs (#5967) --- docs/CONTRIBUTING.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 491cd4249..d8c15c72f 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -8,7 +8,10 @@ Some suggestions to get started: - Help with packaging on various distributions needed! - To use print debugging to the [Helix log file][log-file], you must: * Print using `log::info!`, `warn!`, or `error!`. (`log::info!("helix!")`) - * Pass the appropriate verbosity level option for the desired log level. (`hx -v ` for info, more `v`s for higher severity inclusive) + * Pass the appropriate verbosity level option for the desired log level. (`hx -v ` for info, more `v`s for higher verbosity) + * Want to display the logs in a separate file instead of using the `:log-open` command in your compiled Helix editor? Start your debug version with `cargo run -- --log foo.log` and in a new terminal use `tail -f foo.log` +- Instead of running a release version of Helix, while developing you may want to run in debug mode with `cargo run` which is way faster to compile +- Looking for even faster compile times? Give a try to [mold](https://github.com/rui314/mold) - If your preferred language is missing, integrating a tree-sitter grammar for it and defining syntax highlight queries for it is straight forward and doesn't require much knowledge of the internals. From e3765ac6d20195d2434c8274850247fe0a044da0 Mon Sep 17 00:00:00 2001 From: Filip Dutescu Date: Mon, 20 Feb 2023 06:00:00 +0200 Subject: [PATCH 0004/1169] feat(dap): send Disconnect if Terminated event received (#5532) Send a `Disconnect` DAP request if the `Terminated` event is received. According to the specification, if the debugging session was started by as `launch`, the debuggee should be terminated alongside the session. If instead the session was started as `attach`, it should not be disposed of. This default behaviour can be overriden if the `supportTerminateDebuggee` capability is supported by the adapter, through the `Disconnect` request `terminateDebuggee` argument, as described in [the specification][discon-spec]. This also implies saving the starting command for a debug sessions, in order to decide which behaviour should be used, as well as validating the capabilities of the adapter, in order to decide what the disconnect should do. An additional change made is handling of the `Exited` event, showing a message if the exit code is different than `0`, for the user to be aware off the termination failure. [discon-spec]: https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect Closes: #4674 Signed-off-by: Filip Dutescu --- helix-dap/src/client.rs | 28 ++++++++++++--- helix-dap/src/lib.rs | 2 +- helix-dap/src/types.rs | 13 ++++++- helix-term/src/commands/dap.rs | 2 +- helix-view/src/handlers/dap.rs | 63 +++++++++++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index 10d931809..f6d8a0696 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -1,4 +1,5 @@ use crate::{ + requests::DisconnectArguments, transport::{Payload, Request, Response, Transport}, types::*, Error, Result, ThreadId, @@ -31,6 +32,7 @@ pub struct Client { _process: Option, server_tx: UnboundedSender, request_counter: AtomicU64, + connection_type: Option, pub caps: Option, // thread_id -> frames pub stack_frames: HashMap>, @@ -41,6 +43,12 @@ pub struct Client { pub quirks: DebuggerQuirks, } +#[derive(Clone, Copy, Debug)] +pub enum ConnectionType { + Launch, + Attach, +} + impl Client { // Spawn a process and communicate with it by either TCP or stdio pub async fn process( @@ -78,7 +86,7 @@ impl Client { server_tx, request_counter: AtomicU64::new(0), caps: None, - // + connection_type: None, stack_frames: HashMap::new(), thread_states: HashMap::new(), thread_id: None, @@ -207,6 +215,10 @@ impl Client { self.id } + pub fn connection_type(&self) -> Option { + self.connection_type + } + fn next_request_id(&self) -> u64 { self.request_counter.fetch_add(1, Ordering::Relaxed) } @@ -334,15 +346,21 @@ impl Client { Ok(()) } - pub fn disconnect(&self) -> impl Future> { - self.call::(()) + pub fn disconnect( + &mut self, + args: Option, + ) -> impl Future> { + self.connection_type = None; + self.call::(args) } - pub fn launch(&self, args: serde_json::Value) -> impl Future> { + pub fn launch(&mut self, args: serde_json::Value) -> impl Future> { + self.connection_type = Some(ConnectionType::Launch); self.call::(args) } - pub fn attach(&self, args: serde_json::Value) -> impl Future> { + pub fn attach(&mut self, args: serde_json::Value) -> impl Future> { + self.connection_type = Some(ConnectionType::Attach); self.call::(args) } diff --git a/helix-dap/src/lib.rs b/helix-dap/src/lib.rs index 24d7472b7..21162cb86 100644 --- a/helix-dap/src/lib.rs +++ b/helix-dap/src/lib.rs @@ -2,7 +2,7 @@ mod client; mod transport; mod types; -pub use client::Client; +pub use client::{Client, ConnectionType}; pub use events::Event; pub use transport::{Payload, Response, Transport}; pub use types::*; diff --git a/helix-dap/src/types.rs b/helix-dap/src/types.rs index 0a9ebe5e9..c598790b2 100644 --- a/helix-dap/src/types.rs +++ b/helix-dap/src/types.rs @@ -391,11 +391,22 @@ pub mod requests { const COMMAND: &'static str = "attach"; } + #[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + pub struct DisconnectArguments { + #[serde(skip_serializing_if = "Option::is_none")] + pub restart: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub terminate_debuggee: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub suspend_debuggee: Option, + } + #[derive(Debug)] pub enum Disconnect {} impl Request for Disconnect { - type Arguments = (); + type Arguments = Option; type Result = (); const COMMAND: &'static str = "disconnect"; } diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index b3166e395..b30dc8c0a 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -539,7 +539,7 @@ pub fn dap_variables(cx: &mut Context) { pub fn dap_terminate(cx: &mut Context) { let debugger = debugger!(cx.editor); - let request = debugger.disconnect(); + let request = debugger.disconnect(None); dap_callback(cx.jobs, request, |editor, _compositor, _response: ()| { // editor.set_error(format!("Failed to disconnect: {}", e)); editor.debugger = None; diff --git a/helix-view/src/handlers/dap.rs b/helix-view/src/handlers/dap.rs index 2e86871b5..107c29be5 100644 --- a/helix-view/src/handlers/dap.rs +++ b/helix-view/src/handlers/dap.rs @@ -1,7 +1,8 @@ use crate::editor::{Action, Breakpoint}; use crate::{align_view, Align, Editor}; +use dap::requests::DisconnectArguments; use helix_core::Selection; -use helix_dap::{self as dap, Client, Payload, Request, ThreadId}; +use helix_dap::{self as dap, Client, ConnectionType, Payload, Request, ThreadId}; use helix_lsp::block_on; use log::warn; use std::fmt::Write; @@ -274,6 +275,66 @@ impl Editor { self.set_status("Debugged application started"); }; // TODO: do we need to handle error? } + Event::Terminated(terminated) => { + let restart_args = if let Some(terminated) = terminated { + terminated.restart + } else { + None + }; + + let disconnect_args = Some(DisconnectArguments { + restart: Some(restart_args.is_some()), + terminate_debuggee: None, + suspend_debuggee: None, + }); + + if let Err(err) = debugger.disconnect(disconnect_args).await { + self.set_error(format!( + "Cannot disconnect debugger upon terminated event receival {:?}", + err + )); + return false; + } + + match restart_args { + Some(restart_args) => { + log::info!("Attempting to restart debug session."); + let connection_type = match debugger.connection_type() { + Some(connection_type) => connection_type, + None => { + self.set_error("No starting request found, to be used in restarting the debugging session."); + return false; + } + }; + + let relaunch_resp = if let ConnectionType::Launch = connection_type { + debugger.launch(restart_args).await + } else { + debugger.attach(restart_args).await + }; + + if let Err(err) = relaunch_resp { + self.set_error(format!( + "Failed to restart debugging session: {:?}", + err + )); + } + } + None => { + self.set_status( + "Terminated debugging session and disconnected debugger.", + ); + } + } + } + Event::Exited(resp) => { + let exit_code = resp.exit_code; + if exit_code != 0 { + self.set_error(format!( + "Debuggee failed to exit successfully (exit code: {exit_code})." + )); + } + } ev => { log::warn!("Unhandled event {:?}", ev); return false; // return early to skip render From 44729fbaf9778b67869b2ed6b76b16b2354cc030 Mon Sep 17 00:00:00 2001 From: Filip Dutescu Date: Mon, 20 Feb 2023 06:00:44 +0200 Subject: [PATCH 0005/1169] fix(dap): validate key and index exist when requesting vars (#5628) Check if the stack frames contain the thread id and the frame before trying to get the frame id. If case any of the two fails to be found, provide the user with messages to inform them of the issue and gracefully return. Closes: #5625 Signed-off-by: Filip Dutescu --- helix-term/src/commands/dap.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index b30dc8c0a..023ed3779 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -475,19 +475,36 @@ pub fn dap_variables(cx: &mut Context) { if debugger.thread_id.is_none() { cx.editor - .set_status("Cannot access variables while target is running"); + .set_status("Cannot access variables while target is running."); return; } let (frame, thread_id) = match (debugger.active_frame, debugger.thread_id) { (Some(frame), Some(thread_id)) => (frame, thread_id), _ => { cx.editor - .set_status("Cannot find current stack frame to access variables"); + .set_status("Cannot find current stack frame to access variables."); return; } }; - let frame_id = debugger.stack_frames[&thread_id][frame].id; + let thread_frame = match debugger.stack_frames.get(&thread_id) { + Some(thread_frame) => thread_frame, + None => { + cx.editor + .set_error("Failed to get stack frame for thread: {thread_id}"); + return; + } + }; + let stack_frame = match thread_frame.get(frame) { + Some(stack_frame) => stack_frame, + None => { + cx.editor + .set_error("Failed to get stack frame for thread {thread_id} and frame {frame}."); + return; + } + }; + + let frame_id = stack_frame.id; let scopes = match block_on(debugger.scopes(frame_id)) { Ok(s) => s, Err(e) => { From b89b2eaf68c53a2c10a0f649f9487ad46c85e11e Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Mon, 20 Feb 2023 23:42:54 +0100 Subject: [PATCH 0006/1169] Added yuck language support (for eww) (#6064) --- book/src/generated/lang-support.md | 1 + languages.toml | 13 ++++++ runtime/queries/yuck/highlights.scm | 66 +++++++++++++++++++++++++++++ runtime/queries/yuck/injections.scm | 2 + 4 files changed, 82 insertions(+) create mode 100644 runtime/queries/yuck/highlights.scm create mode 100644 runtime/queries/yuck/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index b9442693d..1cdc9e6ca 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -147,4 +147,5 @@ | xit | ✓ | | | | | xml | ✓ | | ✓ | | | yaml | ✓ | | ✓ | `yaml-language-server` | +| yuck | ✓ | | | | | zig | ✓ | ✓ | ✓ | `zls` | diff --git a/languages.toml b/languages.toml index 6aac13970..38dedb441 100644 --- a/languages.toml +++ b/languages.toml @@ -2180,3 +2180,16 @@ comment-token = "(" [[grammar]] name = "uxntal" source = { git = "https://github.com/Jummit/tree-sitter-uxntal", rev = "9297e95ef74380b0ad84c4fd98f91e9f6e4319e6" } + +[[language]] +name = "yuck" +scope = "source.yuck" +injection-regex = "yuck" +file-types = ["yuck"] +roots = [] +comment-token = ";" +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "yuck" +source = { git = "https://github.com/Philipp-M/tree-sitter-yuck", rev = "9e97da5773f82123a8c8cccf8f7e795d140ed7d1" } diff --git a/runtime/queries/yuck/highlights.scm b/runtime/queries/yuck/highlights.scm new file mode 100644 index 000000000..483348a8c --- /dev/null +++ b/runtime/queries/yuck/highlights.scm @@ -0,0 +1,66 @@ +(ERROR) @error + +(line_comment) @comment + +; keywords and symbols + +(keyword) @keyword +(symbol) @tag + +; literals + +(bool_literal) @constant.builtin.boolean +(num_literal) @constant.numeric + +; strings +(string_interpolation + (string_interpolation_start) @punctuation.special + (string_interpolation_end) @punctuation.special) + +(escape_sequence) @constant.character.escape + +(string + [ + (unescaped_single_quote_string_fragment) + (unescaped_double_quote_string_fragment) + (unescaped_backtick_string_fragment) + "\"" + "'" + "`" + ]) @string + +; operators and general punctuation + +(unary_expression + operator: _ @operator) + +(binary_expression + operator: _ @operator) + +(ternary_expression + operator: _ @operator) + +[ + ":" + "." + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket +[ + ":" + "." + "," +] @punctuation.delimiter + +; Rest (general identifiers that are not yet catched) + +(index) @variable +(ident) @variable diff --git a/runtime/queries/yuck/injections.scm b/runtime/queries/yuck/injections.scm new file mode 100644 index 000000000..d3fdb0ca7 --- /dev/null +++ b/runtime/queries/yuck/injections.scm @@ -0,0 +1,2 @@ +((line_comment) @injection.content + (#set! injection.language "comment")) From 864ee8fdef435893832a2698159407bd9b43f274 Mon Sep 17 00:00:00 2001 From: Erasin Date: Tue, 21 Feb 2023 07:04:17 +0800 Subject: [PATCH 0007/1169] Add GNU gettext PO grammar (#5996) --- book/src/generated/lang-support.md | 1 + languages.toml | 11 +++++++++++ runtime/queries/po/highlights.scm | 15 +++++++++++++++ runtime/queries/po/textobjects.scm | 6 ++++++ 4 files changed, 33 insertions(+) create mode 100644 runtime/queries/po/highlights.scm create mode 100644 runtime/queries/po/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 1cdc9e6ca..cdecb9b04 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -98,6 +98,7 @@ | pem | ✓ | | | | | perl | ✓ | ✓ | ✓ | | | php | ✓ | ✓ | ✓ | `intelephense` | +| po | ✓ | ✓ | | | | ponylang | ✓ | ✓ | ✓ | | | prisma | ✓ | | | `prisma-language-server` | | prolog | | | | `swipl` | diff --git a/languages.toml b/languages.toml index 38dedb441..1caef6b2d 100644 --- a/languages.toml +++ b/languages.toml @@ -2193,3 +2193,14 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "yuck" source = { git = "https://github.com/Philipp-M/tree-sitter-yuck", rev = "9e97da5773f82123a8c8cccf8f7e795d140ed7d1" } + +[[language]] +name = "po" +scope = "source.po" +file-types = ["po", "pot"] +roots = [] +comment-token = "#" + +[[grammar]] +name = "po" +source = { git = "https://github.com/erasin/tree-sitter-po", rev = "417cee9abb2053ed26b19e7de972398f2da9b29e" } diff --git a/runtime/queries/po/highlights.scm b/runtime/queries/po/highlights.scm new file mode 100644 index 000000000..b090f129d --- /dev/null +++ b/runtime/queries/po/highlights.scm @@ -0,0 +1,15 @@ +[ + (msgctxt) + (msgid) + (msgid_plural) + (msgstr) +]@keyword + +(comment) @comment +(comment (comment_reference (text) @string.special.path)) +(comment (comment_flag (text) @label)) + +(number) @constant.numeric +(string) @string + +(ERROR) @error diff --git a/runtime/queries/po/textobjects.scm b/runtime/queries/po/textobjects.scm new file mode 100644 index 000000000..70abcb149 --- /dev/null +++ b/runtime/queries/po/textobjects.scm @@ -0,0 +1,6 @@ +(msgid) @parameter.inside + +(comment) @comment.inside +(comment)+ @comment.around + + From d9d679652872a0bb637cd5fc056ca8faad1c505d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:15:13 -0600 Subject: [PATCH 0008/1169] build(deps): bump serde_json from 1.0.92 to 1.0.93 (#6069) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.92 to 1.0.93. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.92...v1.0.93) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b70f34c41..06fc37629 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1866,9 +1866,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", From 5c9f8ae2cce5f8c4d8151a1c92e00dcccf3b1f13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:16:31 -0600 Subject: [PATCH 0009/1169] build(deps): bump toml from 0.7.1 to 0.7.2 (#6070) Bumps [toml](https://github.com/toml-rs/toml) from 0.7.1 to 0.7.2. - [Release notes](https://github.com/toml-rs/toml/releases) - [Commits](https://github.com/toml-rs/toml/compare/toml-v0.7.1...toml-v0.7.2) --- updated-dependencies: - dependency-name: toml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06fc37629..6f38ba143 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2192,9 +2192,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772c1426ab886e7362aedf4abc9c0d1348a979517efedfc25862944d10137af0" +checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" dependencies = [ "serde", "serde_spanned", @@ -2213,9 +2213,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a238ee2e6ede22fb95350acc78e21dc40da00bb66c0334bde83de4ed89424e" +checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5" dependencies = [ "indexmap", "nom8", From 8016dccd600a6ef6175636f0b0bce7e8849d4012 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:19:36 -0600 Subject: [PATCH 0010/1169] build(deps): bump tokio-stream from 0.1.11 to 0.1.12 (#6071) Bumps [tokio-stream](https://github.com/tokio-rs/tokio) from 0.1.11 to 0.1.12. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-stream-0.1.11...tokio-stream-0.1.12) --- updated-dependencies: - dependency-name: tokio-stream dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-lsp/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f38ba143..a06dc2c72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2181,9 +2181,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite", diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 068119024..133ead298 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -24,5 +24,5 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" tokio = { version = "1.25", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } -tokio-stream = "0.1.11" +tokio-stream = "0.1.12" which = "4.4" From 1a87d14439bc940d9bf3e66359a612b345aa363f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:22:44 -0600 Subject: [PATCH 0011/1169] build(deps): bump once_cell from 1.17.0 to 1.17.1 (#6072) Bumps [once_cell](https://github.com/matklad/once_cell) from 1.17.0 to 1.17.1. - [Release notes](https://github.com/matklad/once_cell/releases) - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.17.0...v1.17.1) --- updated-dependencies: - dependency-name: once_cell dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a06dc2c72..9f8302e28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1605,9 +1605,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "parking_lot" From 8043959265366ed0c945b7f868337a3f9823a747 Mon Sep 17 00:00:00 2001 From: Alexandr Date: Wed, 22 Feb 2023 15:25:15 +0200 Subject: [PATCH 0012/1169] Doc string fix in selection.rs (#6077) * Doc string fix Delete duplicate `the` * selection.rs doc string wording * Remove extra whitespace at end of doc text --------- Co-authored-by: Ivan Tham --- helix-core/src/selection.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 7817618fb..0ac2c6802 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -21,14 +21,14 @@ use std::borrow::Cow; /// can be in any order, or even share the same position. /// /// The anchor and head positions use gap indexing, meaning -/// that their indices represent the the gaps *between* `char`s +/// that their indices represent the gaps *between* `char`s /// rather than the `char`s themselves. For example, 1 /// represents the position between the first and second `char`. /// -/// Below are some example `Range` configurations to better -/// illustrate. The anchor and head indices are show as -/// "(anchor, head)", followed by example text with "[" and "]" -/// inserted to represent the anchor and head positions: +/// Below are some examples of `Range` configurations. +/// The anchor and head indices are shown as "(anchor, head)" +/// tuples, followed by example text with "[" and "]" symbols +/// representing the anchor and head positions: /// /// - (0, 3): `[Som]e text`. /// - (3, 0): `]Som[e text`. From 6494fc1daf2bb379e6a1f4ec94f3dafd99b3fb34 Mon Sep 17 00:00:00 2001 From: LeoniePhiline <22329650+LeoniePhiline@users.noreply.github.com> Date: Thu, 23 Feb 2023 01:04:33 +0100 Subject: [PATCH 0013/1169] feat(sql): MariaDB/MySQL syntax, Apache Hive syntax, unified builtin functions, floats, negative integers (#6041) * feat(sql): MariaDB/MySQL table options `COLLATE`, `CHARACTER SET`, `ENGINE` Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/286e10c5bc5d1703ee8f9afb351165a9a6182be1...30e15d45dceb24ea51acf81ee7d75d81567b6e02 * feat(sql): Optional `COLUMN` in `ALTER TABLE` Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/30e15d45dceb24ea51acf81ee7d75d81567b6e02...c508e6044adf4298d7b321f966c90cbe32d75d23 * feat(sql): Add `UNSIGNED` support, refactor numeric types Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/c508e6044adf4298d7b321f966c90cbe32d75d23...2d1d5b68a1e11796dd0f4f068fc3e9d7e59fe9f7 * feat(sql): Add support for Apache Spark create Hive table Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/2d1d5b68a1e11796dd0f4f068fc3e9d7e59fe9f7...7be06f4d5eabace883dd45959c13dc740f1f1b98 * feat(sql): Add support for signed and unsigned floating point literals Upstream changes: https://github.com/DerekStride/tree-sitter-sql/pull/92/files * feat(sql): Add interval data type Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/13d375dea377bae5f235176fae97a50ba584db54...7b4bcd0394d759a660f470a4f07aa08b7b130d8c * feat(sql): Add support for DROP INDEX Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/7b4bcd0394d759a660f470a4f07aa08b7b130d8c...173d6feb5064defb7d0ef742a4fc7c6d763a2df0 * feat(sql): Add MariaDB/MySQL `ALTER TABLE ... CHANGE|MODIFY ... [FIRST|AFTER]` syntax Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/173d6feb5064defb7d0ef742a4fc7c6d763a2df0...0d7a121b2a08fb37109f7be1cc6654443cad661f * feat(sql): Extract fields from Apache Hive storage location and row format Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/0d7a121b2a08fb37109f7be1cc6654443cad661f...d2f0f6695fffa4ec1c81fc2060eddf83161f9ee3 * feat(sql): Fix unified built-in functions Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/d2f0f6695fffa4ec1c81fc2060eddf83161f9ee3...e4e43ba742a2ee88cbb24dbf305a7daadd583873 * feat(sql): Support negative integers Upstream changes: https://github.com/DerekStride/tree-sitter-sql/compare/e4e43ba742a2ee88cbb24dbf305a7daadd583873...3a3f92b29c880488a08bc2baaf1aca6432ec3380 * rework(sql): Improve `@constant.numeric` regex --- languages.toml | 2 +- runtime/queries/sql/highlights.scm | 75 +++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/languages.toml b/languages.toml index 1caef6b2d..d8888c218 100644 --- a/languages.toml +++ b/languages.toml @@ -1434,7 +1434,7 @@ injection-regex = "sql" [[grammar]] name = "sql" -source = { git = "https://github.com/DerekStride/tree-sitter-sql", rev = "286e10c5bc5d1703ee8f9afb351165a9a6182be1" } +source = { git = "https://github.com/DerekStride/tree-sitter-sql", rev = "3a3f92b29c880488a08bc2baaf1aca6432ec3380" } [[language]] name = "gdscript" diff --git a/runtime/queries/sql/highlights.scm b/runtime/queries/sql/highlights.scm index a2d2f7feb..f15a92e7e 100644 --- a/runtime/queries/sql/highlights.scm +++ b/runtime/queries/sql/highlights.scm @@ -1,20 +1,17 @@ -(keyword_btree) @function.builtin -(keyword_hash) @function.builtin -(keyword_gist) @function.builtin -(keyword_spgist) @function.builtin -(keyword_gin) @function.builtin -(keyword_brin) @function.builtin - -(cast - name: (identifier) @function.builtin) - -(count - name: (identifier) @function.builtin) -(keyword_group_concat) @function.builtin +[ + (keyword_btree) + (keyword_hash) + (keyword_gist) + (keyword_spgist) + (keyword_gin) + (keyword_brin) -(invocation - name: (identifier) @function.builtin) + (cast) + (count) + (group_concat) + (invocation) +] @function.builtin (table_reference name: (identifier) @namespace) @@ -57,7 +54,7 @@ ] @constant.builtin ((literal) @constant.numeric - (#match? @constant.numeric "^(-?\d*\.?\d*)$")) + (#match? @constant.numeric "^-?\\d*\\.?\\d*$")) (literal) @string @@ -92,6 +89,8 @@ (keyword_primary) (keyword_create) (keyword_alter) + (keyword_change) + (keyword_modify) (keyword_drop) (keyword_add) (keyword_table) @@ -119,8 +118,12 @@ (keyword_if) (keyword_exists) (keyword_auto_increment) + (keyword_collate) + (keyword_character) + (keyword_engine) (keyword_default) (keyword_cascade) + (keyword_restrict) (keyword_with) (keyword_no) (keyword_data) @@ -144,6 +147,7 @@ (keyword_over) (keyword_nulls) (keyword_first) + (keyword_after) (keyword_last) (keyword_window) (keyword_range) @@ -170,6 +174,37 @@ (keyword_like) (keyword_similar) (keyword_preserve) + (keyword_unsigned) + (keyword_zerofill) + + (keyword_external) + (keyword_stored) + (keyword_cached) + (keyword_uncached) + (keyword_replication) + (keyword_tblproperties) + (keyword_compute) + (keyword_stats) + (keyword_location) + (keyword_partitioned) + (keyword_comment) + (keyword_sort) + (keyword_format) + (keyword_delimited) + (keyword_fields) + (keyword_terminated) + (keyword_escaped) + (keyword_lines) + + (keyword_parquet) + (keyword_rcfile) + (keyword_csv) + (keyword_textfile) + (keyword_avro) + (keyword_sequencefile) + (keyword_orc) + (keyword_avro) + (keyword_jsonfile) ] @keyword [ @@ -193,9 +228,11 @@ (keyword_smallserial) (keyword_serial) (keyword_bigserial) - (keyword_smallint) - (keyword_int) + (tinyint) + (smallint) + (mediumint) + (int) (bigint) (decimal) (numeric) @@ -222,6 +259,8 @@ (keyword_timestamp) (keyword_timestamptz) + (keyword_interval) + (keyword_geometry) (keyword_geography) (keyword_box2d) From 621ab0e57f051790a663dd4a32c841bb96bdd527 Mon Sep 17 00:00:00 2001 From: Skyler Hawthorne Date: Thu, 23 Feb 2023 23:27:24 -0500 Subject: [PATCH 0014/1169] update tree-sitter-git-rebase hash (#6094) --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index d8888c218..aade6bf26 100644 --- a/languages.toml +++ b/languages.toml @@ -1120,7 +1120,7 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "git-rebase" -source = { git = "https://github.com/the-mikedavis/tree-sitter-git-rebase", rev = "332dc528f27044bc4427024dbb33e6941fc131f2" } +source = { git = "https://github.com/the-mikedavis/tree-sitter-git-rebase", rev = "d8a4207ebbc47bd78bacdf48f883db58283f9fd8" } [[language]] name = "regex" From e5af0f1d49547295be796a600c0841135f331618 Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sat, 25 Feb 2023 06:27:13 +0300 Subject: [PATCH 0015/1169] build(nix): update flake to use flake-parts and nci flake-parts module --- flake.lock | 132 +++++++++++++++---------- flake.nix | 217 +++++++++++++++++++----------------------- helix-term/Cargo.toml | 4 - 3 files changed, 180 insertions(+), 173 deletions(-) diff --git a/flake.lock b/flake.lock index 4cf1018c4..de72c612a 100644 --- a/flake.lock +++ b/flake.lock @@ -16,22 +16,6 @@ "type": "github" } }, - "devshell": { - "flake": false, - "locked": { - "lastModified": 1667210711, - "narHash": "sha256-IoErjXZAkzYWHEpQqwu/DeRNJGFdR7X2OGbkhMqMrpw=", - "owner": "numtide", - "repo": "devshell", - "rev": "96a9dd12b8a447840cc246e17a47b81a4268bba7", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "devshell", - "type": "github" - } - }, "dream2nix": { "inputs": { "alejandra": [ @@ -42,10 +26,12 @@ ], "crane": "crane", "devshell": [ + "nci" + ], + "flake-parts": [ "nci", - "devshell" + "parts" ], - "flake-parts": "flake-parts", "flake-utils-pre-commit": [ "nci" ], @@ -70,14 +56,17 @@ ], "pre-commit-hooks": [ "nci" + ], + "pruned-racket-catalog": [ + "nci" ] }, "locked": { - "lastModified": 1671323629, - "narHash": "sha256-9KHTPjIDjfnzZ4NjpE3gGIVHVHopy6weRDYO/7Y3hF8=", + "lastModified": 1677289985, + "narHash": "sha256-lUp06cTTlWubeBGMZqPl9jODM99LpWMcwxRiscFAUJg=", "owner": "nix-community", "repo": "dream2nix", - "rev": "2d7d68505c8619410df2c6b6463985f97cbcba6e", + "rev": "28b973a8d4c30cc1cbb3377ea2023a76bc3fb889", "type": "github" }, "original": { @@ -86,24 +75,6 @@ "type": "github" } }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1668450977, - "narHash": "sha256-cfLhMhnvXn6x1vPm+Jow3RiFAUSCw/l1utktCw5rVA4=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "d591857e9d7dd9ddbfba0ea02b43b927c3c0f1fa", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, "flake-utils": { "locked": { "lastModified": 1659877975, @@ -119,23 +90,40 @@ "type": "github" } }, + "mk-naked-shell": { + "flake": false, + "locked": { + "lastModified": 1676572903, + "narHash": "sha256-oQoDHHUTxNVSURfkFcYLuAK+btjs30T4rbEUtCUyKy8=", + "owner": "yusdacra", + "repo": "mk-naked-shell", + "rev": "aeca9f8aa592f5e8f71f407d081cb26fd30c5a57", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "mk-naked-shell", + "type": "github" + } + }, "nci": { "inputs": { - "devshell": "devshell", "dream2nix": "dream2nix", + "mk-naked-shell": "mk-naked-shell", "nixpkgs": [ "nixpkgs" ], + "parts": "parts", "rust-overlay": [ "rust-overlay" ] }, "locked": { - "lastModified": 1671430291, - "narHash": "sha256-UIc7H8F3N8rK72J/Vj5YJdV72tvDvYjH+UPsOFvlcsE=", + "lastModified": 1677294491, + "narHash": "sha256-p09IOJqhUOM6egRJe4Ou1EXdTs/I9Pmm8e7pMYDlIWM=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "b1b0d38b8c3b0d0e6a38638d5bbe10b0bc67522c", + "rev": "a525ed36c440854f296cd958f4ebf574f0ebe22c", "type": "github" }, "original": { @@ -146,11 +134,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1671359686, - "narHash": "sha256-3MpC6yZo+Xn9cPordGz2/ii6IJpP2n8LE8e/ebUXLrs=", + "lastModified": 1677063315, + "narHash": "sha256-qiB4ajTeAOVnVSAwCNEEkoybrAlA+cpeiBxLobHndE8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "04f574a1c0fde90b51bf68198e2297ca4e7cccf4", + "rev": "988cc958c57ce4350ec248d2d53087777f9e1949", "type": "github" }, "original": { @@ -163,11 +151,11 @@ "nixpkgs-lib": { "locked": { "dir": "lib", - "lastModified": 1665349835, - "narHash": "sha256-UK4urM3iN80UXQ7EaOappDzcisYIuEURFRoGQ/yPkug=", + "lastModified": 1675183161, + "narHash": "sha256-Zq8sNgAxDckpn7tJo7V1afRSk2eoVbu3OjI1QklGLNg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "34c5293a71ffdb2fe054eb5288adc1882c1eb0b1", + "rev": "e1e1b192c1a5aab2960bf0a0bd53a2e8124fa18e", "type": "github" }, "original": { @@ -178,10 +166,50 @@ "type": "github" } }, + "parts": { + "inputs": { + "nixpkgs-lib": [ + "nci", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1675933616, + "narHash": "sha256-/rczJkJHtx16IFxMmAWu5nNYcSXNg1YYXTHoGjLrLUA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "47478a4a003e745402acf63be7f9a092d51b83d7", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1675933616, + "narHash": "sha256-/rczJkJHtx16IFxMmAWu5nNYcSXNg1YYXTHoGjLrLUA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "47478a4a003e745402acf63be7f9a092d51b83d7", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "root": { "inputs": { "nci": "nci", "nixpkgs": "nixpkgs", + "parts": "parts_2", "rust-overlay": "rust-overlay" } }, @@ -193,11 +221,11 @@ ] }, "locked": { - "lastModified": 1671416426, - "narHash": "sha256-kpSH1Jrxfk2qd0pRPJn1eQdIOseGv5JuE+YaOrqU9s4=", + "lastModified": 1677292251, + "narHash": "sha256-D+6q5Z2MQn3UFJtqsM5/AvVHi3NXKZTIMZt1JGq/spA=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "fbaaff24f375ac25ec64268b0a0d63f91e474b7d", + "rev": "34cdbf6ad480ce13a6a526f57d8b9e609f3d65dc", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 734ac78e2..66fb641d9 100644 --- a/flake.nix +++ b/flake.nix @@ -12,16 +12,10 @@ inputs.nixpkgs.follows = "nixpkgs"; inputs.rust-overlay.follows = "rust-overlay"; }; + parts.url = "github:hercules-ci/flake-parts"; }; - outputs = { - self, - nixpkgs, - nci, - ... - }: let - lib = nixpkgs.lib; - ncl = nci.lib.nci-lib; + outputs = inp: let mkRootPath = rel: builtins.path { path = "${toString ./.}/${rel}"; @@ -32,6 +26,12 @@ ".envrc" ".ignore" ".github" + ".gitignore" + "logo.svg" + "logo_dark.svg" + "logo_light.svg" + "rust-toolchain.toml" + "rustfmt.toml" "runtime" "screenshot.png" "book" @@ -46,6 +46,7 @@ "flake.lock" ]; ignorePaths = path: type: let + inherit (inp.nixpkgs) lib; # split the nix store path into its components components = lib.splitString "/" path; # drop off the `/nix/hash-source` section from the path @@ -61,123 +62,105 @@ # filter out unnecessary paths filter = ignorePaths; }; - outputs = nci.lib.makeOutputs { - root = ./.; - 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) - ++ (lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.CoreFoundation); - env = [ - { - name = "HELIX_RUNTIME"; - eval = "$PWD/runtime"; - } - { - name = "RUST_BACKTRACE"; - value = "1"; - } + in + inp.parts.lib.mkFlake {inputs = inp;} { + imports = [inp.nci.flakeModule]; + systems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + "i686-linux" + ]; + perSystem = { + config, + pkgs, + lib, + ... + }: let + 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 = + pkgs.runCommand + old.name { - name = "RUSTFLAGS"; - eval = - if common.pkgs.stdenv.isLinux - then "$RUSTFLAGS\" -C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment\"" - else "$RUSTFLAGS"; + inherit (old) pname version; + meta = old.meta or {}; + passthru = + (old.passthru or {}) + // { + unwrapped = old; + }; + nativeBuildInputs = [pkgs.makeWrapper]; + makeWrapperArgs = config.makeWrapperArgs or []; } - ]; - }; - }; - pkgConfig = common: { - helix-term = let - # 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 + '' + cp -rs --no-preserve=mode,ownership ${old} $out + wrapProgram "$out/bin/hx" ''${makeWrapperArgs[@]} --set HELIX_RUNTIME "${runtimeDir}" + ''; + in + helix-wrapped + // { + override = makeOverridableHelix old; + passthru = + helix-wrapped.passthru // { - override = makeOverridableHelix old; - passthru = helix-wrapped.passthru // {wrapper = wrapper {};}; + wrapper = old: makeOverridableHelix old config; }; - in - makeOverridableHelix old {}; - in { - inherit wrapper; - 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 = ncl.addBuildInputs prev [common.config.cCompiler.package.cc.lib]; - - # link languages and theme toml files since helix-term expects them (for tests) - preConfigure = '' - ${prev.preConfigure or ""} - ${ - lib.concatMapStringsSep - "\n" - (path: "ln -sf ${mkRootPath path} ..") - ["languages.toml" "theme.toml" "base16_theme.toml"] - } - ''; - checkPhase = ":"; - - meta.mainProgram = "hx"; + }; + stdenv = + if pkgs.stdenv.isLinux + then pkgs.stdenv + else pkgs.clangStdenv; + rustFlagsEnv = + if stdenv.isLinux + then "$RUSTFLAGS\" -C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment\"" + else "$RUSTFLAGS"; + in { + nci.projects."helix-project".relPath = ""; + nci.crates."helix-term" = { + overrides = { + add-meta.override = _: {meta.mainProgram = "hx";}; + add-inputs.overrideAttrs = prev: { + buildInputs = (prev.buildInputs or []) ++ [stdenv.cc.cc.lib]; + }; + disable-grammar-builds = { + # disable fetching and building of tree-sitter grammars in the helix-term build.rs + HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1"; + }; + disable-tests = {checkPhase = ":";}; + set-stdenv.override = _: {inherit stdenv;}; + set-filtered-src.override = _: {src = filteredSource;}; }; }; + + packages.helix-unwrapped = config.nci.outputs."helix-term".packages.release; + packages.helix-unwrapped-dev = config.nci.outputs."helix-term".packages.dev; + packages.helix = makeOverridableHelix config.packages.helix-unwrapped {}; + packages.helix-dev = makeOverridableHelix config.packages.helix-unwrapped-dev {}; + packages.default = config.packages.helix; + + devShells.default = config.nci.outputs."helix-project".devShell.overrideAttrs (old: { + nativeBuildInputs = + (old.nativeBuildInputs or []) + ++ (with pkgs; [lld_13 cargo-flamegraph rust-analyzer]) + ++ (lib.optional (stdenv.isx86_64 && stdenv.isLinux) pkgs.cargo-tarpaulin) + ++ (lib.optional stdenv.isLinux pkgs.lldb) + ++ (lib.optional stdenv.isDarwin pkgs.darwin.apple_sdk.frameworks.CoreFoundation); + shellHook = '' + export HELIX_RUNTIME="$PWD/runtime" + export RUST_BACKTRACE="1" + export RUSTFLAGS="${rustFlagsEnv}" + ''; + }); }; }; - in - outputs - // { - packages = - lib.mapAttrs - ( - system: packages: - packages - // { - helix-unwrapped = packages.helix.passthru.unwrapped; - helix-unwrapped-dev = packages.helix-dev.passthru.unwrapped; - } - ) - outputs.packages; - }; nixConfig = { extra-substituters = ["https://helix.cachix.org"]; diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 603f37d39..2d4ba436e 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -12,10 +12,6 @@ include = ["src/**/*", "README.md"] default-run = "hx" rust-version = "1.57" -[package.metadata.nix] -build = true -app = true - [features] default = ["git"] unicode-lines = ["helix-core/unicode-lines"] From 309735aa2d9516a734165ee066f2c9a080f9849a Mon Sep 17 00:00:00 2001 From: Yusuf Bera Ertan Date: Sat, 25 Feb 2023 07:06:30 +0300 Subject: [PATCH 0016/1169] build(nix): fix devshell --- flake.lock | 6 +++--- flake.nix | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index de72c612a..fa292273a 100644 --- a/flake.lock +++ b/flake.lock @@ -119,11 +119,11 @@ ] }, "locked": { - "lastModified": 1677294491, - "narHash": "sha256-p09IOJqhUOM6egRJe4Ou1EXdTs/I9Pmm8e7pMYDlIWM=", + "lastModified": 1677297103, + "narHash": "sha256-ArlJIbp9NGV9yvhZdV0SOUFfRlI/kHeKoCk30NbSiLc=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "a525ed36c440854f296cd958f4ebf574f0ebe22c", + "rev": "a79272a2cb0942392bb3a5bf9a3ec6bc568795b2", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 66fb641d9..2ac764888 100644 --- a/flake.nix +++ b/flake.nix @@ -120,9 +120,11 @@ else pkgs.clangStdenv; rustFlagsEnv = if stdenv.isLinux - then "$RUSTFLAGS\" -C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment\"" + then ''$RUSTFLAGS -C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment'' else "$RUSTFLAGS"; in { + # by default NCI adds rust-analyzer component, but helix toolchain doesn't have rust-analyzer + nci.toolchains.shell.components = ["rust-src" "rustfmt" "clippy"]; nci.projects."helix-project".relPath = ""; nci.crates."helix-term" = { overrides = { From 0cbb61c3a45f605f685897027ec0dc606ec8bef5 Mon Sep 17 00:00:00 2001 From: Matthias Deiml Date: Sat, 25 Feb 2023 19:40:02 +0100 Subject: [PATCH 0017/1169] Improve markdown highlights and add latex injection (#6100) --- languages.toml | 4 ++-- runtime/queries/markdown.inline/injections.scm | 2 ++ runtime/queries/markdown/highlights.scm | 8 +++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/languages.toml b/languages.toml index aade6bf26..aa580dec5 100644 --- a/languages.toml +++ b/languages.toml @@ -1026,7 +1026,7 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "markdown" -source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "a7de4be29783a6e25f3240c90afea52f2417faa3", subpath = "tree-sitter-markdown" } +source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "7e7aa9a25ca9729db9fe22912f8f47bdb403a979", subpath = "tree-sitter-markdown" } [[language]] name = "markdown.inline" @@ -1038,7 +1038,7 @@ grammar = "markdown_inline" [[grammar]] name = "markdown_inline" -source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "a7de4be29783a6e25f3240c90afea52f2417faa3", subpath = "tree-sitter-markdown-inline" } +source = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "7e7aa9a25ca9729db9fe22912f8f47bdb403a979", subpath = "tree-sitter-markdown-inline" } [[language]] name = "dart" diff --git a/runtime/queries/markdown.inline/injections.scm b/runtime/queries/markdown.inline/injections.scm index 2dd149d90..c2e7012ca 100644 --- a/runtime/queries/markdown.inline/injections.scm +++ b/runtime/queries/markdown.inline/injections.scm @@ -1,2 +1,4 @@ ((html_tag) @injection.content (#set! injection.language "html") (#set! injection.include-unnamed-children)) + +((latex_block) @injection.content (#set! injection.language "latex") (#set! injection.include-unnamed-children)) diff --git a/runtime/queries/markdown/highlights.scm b/runtime/queries/markdown/highlights.scm index 25f22ba73..80c9f9583 100644 --- a/runtime/queries/markdown/highlights.scm +++ b/runtime/queries/markdown/highlights.scm @@ -39,7 +39,7 @@ (list_marker_parenthesis) ] @markup.list.numbered -(thematic_break) @punctuation.delimiter +(thematic_break) @punctuation.special [ (block_continuation) @@ -51,3 +51,9 @@ ] @string.escape (block_quote) @markup.quote + +(pipe_table_row + "|" @punctuation.special) +(pipe_table_header + "|" @punctuation.special) +(pipe_table_delimiter_row) @punctuation.special From f69bb411691ba023951168e8ee865795328294bb Mon Sep 17 00:00:00 2001 From: Sophie Dankel <47993817+sdankel@users.noreply.github.com> Date: Sat, 25 Feb 2023 10:47:54 -0800 Subject: [PATCH 0018/1169] Add language support for sway (#6023) --- book/src/generated/lang-support.md | 1 + languages.toml | 14 ++ runtime/queries/sway/highlights.scm | 336 +++++++++++++++++++++++++++ runtime/queries/sway/indents.scm | 71 ++++++ runtime/queries/sway/injections.scm | 2 + runtime/queries/sway/locals.scm | 17 ++ runtime/queries/sway/textobjects.scm | 52 +++++ 7 files changed, 493 insertions(+) create mode 100644 runtime/queries/sway/highlights.scm create mode 100644 runtime/queries/sway/indents.scm create mode 100644 runtime/queries/sway/injections.scm create mode 100644 runtime/queries/sway/locals.scm create mode 100644 runtime/queries/sway/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index cdecb9b04..3bd61d7a9 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -125,6 +125,7 @@ | sshclientconfig | ✓ | | | | | starlark | ✓ | ✓ | | | | svelte | ✓ | | | `svelteserver` | +| sway | ✓ | ✓ | ✓ | `forc` | | swift | ✓ | | | `sourcekit-lsp` | | tablegen | ✓ | ✓ | ✓ | | | task | ✓ | | | | diff --git a/languages.toml b/languages.toml index aa580dec5..b364de961 100644 --- a/languages.toml +++ b/languages.toml @@ -52,6 +52,20 @@ args = { attachCommands = [ "platform select remote-gdb-server", "platform conne name = "rust" source = { git = "https://github.com/tree-sitter/tree-sitter-rust", rev = "0431a2c60828731f27491ee9fdefe25e250ce9c9" } +[[language]] +name = "sway" +scope = "source.sway" +injection-regex = "sway" +file-types = ["sw"] +language-server = { command = "forc", args = ["lsp"] } +roots = ["Forc.toml", "Forc.lock"] +indent = { tab-width = 4, unit = " " } +comment-token = "//" + +[[grammar]] +name = "sway" +source = { git = "https://github.com/FuelLabs/tree-sitter-sway", rev = "e491a005ee1d310f4c138bf215afd44cfebf959c" } + [[language]] name = "toml" scope = "source.toml" diff --git a/runtime/queries/sway/highlights.scm b/runtime/queries/sway/highlights.scm new file mode 100644 index 000000000..98f4d4493 --- /dev/null +++ b/runtime/queries/sway/highlights.scm @@ -0,0 +1,336 @@ +; ------- +; Tree-Sitter doesn't allow overrides in regards to captures, +; though it is possible to affect the child node of a captured +; node. Thus, the approach here is to flip the order so that +; overrides are unnecessary. +; ------- + +; ------- +; Types +; ------- + +; --- +; Primitives +; --- + +(escape_sequence) @constant.character.escape +(primitive_type) @type.builtin +(boolean_literal) @constant.builtin.boolean +(integer_literal) @constant.numeric.integer +(float_literal) @constant.numeric.float +(char_literal) @constant.character +[ + (string_literal) + (raw_string_literal) +] @string +[ + (line_comment) + (block_comment) +] @comment + +; --- +; Extraneous +; --- + +(self) @variable.builtin +(enum_variant (identifier) @type.enum.variant) + +(field_initializer + (field_identifier) @variable.other.member) +(shorthand_field_initializer + (identifier) @variable.other.member) +(shorthand_field_identifier) @variable.other.member + +(loop_label + "'" @label + (identifier) @label) + +; --- +; Punctuation +; --- + +[ + "::" + "." + ";" + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" + "#" +] @punctuation.bracket +(type_arguments + [ + "<" + ">" + ] @punctuation.bracket) +(type_parameters + [ + "<" + ">" + ] @punctuation.bracket) +(closure_parameters + "|" @punctuation.bracket) + +; --- +; Variables +; --- + +(let_declaration + pattern: [ + ((identifier) @variable) + ((tuple_pattern + (identifier) @variable)) + ]) + +; It needs to be anonymous to not conflict with `call_expression` further below. +(_ + value: (field_expression + value: (identifier)? @variable + field: (field_identifier) @variable.other.member)) + +(parameter + pattern: (identifier) @variable.parameter) +(closure_parameters + (identifier) @variable.parameter) + +; ------- +; Keywords +; ------- + +(for_expression + "for" @keyword.control.repeat) +((identifier) @keyword.control + (#match? @keyword.control "^yield$")) + +"in" @keyword.control + +[ + "match" + "if" + "else" +] @keyword.control.conditional + +[ + "while" +] @keyword.control.repeat + +[ + "break" + "continue" + "return" +] @keyword.control.return + +[ + "contract" + "script" + "predicate" +] @keyword.other + +"use" @keyword.control.import +(dep_item "dep" @keyword.control.import !body) +(use_as_clause "as" @keyword.control.import) + +(type_cast_expression "as" @keyword.operator) + +[ + "as" + "pub" + "dep" + + "abi" + "impl" + "where" + "trait" + "for" +] @keyword + +[ + "struct" + "enum" + "storage" + "configurable" +] @keyword.storage.type + +"let" @keyword.storage +"fn" @keyword.function +"abi" @keyword.function + +(mutable_specifier) @keyword.storage.modifier.mut + +(reference_type "&" @keyword.storage.modifier.ref) +(self_parameter "&" @keyword.storage.modifier.ref) + +[ + "const" + "ref" + "deref" + "move" +] @keyword.storage.modifier + +; TODO: variable.mut to highlight mutable identifiers via locals.scm + +; ------- +; Guess Other Types +; ------- + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]*$")) + +; --- +; PascalCase identifiers in call_expressions (e.g. `Ok()`) +; are assumed to be enum constructors. +; --- + +(call_expression + function: [ + ((identifier) @type.enum.variant + (#match? @type.enum.variant "^[A-Z]")) + (scoped_identifier + name: ((identifier) @type.enum.variant + (#match? @type.enum.variant "^[A-Z]"))) + ]) + +; --- +; Assume that types in match arms are enums and not +; tuple structs. Same for `if let` expressions. +; --- + +(match_pattern + (scoped_identifier + name: (identifier) @constructor)) +(tuple_struct_pattern + type: [ + ((identifier) @constructor) + (scoped_identifier + name: (identifier) @constructor) + ]) +(struct_pattern + type: [ + ((type_identifier) @constructor) + (scoped_type_identifier + name: (type_identifier) @constructor) + ]) + +; --- +; Other PascalCase identifiers are assumed to be structs. +; --- + +((identifier) @type + (#match? @type "^[A-Z]")) + +; ------- +; Functions +; ------- + +(call_expression + function: [ + ((identifier) @function) + (scoped_identifier + name: (identifier) @function) + (field_expression + field: (field_identifier) @function) + ]) +(generic_function + function: [ + ((identifier) @function) + (scoped_identifier + name: (identifier) @function) + (field_expression + field: (field_identifier) @function.method) + ]) + +(function_item + name: (identifier) @function) + +(function_signature_item + name: (identifier) @function) + +; ------- +; Operators +; ------- + +[ + "*" + "'" + "->" + "=>" + "<=" + "=" + "==" + "!" + "!=" + "%" + "%=" + "&" + "&=" + "&&" + "|" + "|=" + "||" + "^" + "^=" + "*" + "*=" + "-" + "-=" + "+" + "+=" + "/" + "/=" + ">" + "<" + ">=" + ">>" + "<<" + ">>=" + "<<=" + "@" + ".." + "..=" + "'" +] @operator + +; ------- +; Paths +; ------- + +(use_declaration + argument: (identifier) @namespace) +(use_wildcard + (identifier) @namespace) +(dep_item + name: (identifier) @namespace) +(scoped_use_list + path: (identifier)? @namespace) +(use_list + (identifier) @namespace) +(use_as_clause + path: (identifier)? @namespace + alias: (identifier) @namespace) + +; --- +; Remaining Paths +; --- + +(scoped_identifier + path: (identifier)? @namespace + name: (identifier) @namespace) +(scoped_type_identifier + path: (identifier) @namespace) + +; ------- +; Remaining Identifiers +; ------- + +"?" @special + +(type_identifier) @type +(identifier) @variable +(field_identifier) @variable.other.member diff --git a/runtime/queries/sway/indents.scm b/runtime/queries/sway/indents.scm new file mode 100644 index 000000000..e6902b62c --- /dev/null +++ b/runtime/queries/sway/indents.scm @@ -0,0 +1,71 @@ +[ + (use_list) + (block) + (match_block) + (arguments) + (parameters) + (declaration_list) + (field_declaration_list) + (field_initializer_list) + (struct_pattern) + (tuple_pattern) + (unit_expression) + (enum_variant_list) + (call_expression) + (binary_expression) + (field_expression) + (tuple_expression) + (array_expression) + (where_clause) + + (token_tree) +] @indent + +[ + "}" + "]" + ")" +] @outdent + +; Indent the right side of assignments. +; The #not-same-line? predicate is required to prevent an extra indent for e.g. +; an else-clause where the previous if-clause starts on the same line as the assignment. +(assignment_expression + . + (_) @expr-start + right: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(compound_assignment_expr + . + (_) @expr-start + right: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(let_declaration + . + (_) @expr-start + value: (_) @indent + alternative: (_)? @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) +(if_expression + . + (_) @expr-start + condition: (_) @indent + (#not-same-line? @indent @expr-start) + (#set! "scope" "all") +) + +; Some field expressions where the left part is a multiline expression are not +; indented by cargo fmt. +; Because this multiline expression might be nested in an arbitrary number of +; field expressions, this can only be matched using a Regex. +(field_expression + value: (_) @val + "." @outdent + (#match? @val "(\\A[^\\n\\r]+\\([\\t ]*(\\n|\\r).*)|(\\A[^\\n\\r]*\\{[\\t ]*(\\n|\\r))") +) diff --git a/runtime/queries/sway/injections.scm b/runtime/queries/sway/injections.scm new file mode 100644 index 000000000..e4509a5fd --- /dev/null +++ b/runtime/queries/sway/injections.scm @@ -0,0 +1,2 @@ +([(line_comment) (block_comment)] @injection.content + (#set! injection.language "comment")) diff --git a/runtime/queries/sway/locals.scm b/runtime/queries/sway/locals.scm new file mode 100644 index 000000000..262d609e9 --- /dev/null +++ b/runtime/queries/sway/locals.scm @@ -0,0 +1,17 @@ +; Scopes + +[ + (function_item) + (closure_expression) + (block) +] @local.scope + +; Definitions + +(parameter + (identifier) @local.definition) + +(closure_parameters (identifier) @local.definition) + +; References +(identifier) @local.reference diff --git a/runtime/queries/sway/textobjects.scm b/runtime/queries/sway/textobjects.scm new file mode 100644 index 000000000..15740bc85 --- /dev/null +++ b/runtime/queries/sway/textobjects.scm @@ -0,0 +1,52 @@ +(function_item + body: (_) @function.inside) @function.around(closure_expression body: (_) @function.inside) @function.around + +(struct_item + body: (_) @class.inside) @class.around + +(enum_item + body: (_) @class.inside) @class.around + +(trait_item + body: (_) @class.inside) @class.around + +(impl_item + body: (_) @class.inside) @class.around + +(parameters + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(type_parameters + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(type_arguments + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(closure_parameters + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(arguments + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +[ + (line_comment) + (block_comment) +] @comment.inside + +(line_comment)+ @comment.around + +(block_comment) @comment.around + +(; #[test] + (attribute_item + (attribute + (identifier) @_test_attribute)) + ; allow other attributes like #[should_panic] and comments + [ + (attribute_item) + (line_comment) + ]* + ; the test function + (function_item + body: (_) @test.inside) @test.around + (#eq? @_test_attribute "test")) From a4049e6f55144f502a1d6d1538b690f3e24524ef Mon Sep 17 00:00:00 2001 From: Matthew Toohey Date: Sat, 25 Feb 2023 13:53:37 -0500 Subject: [PATCH 0019/1169] feat: add nasm language (#6068) --- book/src/generated/lang-support.md | 1 + languages.toml | 13 +++ runtime/queries/nasm/highlights.scm | 126 +++++++++++++++++++++++++++ runtime/queries/nasm/injections.scm | 2 + runtime/queries/nasm/textobjects.scm | 15 ++++ 5 files changed, 157 insertions(+) create mode 100644 runtime/queries/nasm/highlights.scm create mode 100644 runtime/queries/nasm/injections.scm create mode 100644 runtime/queries/nasm/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 3bd61d7a9..4011aa114 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -85,6 +85,7 @@ | meson | ✓ | | ✓ | | | mint | | | | `mint` | | msbuild | ✓ | | ✓ | | +| nasm | ✓ | ✓ | | | | nickel | ✓ | | ✓ | `nls` | | nix | ✓ | | | `nil` | | nu | ✓ | | | | diff --git a/languages.toml b/languages.toml index b364de961..d4ea9a867 100644 --- a/languages.toml +++ b/languages.toml @@ -2218,3 +2218,16 @@ comment-token = "#" [[grammar]] name = "po" source = { git = "https://github.com/erasin/tree-sitter-po", rev = "417cee9abb2053ed26b19e7de972398f2da9b29e" } + +[[language]] +name = "nasm" +scope = "source.nasm" +file-types = ["asm", "s", "S", "nasm"] +injection-regex = "n?asm" +roots = [] +comment-token = ";" +indent = { tab-width = 8, unit = " " } + +[[grammar]] +name = "nasm" +source = { git = "https://github.com/naclsn/tree-sitter-nasm", rev = "a0db15db6fcfb1bf2cc8702500e55e558825c48b" } diff --git a/runtime/queries/nasm/highlights.scm b/runtime/queries/nasm/highlights.scm new file mode 100644 index 000000000..5e3cfebe6 --- /dev/null +++ b/runtime/queries/nasm/highlights.scm @@ -0,0 +1,126 @@ +(comment) @comment + +(label) @label + +(preproc_expression) @keyword.directive + +[ + (line_here_token) + (section_here_token) +] @variable.builtin + +(unary_expression + operator: _ @operator) +(binary_expression + operator: _ @operator) +(conditional_expression + "?" @operator + ":" @operator) + +[ + ":" + "," +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +(instruction_prefix) @keyword +(actual_instruction + instruction: (word) @function) + +(call_syntax_expression + base: (word) @function) + +(size_hint) @type +(struc_declaration + name: (word) @type) +(struc_instance + name: (word) @type) + +(effective_address + hint: _ @type) +(effective_address + segment: _ @constant.builtin) + +(register) @constant.builtin + +(number_literal) @constant.numeric.integer +(string_literal) @string +(float_literal) @constant.numeric.float +(packed_bcd_literal) @constant.numeric.integer + +((word) @constant + (#match? @constant "^[A-Z_][?A-Z_0-9]+$")) +((word) @constant.builtin + (#match? @constant.builtin "^__\\?[A-Z_a-z0-9]+\\?__$")) +(word) @variable + +(preproc_arg) @keyword.directive + +[ + (preproc_def) + (preproc_function_def) + (preproc_undef) + (preproc_alias) + (preproc_multiline_macro) + (preproc_multiline_unmacro) + (preproc_if) + (preproc_rotate) + (preproc_rep_loop) + (preproc_include) + (preproc_pathsearch) + (preproc_depend) + (preproc_use) + (preproc_push) + (preproc_pop) + (preproc_repl) + (preproc_arg) + (preproc_stacksize) + (preproc_local) + (preproc_reporting) + (preproc_pragma) + (preproc_line) + (preproc_clear) +] @keyword.directive +[ + (pseudo_instruction_dx) + (pseudo_instruction_resx) + (pseudo_instruction_incbin_command) + (pseudo_instruction_equ_command) + (pseudo_instruction_times_prefix) + (pseudo_instruction_alignx_macro) +] @function.special +[ + (assembl_directive_target) + (assembl_directive_defaults) + (assembl_directive_sections) + (assembl_directive_absolute) + (assembl_directive_symbols) + (assembl_directive_common) + (assembl_directive_symbolfixes) + (assembl_directive_cpu) + (assembl_directive_floathandling) + (assembl_directive_org) + (assembl_directive_sectalign) + + (assembl_directive_primitive_target) + (assembl_directive_primitive_defaults) + (assembl_directive_primitive_sections) + (assembl_directive_primitive_absolute) + (assembl_directive_primitive_symbols) + (assembl_directive_primitive_common) + (assembl_directive_primitive_symbolfixes) + (assembl_directive_primitive_cpu) + (assembl_directive_primitive_floathandling) + (assembl_directive_primitive_org) + (assembl_directive_primitive_sectalign) + (assembl_directive_primitive_warning) + (assembl_directive_primitive_map) +] @keyword diff --git a/runtime/queries/nasm/injections.scm b/runtime/queries/nasm/injections.scm new file mode 100644 index 000000000..2f0e58eb6 --- /dev/null +++ b/runtime/queries/nasm/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/runtime/queries/nasm/textobjects.scm b/runtime/queries/nasm/textobjects.scm new file mode 100644 index 000000000..ddfbad78c --- /dev/null +++ b/runtime/queries/nasm/textobjects.scm @@ -0,0 +1,15 @@ +(preproc_multiline_macro + body: (body) @function.inside) @function.around +(struc_declaration + body: (struc_declaration_body) @class.inside) @class.around +(struc_instance + body: (struc_instance_body) @class.inside) @class.around + +(preproc_function_def_parameters + (word) @parameter.inside) +(call_syntax_arguments + (_) @parameter.inside) +(operand) @parameter.inside + +(comment) @comment.inside +(comment)+ @comment.around From 98a3d46912be7dcc650c54ea417d7f00ab6d05a3 Mon Sep 17 00:00:00 2001 From: Mathieu Agopian Date: Sat, 25 Feb 2023 19:55:44 +0100 Subject: [PATCH 0020/1169] Add elm treesitter textobjects (#6084) --- book/src/generated/lang-support.md | 2 +- runtime/queries/elm/textobjects.scm | 63 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/elm/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 4011aa114..e997b3e81 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -30,7 +30,7 @@ | eex | ✓ | | | | | ejs | ✓ | | | | | elixir | ✓ | ✓ | ✓ | `elixir-ls` | -| elm | ✓ | | | `elm-language-server` | +| elm | ✓ | ✓ | | `elm-language-server` | | elvish | ✓ | | | `elvish` | | env | ✓ | | | | | erb | ✓ | | | | diff --git a/runtime/queries/elm/textobjects.scm b/runtime/queries/elm/textobjects.scm new file mode 100644 index 000000000..d212e9c3b --- /dev/null +++ b/runtime/queries/elm/textobjects.scm @@ -0,0 +1,63 @@ +(line_comment) @comment.inside +(line_comment)+ @comment.around +(block_comment) @comment.inside +(block_comment)+ @comment.around + +((type_annotation)? + (value_declaration + (function_declaration_left (lower_case_identifier)) + (eq) + (_) @function.inside + ) +) @function.around + +(parenthesized_expr + (anonymous_function_expr + ( + (arrow) + (_) @function.inside + ) + ) +) @function.around + +(value_declaration + (function_declaration_left + (lower_pattern + (lower_case_identifier) @parameter.inside @parameter.around + ) + ) +) + +(value_declaration + (function_declaration_left + (pattern) @parameter.inside @parameter.around + ) +) + +(value_declaration + (function_declaration_left + (tuple_pattern + (pattern) @parameter.inside + ) @parameter.around + ) +) + +(value_declaration + (function_declaration_left + (record_pattern + (lower_pattern + (lower_case_identifier) @parameter.inside + ) + ) @parameter.around + ) +) + +(parenthesized_expr + (anonymous_function_expr + ( + (backslash) + (pattern) @parameter.inside + (arrow) + ) + ) +) From eb3086a5b3d37e871e1e752652617fbc2dc2a085 Mon Sep 17 00:00:00 2001 From: Adam Becker <47185607+adam-becker@users.noreply.github.com> Date: Sun, 26 Feb 2023 14:29:16 -0700 Subject: [PATCH 0021/1169] Fix diagnostic underline colors in catppuccin themes (#6107) --- runtime/themes/catppuccin_mocha.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/themes/catppuccin_mocha.toml b/runtime/themes/catppuccin_mocha.toml index 2504fec87..59fa430f8 100644 --- a/runtime/themes/catppuccin_mocha.toml +++ b/runtime/themes/catppuccin_mocha.toml @@ -103,10 +103,10 @@ "ui.menu" = { fg = "overlay2", bg = "surface0" } "ui.menu.selected" = { fg = "text", bg = "surface1", modifiers = ["bold"] } -"diagnostic.error" = { fg = "red", underline = { color = "red", style = "curl" } } -"diagnostic.warning" = { fg = "yellow", underline = { color = "yellow", style = "curl" } } -"diagnostic.info" = { fg = "sky", underline = { color = "sky", style = "curl" } } -"diagnostic.hint" = { fg = "teal", underline = { color = "teal", style = "curl" } } +"diagnostic.error" = { underline = { color = "red", style = "curl" } } +"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } } +"diagnostic.info" = { underline = { color = "sky", style = "curl" } } +"diagnostic.hint" = { underline = { color = "teal", style = "curl" } } error = "red" warning = "yellow" From cac4a3604cc9049f03054e964776c4fe53352696 Mon Sep 17 00:00:00 2001 From: luetage Date: Sun, 26 Feb 2023 22:34:46 +0100 Subject: [PATCH 0022/1169] Kanagawa: fix bufferline, theme wrap-indicators, cursor, menu, and syntax changes (#6085) --- runtime/themes/kanagawa.toml | 53 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/runtime/themes/kanagawa.toml b/runtime/themes/kanagawa.toml index 16e27362f..a7d33f3ee 100644 --- a/runtime/themes/kanagawa.toml +++ b/runtime/themes/kanagawa.toml @@ -14,9 +14,8 @@ "ui.linenr" = { fg = "sumiInk4" } "ui.linenr.selected" = { fg = "roninYellow" } +"ui.virtual" = "sumiInk4" "ui.virtual.ruler" = { bg = "sumiInk2" } -"ui.virtual.whitespace" = "waveBlue1" -"ui.virtual.indent-guide" = "sumiInk4" "ui.statusline" = { fg = "oldWhite", bg = "sumiInk0" } "ui.statusline.inactive" = { fg = "fujiGray", bg = "sumiInk0" } @@ -24,8 +23,9 @@ "ui.statusline.insert" = { fg = "sumiInk0", bg = "autumnGreen", modifiers = ["bold"] } "ui.statusline.select" = { fg = "sumiInk0", bg = "oniViolet", modifiers = ["bold"] } -"ui.bufferline" = { fg = "oldWhite", bg = "sumiInk0" } -"ui.bufferline.inactive" = { fg = "fujiGray", bg = "sumiInk0" } +"ui.bufferline" = { fg = "fujiGray", bg = "sumiInk0" } +"ui.bufferline.active" = { fg = "oldWhite", bg = "sumiInk0" } +"ui.bufferline.background" = { bg = "sumiInk0" } "ui.popup" = { fg = "fujiWhite", bg = "sumiInk0" } "ui.window" = { fg = "sumiInk0" } @@ -33,14 +33,16 @@ "ui.text" = "fujiWhite" "ui.text.focus" = { fg = "fujiWhite", bg = "waveBlue1", modifiers = ["bold"] } -"ui.cursor" = { fg = "waveBlue1", bg = "fujiWhite"} -"ui.cursor.primary" = { fg = "waveBlue1", bg = "seaFoam" } -"ui.cursor.match" = { fg = "seaFoam", modifiers = ["bold"] } +"ui.cursor" = { fg = "waveBlue1", bg = "waveAqua2"} +"ui.cursor.primary" = { fg = "waveBlue1", bg = "fujiWhite" } +"ui.cursor.match" = { fg = "waveRed", modifiers = ["bold"] } "ui.highlight" = { fg = "fujiWhite", bg = "waveBlue2" } -"ui.menu" = { fg = "fujiWhite", bg = "sumiInk0" } -"ui.menu.selected" = { fg = "fujiWhite", bg = "waveBlue1", modifiers = ["bold"] } +"ui.menu" = { fg = "fujiWhite", bg = "waveBlue1" } +"ui.menu.selected" = { fg = "fujiWhite", bg = "waveBlue2", modifiers = ["bold"] } +"ui.menu.scroll" = { fg = "oldWhite", bg = "waveBlue1" } "ui.cursorline.primary" = { bg = "sumiInk3"} +"ui.cursorcolumn.primary" = { bg = "sumiInk3" } "diagnostic.error" = { underline = { color = "samuraiRed", style = "curl" } } "diagnostic.warning" = { underline = { color = "roninYellow", style = "curl" } } @@ -58,12 +60,16 @@ hint = "dragonBlue" "diff.delta" = "autumnYellow" ## Syntax highlighting +"attribute" = "waveRed" "type" = "waveAqua2" +"constructor" = "springBlue" "constant" = "surimiOrange" "constant.numeric" = "sakuraPink" "constant.character.escape" = "springBlue" "string" = "springGreen" "string.regexp" = "boatYellow2" +"string.special.url" = "springBlue" +"string.special.symbol" = "oniViolet" "comment" = "fujiGray" "variable" = "fujiWhite" "variable.builtin" = "waveRed" @@ -71,37 +77,36 @@ hint = "dragonBlue" "variable.other.member" = "carpYellow" "label" = "springBlue" "punctuation" = "springViolet2" -"punctuation.delimiter" = "springViolet2" -"punctuation.bracket" = "springViolet2" "keyword" = "oniViolet" +"keyword.control.return" = "peachRed" +"keyword.control.exception" = "peachRed" "keyword.directive" = "peachRed" "operator" = "boatYellow2" "function" = "crystalBlue" -"function.builtin" = "peachRed" +"function.builtin" = "springBlue" "function.macro" = "waveRed" -"tag" = "springBlue" +"tag" = "waveAqua2" "namespace" = "surimiOrange" -"attribute" = "peachRed" -"constructor" = "springBlue" -"module" = "waveAqua2" "special" = "peachRed" ## Markup modifiers -"markup.heading.marker" = "fujiGray" +"markup.heading.marker" = "springViolet2" "markup.heading.1" = { fg = "surimiOrange", modifiers = ["bold"] } "markup.heading.2" = { fg = "carpYellow", modifiers = ["bold"] } "markup.heading.3" = { fg = "waveAqua2", modifiers = ["bold"] } -"markup.heading.4" = { fg = "springGreen", modifiers = ["bold"] } -"markup.heading.5" = { fg = "waveRed", modifiers = ["bold"] } -"markup.heading.6" = { fg = "autumnRed", modifiers = ["bold"] } -"markup.list" = "oniViolet" +"markup.heading.4" = { fg = "lightBlue", modifiers = ["bold"] } +"markup.heading.5" = { fg = "oniViolet", modifiers = ["bold"] } +"markup.heading.6" = { fg = "springViolet1", modifiers = ["bold"] } +"markup.list.numbered" = "sakuraPink" +"markup.list.unnumbered" = "waveRed" "markup.bold" = { modifiers = ["bold"] } "markup.italic" = { modifiers = ["italic"] } "markup.strikethrough" = { modifiers = ["crossed_out"] } -"markup.link.url" = { fg = "springBlue", modifiers = ["underlined"] } "markup.link.text" = "crystalBlue" -"markup.quote" = "seaFoam" -"markup.raw" = "seaFoam" +"markup.link.url" = { fg = "springBlue", underline.style = "line" } +"markup.link.label" = "surimiOrange" +"markup.quote" = "springViolet1" +"markup.raw" = "springGreen" [palette] seaFoam = "#C7CCD1" # custom lighter foreground From f02fdd2f7324121cd01b7cf51f462bce46819e4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 10:24:06 +0900 Subject: [PATCH 0023/1169] build(deps): bump tempfile from 3.3.0 to 3.4.0 (#6128) Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/Stebalien/tempfile/releases) - [Changelog](https://github.com/Stebalien/tempfile/blob/master/NEWS) - [Commits](https://github.com/Stebalien/tempfile/commits) --- updated-dependencies: - dependency-name: tempfile dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 69 ++++++++++++++++++++++++++++++++++--------- helix-term/Cargo.toml | 2 +- helix-vcs/Cargo.toml | 2 +- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f8302e28..cb3300a34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,27 @@ dependencies = [ "encoding_rs", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "error-code" version = "2.3.1" @@ -1414,6 +1435,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "io-lifetimes" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "itoa" version = "1.0.4" @@ -1460,6 +1491,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -1792,15 +1829,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "ropey" version = "1.6.0" @@ -1811,6 +1839,20 @@ dependencies = [ "str_indices", ] +[[package]] +name = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.9" @@ -2025,16 +2067,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys", ] [[package]] diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 2d4ba436e..4921db926 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -75,4 +75,4 @@ helix-loader = { version = "0.6", path = "../helix-loader" } [dev-dependencies] smallvec = "1.10" indoc = "2.0.0" -tempfile = "3.3.0" +tempfile = "3.4.0" diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index 19b660a60..c4d6eb45b 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -25,4 +25,4 @@ log = "0.4" git = ["git-repository"] [dev-dependencies] -tempfile = "3.3" \ No newline at end of file +tempfile = "3.4" \ No newline at end of file From 7b8daae3950e5c7f46f219cbd7d60107b4428f65 Mon Sep 17 00:00:00 2001 From: Isotoxal <62714538+IsotoxalDev@users.noreply.github.com> Date: Tue, 28 Feb 2023 08:12:58 +0530 Subject: [PATCH 0024/1169] theme: Add Everblush (#6086) --- runtime/themes/everblush.toml | 114 ++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 runtime/themes/everblush.toml diff --git a/runtime/themes/everblush.toml b/runtime/themes/everblush.toml new file mode 100644 index 000000000..edcdaf41b --- /dev/null +++ b/runtime/themes/everblush.toml @@ -0,0 +1,114 @@ +# Author: Isotoxal + +"attribute" = { fg = "blue" } +"comment" = { fg = "comment", modifiers = ["italic"] } +"constant" = { fg = "cyan" } +"constant.builtin.boolean" = { fg = "cyan" } +"constant.character" = { fg = "blue" } +"constant.numeric.float" = { fg = "black-light" } +"constant.builtin" = { fg = "blue" } +"constant.numeric" = { fg = "yellow" } +"constructor" = { fg = "blue" } +"function" = { fg = "red" } +"function.builtin" = { fg = "cyan-light" } +"function.macro" = { fg = "green" } +"function.method" = { fg = "blue-light" } +"keyword" = { fg = "blue" } +"keyword.function" = { fg = "blue" } +"keyword.operator" = { fg = "blue-light" } +"keyword.control.conditional" = { fg = "red" } +"keyword.control.import" = { fg = "red-light" } +"keyword.control.return" = { fg = "blue" } +"keyword.control.repeat" = { fg = "yellow-light" } +"keyword.control.exception" = { fg = "black-light" } +"label" = { fg = "blue" } +"namespace" = { fg = "red-light" } +"operator" = { fg = "white" } +#"parameter.reference" = { fg = "red-light" } +#"property" = { fg = "red" } +"punctuation.bracket" = { fg = "white" } +"punctuation.delimiter" = { fg = "white" } +"punctuation.special" = { fg = "white" } +"string" = { fg = "green" } +"string.escape" = { fg = "blue" } +"string.regex" = { fg = "green" } +"string.special" = { fg = "blue" } +"string.special.symbol" = { fg = "red" } +"tag" = { fg = "blue" } +"type" = { fg = "yellow" } +"type.builtin" = { fg = "yellow" } +"variable" = { fg = "white" } +"variable.builtin" = { fg = "blue" } +"variable.parameter" = { fg = "red" } +"variable.other.member" = { fg = "red" } + +"diff.plus" = { fg = "blue" } +"diff.delta" = { fg = "magenta" } +"diff.minus" = { fg = "red" } + +"ui.background" = { fg = "foreground", bg = "background" } +"ui.cursor" = { modifiers = ["reversed"] } +"ui.cursorline.primary" = { bg = "cursorline" } +"ui.help" = { fg = "foreground", bg = "contrast" } +"ui.linenr" = { fg = "comment" } +"ui.linenr.selected" = { fg = "foreground" } +"ui.menu" = { fg = "foreground", bg = "contrast" } +"ui.menu.selected" = { bg = "black" } +"ui.popup" = { fg = "foreground", bg = "contrast" } +"ui.selection" = { bg = "black" } +"ui.selection.primary" = { bg = "black" } +"ui.statusline" = { fg = "foreground", bg = "background" } +"ui.statusline.inactive" = { fg = "foreground", bg = "background" } +"ui.statusline.normal" = { fg = "white", bg = "background" } +"ui.statusline.insert" = { fg = "blue", bg = "background" } +"ui.statusline.select" = { fg = "cyan", bg = "magenta" } +"ui.text" = { fg = "foreground" } +"ui.text.focus" = { fg = "blue" } +"ui.virtual.ruler" = { bg = "cursorline" } +"ui.virtual.whitespace" = { fg = "comment" } +"ui.virtual.wrap" = { fg = "comment" } +"ui.virtual.indent-guide" = { fg = "comment" } +"ui.window" = { fg = "black" } + +"error" = { fg = "red" } +"hint" = { fg = "green" } +"warning" = { fg = "yellow" } +"info" = { fg = "blue" } +"diagnostic.error" = { underline = { style = "curl", color = "red" } } +"diagnostic.warning" = { underline = { style = "curl", color = "yellow" } } +"diagnostic.info" = { underline = { style = "curl", color = "blue" } } +"diagnostic.hint" = { underline = { style = "curl", color = "green" } } +"special" = { fg = "red-light" } + +"markup.heading" = { fg = "blue", modifiers = ["bold"] } +"markup.list" = { fg = "cyan" } +"markup.bold" = { fg = "magenta", modifiers = ["bold"] } +"markup.italic" = { fg = "yellow", modifiers = ["italic"] } +"markup.strikethrough" = { modifiers = ["crossed_out"] } +"markup.link.url" = { fg = "green" } +"markup.link.text" = { fg = "black-light" } +"markup.quote" = { fg = "yellow", modifiers = ["italic"] } +"markup.raw" = { fg = "cyan" } + +[palette] +black = "#232a2d" +red = "#e57474" +green = "#8ccf7e" +yellow = "#e5c76b" +blue = "#67b0e8" +magenta = "#c47fd5" +cyan = "#6cbfbf" +white = "#b3b9b8" +black-light = "#2d3437" +red-light = "#ef7e7e" +green-light = "#96d988" +yellow-light = "#f4d67a" +blue-light = "#71baf2" +magenta-light = "#ce89df" +cyan-light = "#67cbe7" +white-light = "#bdc3c2" +comment = "#404749" +contrast = "#161d1f" +background = "#141b1e" +foreground = "#dadada" +cursorline = "#2c3333" From 5ef3f5f59f37df04267fdebd64d9b51b5c4419a7 Mon Sep 17 00:00:00 2001 From: Mofiqul Date: Tue, 28 Feb 2023 08:14:46 +0530 Subject: [PATCH 0025/1169] theme: Add Adwaita Dark (#6042) --- runtime/themes/adwaita-dark.toml | 169 +++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 runtime/themes/adwaita-dark.toml diff --git a/runtime/themes/adwaita-dark.toml b/runtime/themes/adwaita-dark.toml new file mode 100644 index 000000000..f339e33c1 --- /dev/null +++ b/runtime/themes/adwaita-dark.toml @@ -0,0 +1,169 @@ +# Author: Mofiqul Islam + +"attribute" = "orange_4" + +"type" = "teal_2" +"type.builtin" = "teal_2" + +"constructor" = "blue_2" + +"constant" = "violet_2" +"constant.builtin" = { fg = "violet_2", modifiers = ["bold"] } +"constant.character" = "teal_3" +"constant.numeric" = { fg = "teal_3", modifiers = ["bold"] } +"constant.character.escape" = "violet_2" + +"string" = "teal_2" +"string.regexp" = "purple_2" +"string.special" = "blue_2" + +"comment" = "dark_2" + +"variable" = "light_4" +"variable.parameter" = "orange_2" +"variable.builtin" = "orange_2" +"variable.other" = "teal_2" +"variable.other.member" = "teal_2" + +"label" = "purple_2" + +"punctuation" = "light_4" +"punctuation.delimiter" = "light_4" +"punctuation.bracket" = "light_4" +"punctuation.special" = "red_3" + +"keyword" = { fg = "orange_2", modifiers = ["bold"] } +"keyword.control" = { fg = "orange_2", modifiers = ["bold"] } +"keyword.operator" = "purple_2" +"keyword.directive" = { fg = "orange_2", modifiers = ["bold"] } +"keyword.function" = "orange_2" +"keyword.storage" = { fg = "orange_2", modifiers = ["bold"] } + +"operator" = "purple_2" + +"function" = "blue_2" +"function.builtin" = "blue_2" +"function.macro" = { fg = "blue_2", modifiers = ["bold"] } +"function.special" = { fg = "blue_2", modifiers = ["bold"] } + +"tag" = "teal_2" + +"namespace" = "orange_2" + +"markup" = "light_4" +"markup.heading" = { fg = "teal_2", modifiers = ["bold"] } +"markup.list" = { fg = "orange_2", modifiers = ["bold"] } +"markup.bold" = { fg = "light_4", modifiers = ["bold"] } +"markup.italic" = { fg = "light_4", modifiers = ["italic"] } +"markup.link" = { fg = "blue_3", modifiers = ["underlined"] } +"markup.quote" = { fg = "light_3", modifiers = ["italic"] } +"diff.plus" = "teal_3" +"diff.minus" = "red_1" +"diff.delta" = "orange_3" +"diff.delta.moved" = "orange_2" + +"ui.background" = { fg = "light_4", bg = "libadwaita_dark" } +"ui.background.separator" = { fg = "split_and_borders", bg = "libadwaita_dark" } +"ui.cursor" = { fg = "libadwaita_dark", bg = "light_5" } +"ui.cursor.insert" = { fg = "libadwaita_dark", bg = "light_5" } +"ui.cursor.select" = { fg = "libadwaita_dark", bg = "light_5" } +"ui.cursor.match" = { fg = "libadwaita_dark", bg = "blue_2" } +"ui.cursor.primary" = { fg = "libadwaita_dark", bg = "light_7" } +"ui.linenr" = "dark_2" +"ui.linenr.selected" = { fg = "light_7", bg = "libadwaita_dark_alt", modifiers = [ + "bold", +] } +"ui.statusline" = { fg = "light_4", bg = "libadwaita_dark_alt" } +"ui.statusline.inactive" = { fg = "light_4", bg = "libadwaita_dark_alt" } +"ui.statusline.insert" = { fg = "light_4", bg = "teal_4" } +"ui.statusline.select" = { fg = "light_4", bg = "blue_4" } +"ui.popup" = { bg = "libadwaita_popup" } +"ui.window" = "split_and_borders" +"ui.help" = { bg = "libadwaita_dark_alt" } +"ui.text" = "light_4" +"ui.virtual" = "dark_1" +"ui.menu" = { fg = "light_4", bg = "libadwaita_popup" } +"ui.menu.selected" = { fg = "light_4", bg = "blue_5" } +"ui.menu.scroll" = { fg = "light_7", bg = "dark_3" } +"ui.selection" = { bg = "blue_7" } +"ui.selection.primary" = { bg = "blue_7" } +"ui.cursorline.primary" = { bg = "libadwaita_dark_alt" } + +"warning" = "yellow_2" +"error" = "red_4" +"info" = "purple_2" +"hint" = "blue_2" + +"diagnostic.hint" = { fg = "blue_2", modifiers = ["dim"] } +"diagnostic.info" = { fg = "purple_2", modifiers = ["dim"] } +"diagnostic.error" = { fg = "red_4", modifiers = ["underlined"] } +"diagnostic.warning" = { fg = "yellow_2", modifiers = ["underlined"] } + +[palette] +blue_1 = "#99C1F1" +blue_2 = "#62A0EA" +blue_3 = "#3584E4" +blue_4 = "#1C71D8" +blue_5 = "#1A5FB4" +blue_6 = "#1B497E" +blue_7 = "#193D66" +brown_1 = "#CDAB8F" +brown_2 = "#B5835A" +brown_3 = "#986A44" +brown_4 = "#865E3C" +brown_5 = "#63452C" +chameleon_3 = "#4E9A06" +dark_1 = "#77767B" +dark_2 = "#5E5C64" +dark_3 = "#504E55" +dark_4 = "#3D3846" +dark_5 = "#241F31" +dark_6 = "#000000" +dark_7 = "#1c1c1c" +green_1 = "#8FF0A4" +green_2 = "#57E389" +green_3 = "#33D17A" +green_4 = "#2EC27E" +green_5 = "#26A269" +green_6 = "#1F7F56" +green_7 = "#1C6849" +libadwaita_dark = "#1D1D1D" +libadwaita_dark_alt = "#303030" +libadwaita_popup = "#282828" +light_1 = "#FFFFFF" +light_2 = "#FCFCFC" +light_3 = "#F6F5F4" +light_4 = "#DEDDDA" +light_5 = "#C0BFBC" +light_6 = "#B0AFAC" +light_7 = "#9A9996" +orange_1 = "#FFBE6F" +orange_2 = "#FFA348" +orange_3 = "#FF7800" +orange_4 = "#E66100" +orange_5 = "#C64600" +purple_1 = "#DC8ADD" +purple_2 = "#C061CB" +purple_3 = "#9141AC" +purple_4 = "#813D9C" +purple_5 = "#613583" +red_1 = "#F66151" +red_2 = "#ED333B" +red_3 = "#E01B24" +red_4 = "#C01C28" +red_5 = "#A51D2D" +teal_1 = "#93DDC2" +teal_2 = "#5BC8AF" +teal_3 = "#33B2A4" +teal_4 = "#26A1A2" +teal_5 = "#218787" +violet_2 = "#7D8AC7" +violet_3 = "#6362C8" +violet_4 = "#4E57BA" +yellow_1 = "#F9F06B" +yellow_2 = "#F8E45C" +yellow_3 = "#F6D32D" +yellow_4 = "#F5C211" +yellow_5 = "#E5A50A" +yellow_6 = "#D38B09" +split_and_borders = "#4F4F4F" From a976786a4fa013150ec1b59ae55276361e61340f Mon Sep 17 00:00:00 2001 From: Mathieu Agopian Date: Tue, 28 Feb 2023 04:03:56 +0100 Subject: [PATCH 0026/1169] book: Document h and g (#6124) --- book/src/keymap.md | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/book/src/keymap.md b/book/src/keymap.md index 8864ab696..87867024b 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -268,28 +268,30 @@ Accessed by typing `Space` in [normal mode](#normal-mode). This layer is a kludge of mappings, mostly pickers. -| Key | Description | Command | -| ----- | ----------- | ------- | -| `f` | Open file picker | `file_picker` | -| `F` | Open file picker at current working directory | `file_picker_in_current_directory` | -| `b` | Open buffer picker | `buffer_picker` | -| `j` | Open jumplist picker | `jumplist_picker` | -| `k` | Show documentation for item under cursor in a [popup](#popup) (**LSP**) | `hover` | -| `s` | Open document symbol picker (**LSP**) | `symbol_picker` | -| `S` | Open workspace symbol picker (**LSP**) | `workspace_symbol_picker` | -| `d` | Open document diagnostics picker (**LSP**) | `diagnostics_picker` | -| `D` | Open workspace diagnostics picker (**LSP**) | `workspace_diagnostics_picker` | -| `r` | Rename symbol (**LSP**) | `rename_symbol` | -| `a` | Apply code action (**LSP**) | `code_action` | -| `'` | Open last fuzzy picker | `last_picker` | -| `w` | Enter [window mode](#window-mode) | N/A | -| `p` | Paste system clipboard after selections | `paste_clipboard_after` | -| `P` | Paste system clipboard before selections | `paste_clipboard_before` | -| `y` | Join and yank selections to clipboard | `yank_joined_to_clipboard` | -| `Y` | Yank main selection to clipboard | `yank_main_selection_to_clipboard` | -| `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` | -| `/` | Global search in workspace folder | `global_search` | -| `?` | Open command palette | `command_palette` | +| Key | Description | Command | +| ----- | ----------- | ------- | +| `f` | Open file picker | `file_picker` | +| `F` | Open file picker at current working directory | `file_picker_in_current_directory` | +| `b` | Open buffer picker | `buffer_picker` | +| `j` | Open jumplist picker | `jumplist_picker` | +| `g` | Debug (experimental) | N/A | +| `k` | Show documentation for item under cursor in a [popup](#popup) (**LSP**) | `hover` | +| `s` | Open document symbol picker (**LSP**) | `symbol_picker` | +| `S` | Open workspace symbol picker (**LSP**) | `workspace_symbol_picker` | +| `d` | Open document diagnostics picker (**LSP**) | `diagnostics_picker` | +| `D` | Open workspace diagnostics picker (**LSP**) | `workspace_diagnostics_picker` | +| `r` | Rename symbol (**LSP**) | `rename_symbol` | +| `a` | Apply code action (**LSP**) | `code_action` | +| `h` | Select symbol references (**LSP**) | `select_references_to_symbol_under_cursor` | +| `'` | Open last fuzzy picker | `last_picker` | +| `w` | Enter [window mode](#window-mode) | N/A | +| `p` | Paste system clipboard after selections | `paste_clipboard_after` | +| `P` | Paste system clipboard before selections | `paste_clipboard_before` | +| `y` | Join and yank selections to clipboard | `yank_joined_to_clipboard` | +| `Y` | Yank main selection to clipboard | `yank_main_selection_to_clipboard` | +| `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` | +| `/` | Global search in workspace folder | `global_search` | +| `?` | Open command palette | `command_palette` | > TIP: Global search displays results in a fuzzy picker, use `Space + '` to bring it back up after opening a file. From 79bf5e3094e16a34637703b14c7bf090d2dcf155 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 10 Jan 2023 20:48:26 -0600 Subject: [PATCH 0027/1169] Update crossterm to 0.26.1 Crossterm 0.26.x includes a breaking change for the command to set the cursor shape. This commit includes a change which uses the new type. --- Cargo.lock | 4 ++-- helix-term/Cargo.toml | 2 +- helix-tui/Cargo.toml | 2 +- helix-tui/src/backend/crossterm.rs | 10 +++++----- helix-view/Cargo.toml | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb3300a34..c74e8e40c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,9 +261,9 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" dependencies = [ "bitflags", "crossterm_winapi", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 4921db926..975d08712 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -37,7 +37,7 @@ which = "4.4" tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] } -crossterm = { version = "0.25", features = ["event-stream"] } +crossterm = { version = "0.26", features = ["event-stream"] } signal-hook = "0.3" tokio-stream = "0.1" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index a4a1c389f..ccd016f50 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -19,7 +19,7 @@ default = ["crossterm"] bitflags = "1.3" cassowary = "0.3" unicode-segmentation = "1.10" -crossterm = { version = "0.25", optional = true } +crossterm = { version = "0.26", optional = true } termini = "0.1" serde = { version = "1", "optional" = true, features = ["derive"]} helix-view = { version = "0.6", path = "../helix-view", features = ["term"] } diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs index c00e1f406..5305640cb 100644 --- a/helix-tui/src/backend/crossterm.rs +++ b/helix-tui/src/backend/crossterm.rs @@ -1,6 +1,6 @@ use crate::{backend::Backend, buffer::Cell}; use crossterm::{ - cursor::{CursorShape, Hide, MoveTo, SetCursorShape, Show}, + cursor::{Hide, MoveTo, SetCursorStyle, Show}, execute, queue, style::{ Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor, @@ -156,12 +156,12 @@ where fn show_cursor(&mut self, kind: CursorKind) -> io::Result<()> { let shape = match kind { - CursorKind::Block => CursorShape::Block, - CursorKind::Bar => CursorShape::Line, - CursorKind::Underline => CursorShape::UnderScore, + CursorKind::Block => SetCursorStyle::SteadyBlock, + CursorKind::Bar => SetCursorStyle::SteadyBar, + CursorKind::Underline => SetCursorStyle::SteadyUnderScore, CursorKind::Hidden => unreachable!(), }; - map_error(execute!(self.buffer, Show, SetCursorShape(shape))) + map_error(execute!(self.buffer, Show, shape)) } fn get_cursor(&mut self) -> io::Result<(u16, u16)> { diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 17e07e9a2..54b679ade 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -20,7 +20,7 @@ helix-core = { version = "0.6", path = "../helix-core" } helix-loader = { version = "0.6", path = "../helix-loader" } helix-lsp = { version = "0.6", path = "../helix-lsp" } helix-dap = { version = "0.6", path = "../helix-dap" } -crossterm = { version = "0.25", optional = true } +crossterm = { version = "0.26", optional = true } helix-vcs = { version = "0.6", path = "../helix-vcs" } # Conversion traits From a066815833b322233dc11aeae38679bc8466ec56 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sat, 26 Nov 2022 13:04:27 -0600 Subject: [PATCH 0028/1169] Enable the enhanced keyboard protocol if supported --- helix-term/src/application.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index c8e8ecb1a..d78964194 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -40,7 +40,8 @@ use anyhow::{Context, Error}; use crossterm::{ event::{ DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, - EnableFocusChange, EnableMouseCapture, Event as CrosstermEvent, + EnableFocusChange, EnableMouseCapture, Event as CrosstermEvent, KeyboardEnhancementFlags, + PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, }, execute, terminal, tty::IsTty, @@ -111,6 +112,9 @@ fn restore_term() -> Result<(), Error> { let mut stdout = stdout(); // reset cursor shape write!(stdout, "\x1B[0 q")?; + if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { + execute!(stdout, PopKeyboardEnhancementFlags)?; + } // Ignore errors on disabling, this might trigger on windows if we call // disable without calling enable previously let _ = execute!(stdout, DisableMouseCapture); @@ -1062,6 +1066,19 @@ impl Application { if self.config.load().editor.mouse { execute!(stdout, EnableMouseCapture)?; } + if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { + log::debug!("The enhanced keyboard protocol is supported on this terminal"); + execute!( + stdout, + PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS + ) + )?; + } else { + log::debug!("The enhanced keyboard protocol is not supported on this terminal"); + } + Ok(()) } From 8dab8a0a039fe1f3dd98fc62ac97d2f1c089793a Mon Sep 17 00:00:00 2001 From: lesleyrs <19632758+lesleyrs@users.noreply.github.com> Date: Tue, 29 Nov 2022 23:31:18 +0100 Subject: [PATCH 0029/1169] Add shift-backspace keybind alias for backspace (#4937) When the Kitty Keyboard Protocol is enabled, S-backspace is distinguished from backspace with no modifiers. This is awkward when typing because it's very easy to accidentally hold shift and press backspace temporarily when typing capital letters. Kakoune (which is also a Kitty Keyboard Protocol application) treats S-backspace as backspace too: https://github.com/mawww/kakoune/blob/3150e9b3cd8e61d9bc68245d67822614d4376cf4/src/input_handler.cc#L1275 --- book/src/keymap.md | 4 ++-- helix-term/src/keymap/default.rs | 2 +- helix-term/src/ui/prompt.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/book/src/keymap.md b/book/src/keymap.md index 87867024b..bc16aa1a7 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -352,7 +352,7 @@ experience. | `Alt-d`, `Alt-Delete` | Delete next word | `delete_word_forward` | | `Ctrl-u` | Delete to start of line | `kill_to_line_start` | | `Ctrl-k` | Delete to end of line | `kill_to_line_end` | -| `Ctrl-h`, `Backspace` | Delete previous char | `delete_char_backward` | +| `Ctrl-h`, `Backspace`, `Shift-Backspace` | Delete previous char | `delete_char_backward` | | `Ctrl-d`, `Delete` | Delete next char | `delete_char_forward` | | `Ctrl-j`, `Enter` | Insert new line | `insert_newline` | @@ -433,7 +433,7 @@ Keys to use within prompt, Remapping currently not supported. | `Alt-d`, `Alt-Delete`, `Ctrl-Delete` | Delete next word | | `Ctrl-u` | Delete to start of line | | `Ctrl-k` | Delete to end of line | -| `Backspace`, `Ctrl-h` | Delete previous char | +| `Backspace`, `Ctrl-h`, `Shift-Backspace` | Delete previous char | | `Delete`, `Ctrl-d` | Delete next char | | `Ctrl-s` | Insert a word under doc cursor, may be changed to Ctrl-r Ctrl-w later | | `Ctrl-p`, `Up` | Select previous history | diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 01184f80e..7425c8155 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -363,7 +363,7 @@ pub fn default() -> HashMap { "A-d" | "A-del" => delete_word_forward, "C-u" => kill_to_line_start, "C-k" => kill_to_line_end, - "C-h" | "backspace" => delete_char_backward, + "C-h" | "backspace" | "S-backspace" => delete_char_backward, "C-d" | "del" => delete_char_forward, "C-j" | "ret" => insert_newline, "tab" => insert_tab, diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index f438231fa..35ae8c2a8 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -516,7 +516,7 @@ impl Component for Prompt { alt!('d') | alt!(Delete) | ctrl!(Delete) => self.delete_word_forwards(cx.editor), ctrl!('k') => self.kill_to_end_of_line(cx.editor), ctrl!('u') => self.kill_to_start_of_line(cx.editor), - ctrl!('h') | key!(Backspace) => { + ctrl!('h') | key!(Backspace) | shift!(Backspace) => { self.delete_char_backwards(cx.editor); (self.callback_fn)(cx, &self.line, PromptEvent::Update); } From 27211abf0688794f9bf7a395a8c47846b4f7fd41 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 28 Feb 2023 19:26:02 -0600 Subject: [PATCH 0030/1169] Ignore key-release keyboard events (#6139) Since crossterm 0.26.x, we receive press/release keyboard events on Windows always. We can ignore the release events though to emulate the behavior of keyboard input on Windows on crossterm 0.25.x. --- helix-term/src/application.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index d78964194..ee2a438c0 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -638,6 +638,11 @@ impl Application { self.compositor .handle_event(&Event::Resize(width, height), &mut cx) } + // Ignore keyboard release events. + CrosstermEvent::Key(crossterm::event::KeyEvent { + kind: crossterm::event::KeyEventKind::Release, + .. + }) => false, event => self.compositor.handle_event(&event.into(), &mut cx), }; From c082ef28632e8a92a91926f3714808942238098b Mon Sep 17 00:00:00 2001 From: NomisIV <47303199+NomisIV@users.noreply.github.com> Date: Wed, 1 Mar 2023 07:45:27 +0100 Subject: [PATCH 0031/1169] Fix indentation lines (#6134) (#6136) --- helix-term/src/ui/document.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/helix-term/src/ui/document.rs b/helix-term/src/ui/document.rs index ed4b1de90..4f615f7bd 100644 --- a/helix-term/src/ui/document.rs +++ b/helix-term/src/ui/document.rs @@ -322,7 +322,7 @@ pub struct TextRenderer<'a> { pub nbsp: String, pub space: String, pub tab: String, - pub tab_width: u16, + pub indent_width: u16, pub starting_indent: usize, pub draw_indent_guides: bool, pub col_offset: usize, @@ -370,16 +370,19 @@ impl<'a> TextRenderer<'a> { let text_style = theme.get("ui.text"); + let indent_width = doc.indent_style.indent_width(tab_width) as u16; + TextRenderer { surface, indent_guide_char: editor_config.indent_guides.character.into(), newline, nbsp, space, - tab_width: tab_width as u16, tab, whitespace_style: theme.get("ui.virtual.whitespace"), - starting_indent: (col_offset / tab_width) + indent_width, + starting_indent: col_offset / indent_width as usize + + (col_offset % indent_width as usize != 0) as usize + editor_config.indent_guides.skip_levels as usize, indent_guide_style: text_style.patch( theme @@ -461,14 +464,14 @@ impl<'a> TextRenderer<'a> { // Don't draw indent guides outside of view let end_indent = min( indent_level, - // Add tab_width - 1 to round up, since the first visible + // Add indent_width - 1 to round up, since the first visible // indent might be a bit after offset.col - self.col_offset + self.viewport.width as usize + (self.tab_width - 1) as usize, - ) / self.tab_width as usize; + self.col_offset + self.viewport.width as usize + (self.indent_width as usize - 1), + ) / self.indent_width as usize; for i in self.starting_indent..end_indent { - let x = - (self.viewport.x as usize + (i * self.tab_width as usize) - self.col_offset) as u16; + let x = (self.viewport.x as usize + (i * self.indent_width as usize) - self.col_offset) + as u16; let y = self.viewport.y + row; debug_assert!(self.surface.in_bounds(x, y)); self.surface From 0625f410ebdab22e54af10f934de41bdba911069 Mon Sep 17 00:00:00 2001 From: Andrey Grebenyk <35263631+x318@users.noreply.github.com> Date: Thu, 2 Mar 2023 18:59:16 +0300 Subject: [PATCH 0032/1169] Add graphql schema file type (#6159) Co-authored-by: Andrey Grebenyk --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index d4ea9a867..006db633c 100644 --- a/languages.toml +++ b/languages.toml @@ -1190,7 +1190,7 @@ source = { git = "https://github.com/shunsambongi/tree-sitter-gitignore", rev = name = "graphql" scope = "source.graphql" injection-regex = "graphql" -file-types = ["gql", "graphql"] +file-types = ["gql", "graphql", "graphqls"] roots = [] indent = { tab-width = 2, unit = " " } From 6e7dcb33170e50088de6734a046480917a386049 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 2 Mar 2023 17:01:47 -0600 Subject: [PATCH 0033/1169] CI: Update cachix/install-nix-action to v20 (#6163) This fixes an issue with installing Nix 1.14 which causes the cachix/cachix-action in the next step to fail. --- .github/workflows/cachix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml index bcdea3197..7d2f734aa 100644 --- a/.github/workflows/cachix.yml +++ b/.github/workflows/cachix.yml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v3 - name: Install nix - uses: cachix/install-nix-action@v19 + uses: cachix/install-nix-action@v20 - name: Authenticate with Cachix uses: cachix/cachix-action@v12 From ddc5bf4e606230dd8bc20c59b1cc114e93bbf1c0 Mon Sep 17 00:00:00 2001 From: nuid32 <91177333+nuid32@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:50:26 +0500 Subject: [PATCH 0034/1169] Fix 'attempt to divide by zero' panic (#6155) --- helix-term/src/ui/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-term/src/ui/text.rs b/helix-term/src/ui/text.rs index c318052b2..a379536f8 100644 --- a/helix-term/src/ui/text.rs +++ b/helix-term/src/ui/text.rs @@ -58,7 +58,7 @@ pub fn required_size(text: &tui::text::Text, max_text_width: u16) -> (u16, u16) let content_width = content.width() as u16; if content_width > max_text_width { text_width = max_text_width; - height += content_width / max_text_width; + height += content_width.checked_div(max_text_width).unwrap_or(0); } else if content_width > text_width { text_width = content_width; } From 2d5577dbe6b353bd266154ab693ffedc521afee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Donk=C3=B3?= Date: Fri, 3 Mar 2023 17:03:03 +0100 Subject: [PATCH 0035/1169] Extend the set of tags highlighted in comments (#6143) --- runtime/queries/comment/highlights.scm | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/runtime/queries/comment/highlights.scm b/runtime/queries/comment/highlights.scm index 88685d59a..3b25531a8 100644 --- a/runtime/queries/comment/highlights.scm +++ b/runtime/queries/comment/highlights.scm @@ -5,17 +5,33 @@ ":" @punctuation.delimiter +; Hint level tags +((tag (name) @hint) + (#match? @hint "^(HINT|MARK)$")) + +("text" @hint + (#match? @hint "^(HINT|MARK)$")) + +; Info level tags +((tag (name) @info) + (#match? @info "^(INFO|NOTE|TODO)$")) + +("text" @info + (#match? @info "^(INFO|NOTE|TODO)$")) + +; Warning level tags ((tag (name) @warning) - (#match? @warning "^(TODO|HACK|WARNING)$")) + (#match? @warning "^(HACK|WARN|WARNING)$")) ("text" @warning - (#match? @warning "^(TODO|HACK|WARNING)$")) + (#match? @warning "^(HACK|WARN|WARNING)$")) +; Error level tags ((tag (name) @error) - (match? @error "^(FIXME|XXX|BUG)$")) + (match? @error "^(BUG|FIXME|ISSUE|XXX)$")) ("text" @error - (match? @error "^(FIXME|XXX|BUG)$")) + (match? @error "^(BUG|FIXME|ISSUE|XXX)$")) (tag (name) @ui.text From 5c716af7a2c2fff36080d51be3cb9fa30aa36bc7 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Fri, 3 Mar 2023 17:05:40 +0100 Subject: [PATCH 0036/1169] Fix scrolloff at view bottom (#6142) Fixes a regression introduced in #5420 where a scrolloff of `x - 1` was used instead if `x` at the bottom of the screen. This was especially problematic if the scrolloff was set to `0` in that case the scrolloff behaved as tough set to `-1` and the cursor disappeared from the view if scrolled to the botoom. --- helix-view/src/view.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index f793cbe36..7bfbb2418 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -242,7 +242,7 @@ impl View { at_top = true; true } - Some((visual_pos, _)) if visual_pos.row >= vertical_viewport_end - scrolloff => { + Some((visual_pos, _)) if visual_pos.row + scrolloff + 1 >= vertical_viewport_end => { if CENTERING && visual_pos.row >= vertical_viewport_end { // cursor out of view return None; @@ -257,7 +257,7 @@ impl View { let v_off = if at_top { scrolloff as isize } else { - viewport.height as isize - scrolloff as isize + viewport.height as isize - scrolloff as isize - 1 }; (offset.anchor, offset.vertical_offset) = char_idx_at_visual_offset(doc_text, cursor, -v_off, 0, &text_fmt, &annotations); From 2bd8bc8d8484a4a8852280f16888a819acbfde7a Mon Sep 17 00:00:00 2001 From: Matthias Q <35303817+matthias-Q@users.noreply.github.com> Date: Fri, 3 Mar 2023 20:12:37 +0100 Subject: [PATCH 0037/1169] feat(prql): add prql support (#6126) --- book/src/generated/lang-support.md | 1 + languages.toml | 13 +++ runtime/queries/prql/highlights.scm | 136 ++++++++++++++++++++++++++++ runtime/queries/prql/injections.scm | 8 ++ 4 files changed, 158 insertions(+) create mode 100644 runtime/queries/prql/highlights.scm create mode 100644 runtime/queries/prql/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index e997b3e81..cf8e50236 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -104,6 +104,7 @@ | prisma | ✓ | | | `prisma-language-server` | | prolog | | | | `swipl` | | protobuf | ✓ | | ✓ | | +| prql | ✓ | | | | | purescript | ✓ | | | `purescript-language-server` | | python | ✓ | ✓ | ✓ | `pylsp` | | qml | ✓ | | ✓ | `qmlls` | diff --git a/languages.toml b/languages.toml index 006db633c..b0a34be0d 100644 --- a/languages.toml +++ b/languages.toml @@ -2208,6 +2208,19 @@ indent = { tab-width = 2, unit = " " } name = "yuck" source = { git = "https://github.com/Philipp-M/tree-sitter-yuck", rev = "9e97da5773f82123a8c8cccf8f7e795d140ed7d1" } +[[language]] +name = "prql" +scope = "source.prql" +injection-regex = "prql" +file-types = ["prql"] +roots = [] +comment-token = "#" +indent = { tab-width = 4, unit = " " } + +[[grammar]] +name = "prql" +source = { git = "https://github.com/PRQL/tree-sitter-prql", rev = "3f27cac466f030ee7d985d91eba5470e01dd21ea" } + [[language]] name = "po" scope = "source.po" diff --git a/runtime/queries/prql/highlights.scm b/runtime/queries/prql/highlights.scm new file mode 100644 index 000000000..5cfedee48 --- /dev/null +++ b/runtime/queries/prql/highlights.scm @@ -0,0 +1,136 @@ +[ + (keyword_from) + (keyword_filter) + (keyword_derive) + (keyword_group) + (keyword_aggregate) + (keyword_sort) + (keyword_take) + (keyword_window) + (keyword_join) + (keyword_select) + (keyword_switch) + (keyword_append) + (keyword_remove) + (keyword_intersect) + (keyword_rolling) + (keyword_rows) + (keyword_expanding) + (keyword_let) + (keyword_prql) + (keyword_from_text) +] @keyword + +(literal) @string + +(assignment + alias: (field) @variable.other.member) + +alias: (identifier) @variable.other.member + +(f_string) @string.special +(s_string) @string.special + +(comment) @comment + +(keyword_func) @keyword.function + +(function_call + (identifier) @function) + +[ + "+" + "-" + "*" + "/" + "=" + "==" + "<" + "<=" + "!=" + ">=" + ">" + "->" + (bang) +] @operator + +[ + "(" + ")" + "[" + "]" +] @punctuation.bracket + +[ + "," + "." + (pipe) +] @punctuation.delimiter + +(literal + (integer) @constant.numeric.integer) + +(integer) @constant.numeric.integer + +(literal + (decimal_number) @constant.numeric.float) + +(decimal_number) @constant.numeric.float + +[ + (keyword_min) + (keyword_max) + (keyword_count) + (keyword_count_distinct) + (keyword_average) + (keyword_avg) + (keyword_sum) + (keyword_stddev) + (keyword_count) +] @function + +[ + (keyword_side) + (keyword_version) + (keyword_target) + (keyword_null) + (keyword_format) +] @attribute + +(target) @function.builtin + + [ + (date) + (time) + (timestamp) +] @string.special + +[ + (keyword_left) + (keyword_inner) + (keyword_right) + (keyword_full) + (keyword_csv) + (keyword_json) +] @function.method + +[ + (keyword_true) + (keyword_false) +] @constant.builtin.boolean + +[ + (keyword_and) + (keyword_or) +] @keyword.operator + +(function_definition + (keyword_func) + name: (identifier) @function) + +(parameter + (identifier) @variable.parameter) + +(variable + (keyword_let) + name: (identifier) @constant) diff --git a/runtime/queries/prql/injections.scm b/runtime/queries/prql/injections.scm new file mode 100644 index 000000000..02a8919f8 --- /dev/null +++ b/runtime/queries/prql/injections.scm @@ -0,0 +1,8 @@ +((s_string) @injection.content + (#set! injection.language "sql")) + +(from_text + (keyword_from_text) + (keyword_json) + (literal) @injection.content + (#set! injection.language "json")) From bf872366fdc9d31383cc84a35bd8d30b1a9b9da6 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 4 Mar 2023 23:04:17 +0530 Subject: [PATCH 0038/1169] Document the file-modification-indicator statusline element (#6036) --- book/src/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/book/src/configuration.md b/book/src/configuration.md index 7514a3d0f..0b9ebe966 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -98,6 +98,7 @@ The following statusline elements can be configured: | `spinner` | A progress spinner indicating LSP activity | | `file-name` | The path/name of the opened file | | `file-base-name` | The basename of the opened file | +| `file-modification-indicator` | The indicator to show whether the file is modified (a `[+]` appears when there are unsaved changes) | | `file-encoding` | The encoding of the opened file if it differs from UTF-8 | | `file-line-ending` | The file line endings (CRLF or LF) | | `total-line-numbers` | The total line numbers of the opened file | From a2e54167d8f9ad8764ba586488d3995aeb33a559 Mon Sep 17 00:00:00 2001 From: Alexander Brevig Date: Sun, 5 Mar 2023 02:52:20 +0100 Subject: [PATCH 0039/1169] fix: Handle signals before crossterm events (#6170) This is a workaround for a freeze when suspending Helix with C-z on non-Windows systems. The check for the keyboard enhancement protocol locks up crossterm's internal event reading/polling system by trying to set up multiple concurrent readers. `input_stream.next()` sets up one reader looking for regular crossterm events while the `supports_keyboard_enhancement` query sets up another looking for internal events. The latter hangs for two seconds or until the former yields an event. By handling signals first we don't lock up the mutex by trying to read keyboard events. --- helix-term/src/application.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index ee2a438c0..df6d9da6c 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -345,12 +345,12 @@ impl Application { tokio::select! { biased; - Some(event) = input_stream.next() => { - self.handle_terminal_events(event).await; - } Some(signal) = self.signals.next() => { self.handle_signals(signal).await; } + Some(event) = input_stream.next() => { + self.handle_terminal_events(event).await; + } Some(callback) = self.jobs.futures.next() => { self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); self.render().await; From cf153080d7712f9a03e5e2c30bbae10eade1dc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 5 Mar 2023 03:21:31 +0100 Subject: [PATCH 0040/1169] Theme: Papercolor: Add ui.highlight (#6162) Using the picker with syntax highlighting the fallback `ui.selection` makes a lot of text, especially for the light variant, hard to read. Instead, use a lighter background for highlights --- runtime/themes/papercolor-dark.toml | 1 + runtime/themes/papercolor-light.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/runtime/themes/papercolor-dark.toml b/runtime/themes/papercolor-dark.toml index 088658e9e..eaaa36dcf 100644 --- a/runtime/themes/papercolor-dark.toml +++ b/runtime/themes/papercolor-dark.toml @@ -7,6 +7,7 @@ "ui.text.focus" = { fg = "selection_background", modifiers = ["bold"]} "ui.selection" = {bg="selection_background", fg="selection_foreground"} "ui.cursorline" = {bg="cursorline_background"} +"ui.highlight" = {bg="cursorline_background"} "ui.statusline" = {bg="paper_bar_bg", fg="regular0"} "ui.statusline.select" = {bg="background", fg="bright7"} "ui.statusline.normal" = {bg="background", fg="bright3"} diff --git a/runtime/themes/papercolor-light.toml b/runtime/themes/papercolor-light.toml index c44c67091..63671e1b3 100644 --- a/runtime/themes/papercolor-light.toml +++ b/runtime/themes/papercolor-light.toml @@ -6,6 +6,7 @@ "ui.text" = "foreground" "ui.text.focus" = { fg = "selection_background", modifiers = ["bold"]} "ui.selection" = {bg="selection_background", fg="selection_foreground"} +"ui.highlight" = {bg="cursorline_background"} "ui.cursorline" = {bg="cursorline_background"} "ui.statusline" = {bg="paper_bar_bg", fg="regular0"} "ui.statusline.select" = {bg="background", fg="bright7"} From 725d9aecf08262e83553e54aa57d9bbec4841c80 Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Sun, 5 Mar 2023 03:36:01 +0100 Subject: [PATCH 0041/1169] Add support for reStructuredText (#6180) --- book/src/generated/lang-support.md | 1 + languages.toml | 11 +++++++++ runtime/queries/rst/highlights.scm | 38 ++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 runtime/queries/rst/highlights.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index cf8e50236..48cb66f1f 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -114,6 +114,7 @@ | rescript | ✓ | ✓ | | `rescript-language-server` | | rmarkdown | ✓ | | ✓ | `R` | | ron | ✓ | | ✓ | | +| rst | ✓ | | | | | ruby | ✓ | ✓ | ✓ | `solargraph` | | rust | ✓ | ✓ | ✓ | `rust-analyzer` | | sage | ✓ | ✓ | | | diff --git a/languages.toml b/languages.toml index b0a34be0d..e9a77b5a4 100644 --- a/languages.toml +++ b/languages.toml @@ -2244,3 +2244,14 @@ indent = { tab-width = 8, unit = " " } [[grammar]] name = "nasm" source = { git = "https://github.com/naclsn/tree-sitter-nasm", rev = "a0db15db6fcfb1bf2cc8702500e55e558825c48b" } + +[[language]] +name = "rst" +scope = "source.rst" +comment-token = ".." +file-types = ["rst"] +roots = [] + +[[grammar]] +name = "rst" +source = { git = "https://github.com/stsewd/tree-sitter-rst", rev = "25e6328872ac3a764ba8b926aea12719741103f1" } diff --git a/runtime/queries/rst/highlights.scm b/runtime/queries/rst/highlights.scm new file mode 100644 index 000000000..73c0def43 --- /dev/null +++ b/runtime/queries/rst/highlights.scm @@ -0,0 +1,38 @@ +(comment) @comment + +[ + (title) +] @markup.heading.1 + +[ + "adornment" +] @markup.heading.marker + +[ + (target) + (reference) +] @markup.link.url + +[ + "bullet" +] @markup.list.unnumbered + +(strong) @markup.bold +(emphasis) @markup.italic +(literal) @markup.raw.inline + +(list_item + (term) @markup.bold + (classifier)? @markup.italic) + +(directive + [".." (type) "::"] @function +) + +(field + [":" (field_name) ":"] @variable.other.member +) + +(interpreted_text) @markup.raw.inline + +(interpreted_text (role)) @keyword From ac9e0b39f2d217f1c40a7e536e15009b21423610 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 20 Feb 2023 16:48:13 +0100 Subject: [PATCH 0042/1169] upgrade `git-repository` to `gix` 0.36.1; up min. rustc version to 1.64 This fixes breakage when installing `helix` due to an incorrect usage of `as_ref()` when interacting with `bstr` in the `gitoxide` codebase. However, this upgrade also requires a higher rustc version, as `gitoxide` recently updated its `windows` crate version. --- Cargo.lock | 575 ++++++++++++++++++++----------------------- helix-vcs/Cargo.toml | 4 +- helix-vcs/src/git.rs | 21 +- rust-toolchain.toml | 2 +- 4 files changed, 283 insertions(+), 319 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c74e8e40c..1c4549b28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.0.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd" +checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" dependencies = [ "memchr", "once_cell", @@ -383,6 +383,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "dunce" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" + [[package]] name = "either" version = "1.8.0" @@ -561,75 +567,118 @@ dependencies = [ ] [[package]] -name = "git-actor" -version = "0.17.0" +name = "gix" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d5dbcb1efbee862cdc851a23377e1a8a5c1a8971740b4933d4ce022a0889a8" +dependencies = [ + "gix-actor", + "gix-attributes", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-discover", + "gix-features", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-index", + "gix-lock", + "gix-mailmap", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-prompt", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-sec", + "gix-tempfile", + "gix-traverse", + "gix-url", + "gix-validate", + "gix-worktree", + "log", + "once_cell", + "prodash", + "signal-hook", + "smallvec", + "thiserror", + "unicode-normalization", +] + +[[package]] +name = "gix-actor" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9e5fd7bc63ad527d64584f8d01f99b89c051f5fbb8144b58ae5f812775065cf" +checksum = "381153ea93b9d8a5c6894a5c734b2e9c15d623063adfd2bda4342ecf90f9a5f8" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", "btoi", - "git-date", + "gix-date", "itoa", "nom", "quick-error", ] [[package]] -name = "git-attributes" -version = "0.8.0" +name = "gix-attributes" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8013dfce47c1e29236d732308933e2c77af5355ec5105755d26faf7764d3f7b" +checksum = "df09b20424fd4cee04c43b50df954c4b119c45b769639b60d80ee8bb6d84e0aa" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", "compact_str", - "git-features", - "git-glob", - "git-path", - "git-quote", + "gix-features", + "gix-glob", + "gix-path", + "gix-quote", "thiserror", "unicode-bom", ] [[package]] -name = "git-bitmap" -version = "0.2.0" +name = "gix-bitmap" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44304093ac66a0ada1b243c15c3a503a165a1d0f50bec748f4e5a9b84a0d0722" +checksum = "5229fd26e288f417c8dd2385c5bc740415eb55aba4d6f529db7ad4b526771e06" dependencies = [ "quick-error", ] [[package]] -name = "git-chunk" -version = "0.4.0" +name = "gix-chunk" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3090baa2f4a3fe488a9b3e31090b83259aaf930bf0634af34c18117274f8f1a8" +checksum = "b0d39583cab06464b8bf73b3f1707458270f0e7383cb24c3c9c1a16e6f792978" dependencies = [ "thiserror", ] [[package]] -name = "git-command" -version = "0.2.1" +name = "gix-command" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "215145cc1686a45bc6f9872b153a0d3f3c40a1b94173a928325e1b53dfa5e2af" +checksum = "b2c6f75c1e0f924de39e750880a6e21307194bb1ab773efe3c7d2d787277f8ab" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", ] [[package]] -name = "git-config" -version = "0.15.0" +name = "gix-config" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9da662fd64ac69772158dcf04777da6266f0f36bc9a310b3eb2d805bb696315" +checksum = "398b5003d5e4991355528e8fbb4a9d532050c8327df790522735a711db82fcd0" dependencies = [ - "bstr 1.0.1", - "git-config-value", - "git-features", - "git-glob", - "git-path", - "git-ref", - "git-sec", + "bstr 1.3.0", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", "memchr", "nom", "once_cell", @@ -639,81 +688,82 @@ dependencies = [ ] [[package]] -name = "git-config-value" -version = "0.10.0" +name = "gix-config-value" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989a90c1c630513a153c685b4249b96fdf938afc75bf7ef2ae1ccbd3d799f5db" +checksum = "693d4a4ba0531e46fe558459557a5b29fb86c3e4b2666c1c0861d93c7c678331" dependencies = [ "bitflags", - "bstr 1.0.1", - "git-path", + "bstr 1.3.0", + "gix-path", "libc", "thiserror", ] [[package]] -name = "git-credentials" -version = "0.9.0" +name = "gix-credentials" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cd6bbe001afd6356b35ef13f2a6b0f0abc0133d1b2ecaec1033bdd769616d6" +checksum = "5d1536399f70146825bd10321adc5307032e3de93f4954a3c54184281f2e6955" dependencies = [ - "bstr 1.0.1", - "git-command", - "git-config-value", - "git-path", - "git-prompt", - "git-sec", - "git-url", + "bstr 1.3.0", + "gix-command", + "gix-config-value", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-url", "thiserror", ] [[package]] -name = "git-date" -version = "0.4.0" +name = "gix-date" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "412c9b89026505bd24d5f8acafa578de6eea3b271ece307a73b8e646e671302a" +checksum = "b96271912ce39822501616f177dea7218784e6c63be90d5f36322ff3a722aae2" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", "itoa", "thiserror", "time", ] [[package]] -name = "git-diff" -version = "0.26.0" +name = "gix-diff" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca87474422d26d606d04cec6bedfabcd92a0a74102cd7936785358ced6a4a25a" +checksum = "2ec3351a6cec2ddca29c1124afef8b4f3fad0b617dce8916148153541468117c" dependencies = [ - "git-hash", - "git-object", + "gix-hash", + "gix-object", "imara-diff", "thiserror", ] [[package]] -name = "git-discover" -version = "0.12.0" +name = "gix-discover" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9e26e0bc434643228cd418185bd28ca5c7cf831bde1da434807391c27ac40e" +checksum = "38029783886cb46fbe63e61b02a70404aa04cfeacfb53ed336832c20fcb1e281" dependencies = [ - "bstr 1.0.1", - "git-hash", - "git-path", - "git-ref", - "git-sec", + "bstr 1.3.0", + "dunce", + "gix-hash", + "gix-path", + "gix-ref", + "gix-sec", "thiserror", ] [[package]] -name = "git-features" -version = "0.26.0" +name = "gix-features" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ff74064fa007c5beefa89a64bb72834f32b3c497750a56c79c6802bbdb311f9" +checksum = "3402b831ea4bb3af36369d61dbf250eb0e1a8577d3cb77b9719c11a82485bfe9" dependencies = [ "crc32fast", "flate2", - "git-hash", + "gix-hash", "libc", "once_cell", "prodash", @@ -723,51 +773,51 @@ dependencies = [ ] [[package]] -name = "git-glob" -version = "0.5.1" +name = "gix-glob" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3908404c9b76ac7b3f636a104142378d3eaa78623cbc6eb7c7f0651979d48e8a" +checksum = "93e43efd776bc543f46f0fd0ca3d920c37af71a764a16f2aebd89765e9ff2993" dependencies = [ "bitflags", - "bstr 1.0.1", + "bstr 1.3.0", ] [[package]] -name = "git-hash" -version = "0.10.1" +name = "gix-hash" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1532d82bf830532f8d545c5b7b568e311e3593f16cf7ee9dd0ce03c74b12b99d" +checksum = "0c0c5a9f4d621d4f4ea046bb331df5c746ca735b8cae5b234cc2be70ee4dbef0" dependencies = [ "hex", "thiserror", ] [[package]] -name = "git-hashtable" -version = "0.1.0" +name = "gix-hashtable" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52b625ad8cc360a0b7f426266f21fb07bd49b8f4ccf1b3ca7bc89424db1dec4" +checksum = "1a256cceeea0f0d7f42a0c3ac649535644a04395d9f415518f4008ef6bb331b5" dependencies = [ - "git-hash", + "gix-hash", "hashbrown 0.13.2", ] [[package]] -name = "git-index" -version = "0.12.1" +name = "gix-index" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "485da97dd4f69c7d9a8dc238cd6f4a726387ffc34573489e8e0d2bee266e3454" +checksum = "decb345476c25434a202f1cf8a24aa71133c567b7b502c549fd57211c51ed78a" dependencies = [ "atoi", "bitflags", - "bstr 1.0.1", + "bstr 1.3.0", "filetime", - "git-bitmap", - "git-features", - "git-hash", - "git-lock", - "git-object", - "git-traverse", + "gix-bitmap", + "gix-features", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", "itoa", "memmap2", "smallvec", @@ -775,39 +825,39 @@ dependencies = [ ] [[package]] -name = "git-lock" -version = "3.0.0" +name = "gix-lock" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e4f05b8a68c3a5dd83a6651c76be384e910fe283072184fdab9d77f87ccec2" +checksum = "e5fe84f09afadec78a7227d80f58cb5412d216dbae4b7fa060b619c0ce62b55d" dependencies = [ "fastrand", - "git-tempfile", + "gix-tempfile", "quick-error", ] [[package]] -name = "git-mailmap" -version = "0.9.0" +name = "gix-mailmap" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0316b4346f3e162ade368209efb8a609b587793c74aa3b8de0ec01a4f3580120" +checksum = "a28214e75835ab33d34210a18981110642728bf169f5e339dbfb6f6380b94318" dependencies = [ - "bstr 1.0.1", - "git-actor", + "bstr 1.3.0", + "gix-actor", "quick-error", ] [[package]] -name = "git-object" -version = "0.26.0" +name = "gix-object" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8563e2d6f524d7053f3106714f99ecdc3adbba2cb7108c09d71a02579f2e19" +checksum = "de3b04e3028ddab838d005104f234f4d2c26ecd51f2d72d96747c878094c4619" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", "btoi", - "git-actor", - "git-features", - "git-hash", - "git-validate", + "gix-actor", + "gix-features", + "gix-hash", + "gix-validate", "hex", "itoa", "nom", @@ -816,41 +866,41 @@ dependencies = [ ] [[package]] -name = "git-odb" -version = "0.40.0" +name = "gix-odb" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616115a0e3daff6e08842758d24547b37a6eb6d0e2eedd95a740c3aaa2750333" +checksum = "0bd81ab7cd13c0f78bd619f967509953094f415288f8693dbb63a084e5bb39c4" dependencies = [ "arc-swap", - "git-features", - "git-hash", - "git-object", - "git-pack", - "git-path", - "git-quote", + "gix-features", + "gix-hash", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", "parking_lot 0.12.1", "tempfile", "thiserror", ] [[package]] -name = "git-pack" -version = "0.30.0" +name = "gix-pack" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd16b88f4b66041f41ca510c28bd81c4ee7363c5a544b3d62b4170432965871" +checksum = "26143c5c8bc145a39e9b335cc74504f2eba2ce68b1724661d8e6cb4484ab187e" dependencies = [ "bytesize", "clru", "dashmap", - "git-chunk", - "git-diff", - "git-features", - "git-hash", - "git-hashtable", - "git-object", - "git-path", - "git-tempfile", - "git-traverse", + "gix-chunk", + "gix-diff", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-traverse", "memmap2", "parking_lot 0.12.1", "smallvec", @@ -858,147 +908,104 @@ dependencies = [ ] [[package]] -name = "git-path" -version = "0.7.0" +name = "gix-path" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40e68481a06da243d3f4dfd86a4be39c24eefb535017a862e845140dcdb878a" +checksum = "f6c104a66dec149cb8f7aaafc6ab797654cf82d67f050fd0cb7e7294e328354b" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", "thiserror", ] [[package]] -name = "git-prompt" -version = "0.3.0" +name = "gix-prompt" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3612a486e507dd431ef0f7108eeaafc8fd1ed7bd0f205a88554f6f91fe5dccbf" +checksum = "a20cebf73229debaa82574c4fd20dcaf00fa8d4bfce823a862c4e990d7a0b5b4" dependencies = [ - "git-command", - "git-config-value", + "gix-command", + "gix-config-value", "nix", "parking_lot 0.12.1", "thiserror", ] [[package]] -name = "git-quote" -version = "0.4.0" +name = "gix-quote" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd11f4e7f251ab297545faa4c5a4517f4985a43b9c16bf96fa49107f58e837f" +checksum = "e34cffcf5dd0ddf06a768b697a0f29319284deffba970e4355b51b0fee61ffa2" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", "btoi", "quick-error", ] [[package]] -name = "git-ref" -version = "0.23.0" +name = "gix-ref" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6767925a6fc4af5c5a81e348d1d851c1b3ab2b512bd7f562ac11be37c14468" +checksum = "93e85abee11aa093f24da7336bf0a8ad598f15da396b28cf1270ab1091137d35" dependencies = [ - "git-actor", - "git-features", - "git-hash", - "git-lock", - "git-object", - "git-path", - "git-tempfile", - "git-validate", + "gix-actor", + "gix-features", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-validate", "memmap2", "nom", "thiserror", ] [[package]] -name = "git-refspec" -version = "0.7.0" +name = "gix-refspec" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf310ed5f2829ac0af96e7d4aebd4ae4b89f0718a7ae3666d09b02b2c5a1dfd" +checksum = "ac80b201eeeb3bc554583fd0127cb6bc9e20981cabb085149c9740329f8a2319" dependencies = [ - "bstr 1.0.1", - "git-hash", - "git-revision", - "git-validate", - "smallvec", - "thiserror", -] - -[[package]] -name = "git-repository" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993277960cb7e2d3991a11c1ec6951c1d142de052c26a18d2db64304e52d3741" -dependencies = [ - "git-actor", - "git-attributes", - "git-config", - "git-credentials", - "git-date", - "git-diff", - "git-discover", - "git-features", - "git-glob", - "git-hash", - "git-hashtable", - "git-index", - "git-lock", - "git-mailmap", - "git-object", - "git-odb", - "git-pack", - "git-path", - "git-prompt", - "git-ref", - "git-refspec", - "git-revision", - "git-sec", - "git-tempfile", - "git-traverse", - "git-url", - "git-validate", - "git-worktree", - "log", - "once_cell", - "prodash", - "signal-hook", + "bstr 1.3.0", + "gix-hash", + "gix-revision", + "gix-validate", "smallvec", "thiserror", - "unicode-normalization", ] [[package]] -name = "git-revision" -version = "0.10.0" +name = "gix-revision" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9a6bd28c9d1676bb96f428cd09614ae18a0087d7cea1cebfd177e25f99b2af" +checksum = "107a10d92379a797bea0f1d0eceded58e08913e0a706c8d436592673c6c6503f" dependencies = [ - "bstr 1.0.1", - "git-date", - "git-hash", - "git-hashtable", - "git-object", + "bstr 1.3.0", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", "thiserror", ] [[package]] -name = "git-sec" -version = "0.6.0" +name = "gix-sec" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1802e8252fa223b0ad89a393aed461132174ced1e6842a41f56dc92a3fc14f" +checksum = "e8ffa5bf0772f9b01de501c035b6b084cf9b8bb07dec41e3afc6a17336a65f47" dependencies = [ "bitflags", "dirs", - "git-path", + "gix-path", "libc", "windows", ] [[package]] -name = "git-tempfile" -version = "3.0.0" +name = "gix-tempfile" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6bb4dee86c8cae5a078cfaac3b004ef99c31548ed86218f23a7ff9b4b74f3be" +checksum = "48590cb5de0b8feadee42466a90028877ba67b9fd894c5493b4b64f5e3217c17" dependencies = [ "dashmap", "libc", @@ -1009,55 +1016,55 @@ dependencies = [ ] [[package]] -name = "git-traverse" -version = "0.22.0" +name = "gix-traverse" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd356da21ec00f69b9d4f105df4cb85543c746b18f4b7fc81529ce77713cdb29" +checksum = "f7ee7eee98b6e196fba1f34751d4399e0daa4e61892a78f634d0901e52dd739b" dependencies = [ - "git-hash", - "git-hashtable", - "git-object", + "gix-hash", + "gix-hashtable", + "gix-object", "thiserror", ] [[package]] -name = "git-url" -version = "0.13.0" +name = "gix-url" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85af407ed0dbb8d8da2a7241827d2fd5681186d9dab3570fc8dd8d6152ec48f" +checksum = "4d6e3e05267f7873099b3e510ab8eebdfc28920a915ab2e3d549493abe0fd9f0" dependencies = [ - "bstr 1.0.1", - "git-features", - "git-path", + "bstr 1.3.0", + "gix-features", + "gix-path", "home", "thiserror", "url", ] [[package]] -name = "git-validate" -version = "0.7.1" +name = "gix-validate" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0431cf9352c596dc7c8ec9066ee551ce54e63c86c3c767e5baf763f6019ff3c2" +checksum = "b69ddb780ea1465255e66818d75b7098371c58dbc9560da4488a44b9f5c7e443" dependencies = [ - "bstr 1.0.1", + "bstr 1.3.0", "thiserror", ] [[package]] -name = "git-worktree" -version = "0.12.0" +name = "gix-worktree" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3bc63878f134e08ed52dba5d82422798c01a3f2e48c38ae9a2f7ff9194f362" +checksum = "da7ddd5b042c85cfe768d5ea97bb204cf1ed2b9413148f482146f4e831ca172e" dependencies = [ - "bstr 1.0.1", - "git-attributes", - "git-features", - "git-glob", - "git-hash", - "git-index", - "git-object", - "git-path", + "bstr 1.3.0", + "gix-attributes", + "gix-features", + "gix-glob", + "gix-hash", + "gix-index", + "gix-object", + "gix-path", "io-close", "thiserror", ] @@ -1269,7 +1276,7 @@ dependencies = [ name = "helix-vcs" version = "0.6.0" dependencies = [ - "git-repository", + "gix", "helix-core", "imara-diff", "log", @@ -2472,17 +2479,17 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.40.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30acc718a52fb130fec72b1cb5f55ffeeec9253e1b785e94db222178a6acaa1" +checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" dependencies = [ - "windows_aarch64_gnullvm 0.40.0", - "windows_aarch64_msvc 0.40.0", - "windows_i686_gnu 0.40.0", - "windows_i686_msvc 0.40.0", - "windows_x86_64_gnu 0.40.0", - "windows_x86_64_gnullvm 0.40.0", - "windows_x86_64_msvc 0.40.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2491,93 +2498,51 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm 0.42.0", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm 0.42.0", - "windows_x86_64_msvc 0.42.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3caa4a1a16561b714323ca6b0817403738583033a6a92e04c5d10d4ba37ca10" - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" -[[package]] -name = "windows_aarch64_msvc" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "328973c62dfcc50fb1aaa8e7100676e0b642fe56bac6bafff3327902db843ab4" - [[package]] name = "windows_aarch64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" -[[package]] -name = "windows_i686_gnu" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa5b09fad70f0df85dea2ac2a525537e415e2bf63ee31cf9b8e263645ee9f3c1" - [[package]] name = "windows_i686_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" -[[package]] -name = "windows_i686_msvc" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1ad4031c1a98491fa195d8d43d7489cb749f135f2e5c4eed58da094bd0d876" - [[package]] name = "windows_i686_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" -[[package]] -name = "windows_x86_64_gnu" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520ff37edd72da8064b49d2281182898e17f0688ae9f4070bca27e4b5c162ac7" - [[package]] name = "windows_x86_64_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046e5b82215102c44fd75f488f1b9158973d02aa34d06ed85c23d6f5520a2853" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" -[[package]] -name = "windows_x86_64_msvc" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0c9c6df55dd1bfa76e131cef44bdd8ec9c819ef3611f04dfe453fd5bfeda28" - [[package]] name = "windows_x86_64_msvc" version = "0.42.0" diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index c4d6eb45b..ad8005d1a 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -16,13 +16,13 @@ helix-core = { version = "0.6", path = "../helix-core" } tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] } parking_lot = "0.12" -git-repository = { version = "0.32", default-features = false , optional = true } +gix= { version = "0.36.1", default-features = false , optional = true } imara-diff = "0.1.5" log = "0.4" [features] -git = ["git-repository"] +git = ["gix"] [dev-dependencies] tempfile = "3.4" \ No newline at end of file diff --git a/helix-vcs/src/git.rs b/helix-vcs/src/git.rs index 432159b6c..2a540c8d1 100644 --- a/helix-vcs/src/git.rs +++ b/helix-vcs/src/git.rs @@ -1,9 +1,8 @@ use std::path::Path; -use git::objs::tree::EntryMode; -use git::sec::trust::DefaultForLevel; -use git::{Commit, ObjectId, Repository, ThreadSafeRepository}; -use git_repository as git; +use gix::objs::tree::EntryMode; +use gix::sec::trust::DefaultForLevel; +use gix::{Commit, ObjectId, Repository, ThreadSafeRepository}; use crate::DiffProvider; @@ -15,13 +14,13 @@ pub struct Git; impl Git { fn open_repo(path: &Path, ceiling_dir: Option<&Path>) -> Option { // custom open options - let mut git_open_opts_map = git::sec::trust::Mapping::::default(); + let mut git_open_opts_map = gix::sec::trust::Mapping::::default(); // On windows various configuration options are bundled as part of the installations // This path depends on the install location of git and therefore requires some overhead to lookup // This is basically only used on windows and has some overhead hence it's disabled on other platforms. // `gitoxide` doesn't use this as default - let config = git::permissions::Config { + let config = gix::permissions::Config { system: true, git: true, user: true, @@ -30,16 +29,16 @@ impl Git { git_binary: cfg!(windows), }; // change options for config permissions without touching anything else - git_open_opts_map.reduced = git_open_opts_map.reduced.permissions(git::Permissions { + git_open_opts_map.reduced = git_open_opts_map.reduced.permissions(gix::Permissions { config, - ..git::Permissions::default_for_level(git::sec::Trust::Reduced) + ..gix::Permissions::default_for_level(gix::sec::Trust::Reduced) }); - git_open_opts_map.full = git_open_opts_map.full.permissions(git::Permissions { + git_open_opts_map.full = git_open_opts_map.full.permissions(gix::Permissions { config, - ..git::Permissions::default_for_level(git::sec::Trust::Full) + ..gix::Permissions::default_for_level(gix::sec::Trust::Full) }); - let mut open_options = git::discover::upwards::Options::default(); + let mut open_options = gix::discover::upwards::Options::default(); if let Some(ceiling_dir) = ceiling_dir { open_options.ceiling_dirs = vec![ceiling_dir.to_owned()]; } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ace4f5f96..c72b136d4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.63.0" +channel = "1.64.0" components = ["rustfmt", "rust-src"] From 5b4e73f37dea1b199e30610f255731f110a4a605 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 23 Feb 2023 07:59:41 +0100 Subject: [PATCH 0043/1169] Update helix-vcs/Cargo.toml Co-authored-by: Ivan Tham --- helix-vcs/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index ad8005d1a..d7eef9db3 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -16,7 +16,7 @@ helix-core = { version = "0.6", path = "../helix-core" } tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] } parking_lot = "0.12" -gix= { version = "0.36.1", default-features = false , optional = true } +gix = { version = "0.36.1", default-features = false , optional = true } imara-diff = "0.1.5" log = "0.4" From ccdb1446652662e2577fb7405fee9ccd49c56180 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sat, 4 Mar 2023 21:53:54 +0100 Subject: [PATCH 0044/1169] update MSRV to 1.65 --- .github/workflows/build.yml | 6 +- Cargo.lock | 236 ++++++++++------------------------ helix-term/Cargo.toml | 2 +- helix-term/src/commands.rs | 5 +- helix-term/src/ui/document.rs | 13 +- helix-vcs/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- 7 files changed, 77 insertions(+), 189 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 44d267884..d7d7d47e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: uses: actions/checkout@v3 - name: Install stable toolchain - uses: dtolnay/rust-toolchain@1.63 + uses: dtolnay/rust-toolchain@1.65 - uses: Swatinem/rust-cache@v2 @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@v3 - name: Install stable toolchain - uses: dtolnay/rust-toolchain@1.63 + uses: dtolnay/rust-toolchain@1.65 with: components: rustfmt, clippy @@ -91,7 +91,7 @@ jobs: uses: actions/checkout@v3 - name: Install stable toolchain - uses: dtolnay/rust-toolchain@1.63 + uses: dtolnay/rust-toolchain@1.65 - uses: Swatinem/rust-cache@v2 diff --git a/Cargo.lock b/Cargo.lock index 1c4549b28..090b5f027 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,15 +61,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -107,9 +98,9 @@ dependencies = [ [[package]] name = "btoi" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c0869a9faa81f8bbf8102371105d6d0a7b79167a04c340b04ab16892246a11" +checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" dependencies = [ "num-traits", ] @@ -132,27 +123,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" -[[package]] -name = "bytesize" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70" - [[package]] name = "cassowary" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" -[[package]] -name = "castaway" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" -dependencies = [ - "rustversion", -] - [[package]] name = "cc" version = "1.0.79" @@ -215,17 +191,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "compact_str" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5138945395949e7dfba09646dc9e766b548ff48e23deb5246890e6b64ae9e1b9" -dependencies = [ - "castaway", - "itoa", - "ryu", -] - [[package]] name = "content_inspector" version = "0.2.4" @@ -270,7 +235,7 @@ dependencies = [ "futures-core", "libc", "mio", - "parking_lot 0.12.1", + "parking_lot", "signal-hook", "signal-hook-mio", "winapi", @@ -329,19 +294,6 @@ dependencies = [ "syn", ] -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if", - "hashbrown 0.12.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.4", -] - [[package]] name = "dirs" version = "4.0.0" @@ -568,9 +520,9 @@ dependencies = [ [[package]] name = "gix" -version = "0.36.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d5dbcb1efbee862cdc851a23377e1a8a5c1a8971740b4933d4ce022a0889a8" +checksum = "dabfac58aecb4a38cdd2568de66eb1f0d968fd6726f5a80cb8bea7944ef10cc0" dependencies = [ "gix-actor", "gix-attributes", @@ -602,7 +554,6 @@ dependencies = [ "gix-worktree", "log", "once_cell", - "prodash", "signal-hook", "smallvec", "thiserror", @@ -611,26 +562,25 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.17.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381153ea93b9d8a5c6894a5c734b2e9c15d623063adfd2bda4342ecf90f9a5f8" +checksum = "dc22b0cdc52237667c301dd7cdc6ead8f8f73c9f824e9942c8ebd6b764f6c0bf" dependencies = [ "bstr 1.3.0", "btoi", "gix-date", "itoa", "nom", - "quick-error", + "thiserror", ] [[package]] name = "gix-attributes" -version = "0.8.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df09b20424fd4cee04c43b50df954c4b119c45b769639b60d80ee8bb6d84e0aa" +checksum = "2231a25934a240d0a4b6f4478401c73ee81d8be52de0293eedbc172334abf3e1" dependencies = [ "bstr 1.3.0", - "compact_str", "gix-features", "gix-glob", "gix-path", @@ -641,11 +591,11 @@ dependencies = [ [[package]] name = "gix-bitmap" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5229fd26e288f417c8dd2385c5bc740415eb55aba4d6f529db7ad4b526771e06" +checksum = "024bca0c7187517bda5ea24ab148c9ca8208dd0c3e2bea88cdb2008f91791a6d" dependencies = [ - "quick-error", + "thiserror", ] [[package]] @@ -668,9 +618,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.16.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398b5003d5e4991355528e8fbb4a9d532050c8327df790522735a711db82fcd0" +checksum = "52c62e26ce11f607712e4f49a0a192ed87675d30187fd61be070abbd607d12f1" dependencies = [ "bstr 1.3.0", "gix-config-value", @@ -702,9 +652,9 @@ dependencies = [ [[package]] name = "gix-credentials" -version = "0.9.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1536399f70146825bd10321adc5307032e3de93f4954a3c54184281f2e6955" +checksum = "5be32b5fe339a31b8e53fa854081dc914c45020dcb64637f3c21baf69c96fc1b" dependencies = [ "bstr 1.3.0", "gix-command", @@ -730,9 +680,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.26.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec3351a6cec2ddca29c1124afef8b4f3fad0b617dce8916148153541468117c" +checksum = "585b0834d4b6791a848637c4e109545fda9b0f29b591ba55edb33ceda6e7856b" dependencies = [ "gix-hash", "gix-object", @@ -742,9 +692,9 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.13.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38029783886cb46fbe63e61b02a70404aa04cfeacfb53ed336832c20fcb1e281" +checksum = "91c204adba5ebd211c74735cbb65817d277e154486bac0dffa3701f163b80350" dependencies = [ "bstr 1.3.0", "dunce", @@ -757,9 +707,9 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.26.5" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3402b831ea4bb3af36369d61dbf250eb0e1a8577d3cb77b9719c11a82485bfe9" +checksum = "5e6a9dfa7b3c1a99315203e8b97f8f99f3bd95731590607abeaa5ca31bc41fe3" dependencies = [ "crc32fast", "flate2", @@ -767,8 +717,8 @@ dependencies = [ "libc", "once_cell", "prodash", - "quick-error", "sha1_smol", + "thiserror", "walkdir", ] @@ -794,23 +744,24 @@ dependencies = [ [[package]] name = "gix-hashtable" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a256cceeea0f0d7f42a0c3ac649535644a04395d9f415518f4008ef6bb331b5" +checksum = "9609c1b8f36f12968e6a6098f7cdb52004f7d42d570f47a2d6d7c16612f19acb" dependencies = [ "gix-hash", "hashbrown 0.13.2", + "parking_lot", ] [[package]] name = "gix-index" -version = "0.12.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "decb345476c25434a202f1cf8a24aa71133c567b7b502c549fd57211c51ed78a" +checksum = "c12caf7886c7ba06f2b28835cdc2be1dca86bd047d00299d2d49e707ce1c2616" dependencies = [ - "atoi", "bitflags", "bstr 1.3.0", + "btoi", "filetime", "gix-bitmap", "gix-features", @@ -826,31 +777,31 @@ dependencies = [ [[package]] name = "gix-lock" -version = "3.0.2" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fe84f09afadec78a7227d80f58cb5412d216dbae4b7fa060b619c0ce62b55d" +checksum = "66119ff8a4a395d0ea033fef718bc85f8b4f0855874f4ce1e005fc16cfe1f66e" dependencies = [ "fastrand", "gix-tempfile", - "quick-error", + "thiserror", ] [[package]] name = "gix-mailmap" -version = "0.9.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28214e75835ab33d34210a18981110642728bf169f5e339dbfb6f6380b94318" +checksum = "2b66aea5e52875cd4915f4957a6f4b75831a36981e2ec3f5fad9e370e444fe1a" dependencies = [ "bstr 1.3.0", "gix-actor", - "quick-error", + "thiserror", ] [[package]] name = "gix-object" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3b04e3028ddab838d005104f234f4d2c26ecd51f2d72d96747c878094c4619" +checksum = "8df068db9180ee935fbb70504848369e270bdcb576b05c0faa8b9fd3b86fc017" dependencies = [ "bstr 1.3.0", "btoi", @@ -867,9 +818,9 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.40.2" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd81ab7cd13c0f78bd619f967509953094f415288f8693dbb63a084e5bb39c4" +checksum = "e9a5f9e1afbd509761977a2ea02869cedaaba500b4e783deb2e4de5179a55a80" dependencies = [ "arc-swap", "gix-features", @@ -878,20 +829,18 @@ dependencies = [ "gix-pack", "gix-path", "gix-quote", - "parking_lot 0.12.1", + "parking_lot", "tempfile", "thiserror", ] [[package]] name = "gix-pack" -version = "0.30.3" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26143c5c8bc145a39e9b335cc74504f2eba2ce68b1724661d8e6cb4484ab187e" +checksum = "e51db84e1459a8022e518d40a8778028d793dbb28e4d35c9a5eaf92658fb0775" dependencies = [ - "bytesize", "clru", - "dashmap", "gix-chunk", "gix-diff", "gix-features", @@ -902,7 +851,7 @@ dependencies = [ "gix-tempfile", "gix-traverse", "memmap2", - "parking_lot 0.12.1", + "parking_lot", "smallvec", "thiserror", ] @@ -926,26 +875,26 @@ dependencies = [ "gix-command", "gix-config-value", "nix", - "parking_lot 0.12.1", + "parking_lot", "thiserror", ] [[package]] name = "gix-quote" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34cffcf5dd0ddf06a768b697a0f29319284deffba970e4355b51b0fee61ffa2" +checksum = "a282f5a8d9ee0b09ec47390ac727350c48f2f5c76d803cd8da6b3e7ad56e0bcb" dependencies = [ "bstr 1.3.0", "btoi", - "quick-error", + "thiserror", ] [[package]] name = "gix-ref" -version = "0.24.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e85abee11aa093f24da7336bf0a8ad598f15da396b28cf1270ab1091137d35" +checksum = "90a0ed29e581f04b904ecd0c32b11f33b8209b5a0af9c43f415249a4f2fba632" dependencies = [ "gix-actor", "gix-features", @@ -962,9 +911,9 @@ dependencies = [ [[package]] name = "gix-refspec" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac80b201eeeb3bc554583fd0127cb6bc9e20981cabb085149c9740329f8a2319" +checksum = "aba332462bda2e8efeae4302b39a6ed01ad56ef772fd5b7ef197cf2798294d65" dependencies = [ "bstr 1.3.0", "gix-hash", @@ -976,9 +925,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.10.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "107a10d92379a797bea0f1d0eceded58e08913e0a706c8d436592673c6c6503f" +checksum = "ed98e4a0254953c64bc913bd23146a1de662067d5cf974cbdde396958b39e5b0" dependencies = [ "bstr 1.3.0", "gix-date", @@ -1003,13 +952,13 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "3.0.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48590cb5de0b8feadee42466a90028877ba67b9fd894c5493b4b64f5e3217c17" +checksum = "a8e0227bd284cd16105e8479602bb8af6bddcb800427e881c1feee4806310a31" dependencies = [ - "dashmap", "libc", "once_cell", + "parking_lot", "signal-hook", "signal-hook-registry", "tempfile", @@ -1017,9 +966,9 @@ dependencies = [ [[package]] name = "gix-traverse" -version = "0.22.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ee7eee98b6e196fba1f34751d4399e0daa4e61892a78f634d0901e52dd739b" +checksum = "dd9a4a07bb22168dc79c60e1a6a41919d198187ca83d8a5940ad8d7122a45df3" dependencies = [ "gix-hash", "gix-hashtable", @@ -1029,9 +978,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.13.3" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6e3e05267f7873099b3e510ab8eebdfc28920a915ab2e3d549493abe0fd9f0" +checksum = "044072b7ce8601b62dcec841b92129f5cc677072823324121b395d766ac5f528" dependencies = [ "bstr 1.3.0", "gix-features", @@ -1053,9 +1002,9 @@ dependencies = [ [[package]] name = "gix-worktree" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7ddd5b042c85cfe768d5ea97bb204cf1ed2b9413148f482146f4e831ca172e" +checksum = "b7cb9af6e56152953d8fe113c4f9d7cf60cf7a982362711e9200a255579b49cb" dependencies = [ "bstr 1.3.0", "gix-attributes", @@ -1280,7 +1229,7 @@ dependencies = [ "helix-core", "imara-diff", "log", - "parking_lot 0.12.1", + "parking_lot", "tempfile", "tokio", ] @@ -1339,12 +1288,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "human_format" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86cce260d758a9aa3d7c4b99d55c815a540f8a37514ba6046ab6be402a157cb0" - [[package]] name = "iana-time-zone" version = "0.1.53" @@ -1653,17 +1596,6 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1671,21 +1603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.4", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -1730,15 +1648,9 @@ dependencies = [ [[package]] name = "prodash" -version = "23.0.0" +version = "23.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8c414345b4a98cbcd0e8d8829c8f54b47a7ed4fb771c45b7c5c6c0ae23dc4c" -dependencies = [ - "bytesize", - "dashmap", - "human_format", - "parking_lot 0.11.2", -] +checksum = "d73c6b64cb5b99eb63ca97d378685712617ec0172ff5c04cd47a489d3e2c51f8" [[package]] name = "pulldown-cmark" @@ -1751,12 +1663,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quickcheck" version = "1.0.3" @@ -1860,12 +1766,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "rustversion" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" - [[package]] name = "ryu" version = "1.0.11" @@ -2208,7 +2108,7 @@ dependencies = [ "memchr", "mio", "num_cpus", - "parking_lot 0.12.1", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 975d08712..b1d63a04a 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/helix-editor/helix" homepage = "https://helix-editor.com" include = ["src/**/*", "README.md"] default-run = "hx" -rust-version = "1.57" +rust-version = "1.65" [features] default = ["git"] diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index fb55ca2a8..7b9b59434 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3061,10 +3061,7 @@ fn goto_next_change_impl(cx: &mut Context, direction: Direction) { .prev_hunk(cursor_line) .map(|idx| idx.saturating_sub(count)), }; - // TODO refactor with let..else once MSRV reaches 1.65 - let hunk_idx = if let Some(hunk_idx) = hunk_idx { - hunk_idx - } else { + let Some(hunk_idx) = hunk_idx else { return range; }; let hunk = hunks.nth_hunk(hunk_idx); diff --git a/helix-term/src/ui/document.rs b/helix-term/src/ui/document.rs index 4f615f7bd..28a52f74d 100644 --- a/helix-term/src/ui/document.rs +++ b/helix-term/src/ui/document.rs @@ -202,10 +202,7 @@ pub fn render_text<'t>( // formattter.line_pos returns to line index of the next grapheme // so it must be called before formatter.next let doc_line = formatter.line_pos(); - // TODO refactor with let .. else once MSRV reaches 1.65 - let (grapheme, mut pos) = if let Some(it) = formatter.next() { - it - } else { + let Some((grapheme, mut pos)) = formatter.next() else { let mut last_pos = formatter.visual_pos(); if last_pos.row >= row_off { last_pos.col -= 1; @@ -226,7 +223,6 @@ pub fn render_text<'t>( // skip any graphemes on visual lines before the block start if pos.row < row_off { if char_pos >= style_span.1 { - // TODO refactor using let..else once MSRV reaches 1.65 style_span = if let Some(style_span) = styles.next() { style_span } else { @@ -266,12 +262,7 @@ pub fn render_text<'t>( // aquire the correct grapheme style if char_pos >= style_span.1 { - // TODO refactor using let..else once MSRV reaches 1.65 - style_span = if let Some(style_span) = styles.next() { - style_span - } else { - (Style::default(), usize::MAX) - } + style_span = styles.next().unwrap_or((Style::default(), usize::MAX)); } char_pos += grapheme.doc_chars(); diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index d7eef9db3..789ee7951 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -16,7 +16,7 @@ helix-core = { version = "0.6", path = "../helix-core" } tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] } parking_lot = "0.12" -gix = { version = "0.36.1", default-features = false , optional = true } +gix = { version = "0.39.0", default-features = false , optional = true } imara-diff = "0.1.5" log = "0.4" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c72b136d4..2abc56652 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.64.0" +channel = "1.65.0" components = ["rustfmt", "rust-src"] From def26966d22dfc4b9a74e51542db246ba52f3dc9 Mon Sep 17 00:00:00 2001 From: nuid32 <91177333+nuid32@users.noreply.github.com> Date: Sun, 5 Mar 2023 16:43:24 +0500 Subject: [PATCH 0045/1169] Fix lacking space panic (#6109) * Fix lack of space for popup crash * Fix saturating -> wrapping * Fix wrapping -> saturating (I am an idiot) * Remove useless "mut" in helix-tui/src/buffer.rs Co-authored-by: Michael Davis * Remove redundant bound-check * Return bound-check back * Add bound-check for set_style * Remove set_style bound-check * Revert bound-check --------- Co-authored-by: Michael Davis --- helix-term/src/ui/markdown.rs | 5 +---- helix-view/src/graphics.rs | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 923dd73a1..87136992c 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -342,13 +342,10 @@ impl Component for Markdown { fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { let padding = 2; - if padding >= viewport.1 || padding >= viewport.0 { - return None; - } let contents = self.parse(None); // TODO: account for tab width - let max_text_width = (viewport.0 - padding).min(120); + let max_text_width = (viewport.0.saturating_sub(padding)).min(120); let (width, height) = crate::ui::text::required_size(&contents, max_text_width); Some((width + padding, height + padding)) diff --git a/helix-view/src/graphics.rs b/helix-view/src/graphics.rs index a0b645fae..e813fb560 100644 --- a/helix-view/src/graphics.rs +++ b/helix-view/src/graphics.rs @@ -237,8 +237,8 @@ impl Rect { Rect { x: x1, y: y1, - width: x2 - x1, - height: y2 - y1, + width: x2.saturating_sub(x1), + height: y2.saturating_sub(y1), } } From e6597bc9927c73800d31d4a45dc695a4abe77ec8 Mon Sep 17 00:00:00 2001 From: Erasin Wang Date: Mon, 6 Mar 2023 02:41:18 +0800 Subject: [PATCH 0046/1169] Update queries for godot4 (#6186) --- languages.toml | 2 +- runtime/queries/gdscript/highlights.scm | 88 +++++++++++++------ runtime/queries/gdscript/indents.scm | 2 +- runtime/queries/godot-resource/injections.scm | 16 ++++ 4 files changed, 79 insertions(+), 29 deletions(-) diff --git a/languages.toml b/languages.toml index e9a77b5a4..c34135244 100644 --- a/languages.toml +++ b/languages.toml @@ -1463,7 +1463,7 @@ indent = { tab-width = 4, unit = "\t" } [[grammar]] name = "gdscript" -source = { git = "https://github.com/PrestonKnopp/tree-sitter-gdscript", rev = "a56a6fcec3fb63ec3324bf9373aae7298861eb30" } +source = { git = "https://github.com/PrestonKnopp/tree-sitter-gdscript", rev = "a4b57cc3bcbfc24550e858159647e9238e7ad1ac" } [[language]] name = "godot-resource" diff --git a/runtime/queries/gdscript/highlights.scm b/runtime/queries/gdscript/highlights.scm index f36f4e35c..88f2a1875 100644 --- a/runtime/queries/gdscript/highlights.scm +++ b/runtime/queries/gdscript/highlights.scm @@ -1,7 +1,8 @@ ; Identifier naming conventions -((identifier) @constant - (#match? @constant "^[A-Z][A-Z_]*$")) +( + (identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]+$")) ; class (class_name_statement (name) @type) @@ -11,32 +12,35 @@ ; Function calls (attribute_call (identifier) @function) - (base_call (identifier) @function) - (call (identifier) @function) ; Function definitions (function_definition (name) @function) - (constructor_definition "_init" @function) + ;; Literals -(integer) @constant.numeric.integer -(float) @constant.numeric.float (comment) @comment (string) @string -(escape_sequence) @constant.character.escape -(identifier) @variable + (type) @type +(expression_statement (array (identifier) @type)) +(binary_operator (identifier) @type) -;; Literals +(variable_statement (identifier) @variable) +(get_node) @label + +(const_statement (name) @constant) +(integer) @constant.numeric.integer +(float) @constant.numeric.float +(escape_sequence) @constant.character.escape [ (true) (false) - (null) -] @constant.builtin +] @constant.builtin.boolean +(null) @constant.builtin [ "+" @@ -62,37 +66,67 @@ "~" "<<" ">>" - "and" - "or" - "not" ] @operator +(annotation (identifier) @keyword.storage.modifier) + [ - (static_keyword) - (remote_keyword) - (tool_statement) - "var" - "func" - "setget" - "in" - "is" - "as" "if" "else" "elif" +] @keyword.control.conditional + +[ "while" "for" +] @keyword.control.repeat + +[ "return" + "pass" "break" "continue" - "pass" +] @keyword.control.return + +[ + "func" +] @keyword.control.function + +[ + "export" +] @keyword.control.import + +[ + "in" + "is" + "as" "match" + "and" + "or" + "not" +] @keyword.operator + +[ + "var" "class" "class_name" "enum" +] @keyword.storage.type + + +[ + (remote_keyword) + (static_keyword) + "const" "signal" + "@" +] @keyword.storage.modifier + +[ + "setget" "onready" - "export" "extends" - "const" + "set" + "get" ] @keyword + diff --git a/runtime/queries/gdscript/indents.scm b/runtime/queries/gdscript/indents.scm index 1ab41a44d..c969eb7cf 100644 --- a/runtime/queries/gdscript/indents.scm +++ b/runtime/queries/gdscript/indents.scm @@ -12,6 +12,7 @@ (dictionary (_)) (array (_)) + (setget) ] @indent [ @@ -25,7 +26,6 @@ (class_definition) ] @extend - [ (return_statement) (break_statement) diff --git a/runtime/queries/godot-resource/injections.scm b/runtime/queries/godot-resource/injections.scm index 321c90add..7929d63cd 100644 --- a/runtime/queries/godot-resource/injections.scm +++ b/runtime/queries/godot-resource/injections.scm @@ -1,2 +1,18 @@ ((comment) @injection.content (#set! injection.language "comment")) + +; ((section) @injection.content +; (#set! injection.language "comment")) + +((section + (attribute + (identifier) @_type + (string) @_is_shader) + (property + (path) @_is_code + (string) @injection.content)) + (#match? @_type "type") + (#match? @_is_shader "Shader") + (#eq? @_is_code "code") + (#set! injection.language "glsl") +) From 39d5fb0e593b0da1bf6e2659c67a7914edcd75a6 Mon Sep 17 00:00:00 2001 From: Santiago Vrancovich <103597690+santiagovrancovich@users.noreply.github.com> Date: Sun, 5 Mar 2023 15:41:49 -0300 Subject: [PATCH 0047/1169] Remove centering view from Unimpaired commands (#6193) Remove `align_view` calls from `goto_*_diag` as per issue #6177 --- helix-term/src/commands.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 7b9b59434..cd7626ebd 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2941,7 +2941,6 @@ fn goto_first_diag(cx: &mut Context) { None => return, }; doc.set_selection(view.id, selection); - align_view(doc, view, Align::Center); } fn goto_last_diag(cx: &mut Context) { @@ -2951,7 +2950,6 @@ fn goto_last_diag(cx: &mut Context) { None => return, }; doc.set_selection(view.id, selection); - align_view(doc, view, Align::Center); } fn goto_next_diag(cx: &mut Context) { @@ -2973,7 +2971,6 @@ fn goto_next_diag(cx: &mut Context) { None => return, }; doc.set_selection(view.id, selection); - align_view(doc, view, Align::Center); } fn goto_prev_diag(cx: &mut Context) { @@ -2998,7 +2995,6 @@ fn goto_prev_diag(cx: &mut Context) { None => return, }; doc.set_selection(view.id, selection); - align_view(doc, view, Align::Center); } fn goto_first_change(cx: &mut Context) { From 376c19e06bedf54c8a897068f25ff7b9a8e75198 Mon Sep 17 00:00:00 2001 From: Filip Dutescu Date: Mon, 6 Mar 2023 11:19:53 +0200 Subject: [PATCH 0048/1169] feat(dap): implement Restart request (#5651) Add a restart debug session command, which would issue a [Restart Request][1], if the debugger supports it and a session is running. It uses the same arguments and requests used to start the initial session, when recreating it. It builds upon #5532, making use of the changes to the termination workflow of a session. [1]: https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Restart Closes: #5594 Signed-off-by: Filip Dutescu --- helix-dap/src/client.rs | 17 +++++++++++++++++ helix-dap/src/types.rs | 13 +++++++++++-- helix-term/src/commands.rs | 1 + helix-term/src/commands/dap.rs | 30 ++++++++++++++++++++++++++++++ helix-term/src/keymap/default.rs | 1 + 5 files changed, 60 insertions(+), 2 deletions(-) diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index f6d8a0696..ff727d00a 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -33,6 +33,7 @@ pub struct Client { server_tx: UnboundedSender, request_counter: AtomicU64, connection_type: Option, + starting_request_args: Option, pub caps: Option, // thread_id -> frames pub stack_frames: HashMap>, @@ -87,6 +88,7 @@ impl Client { request_counter: AtomicU64::new(0), caps: None, connection_type: None, + starting_request_args: None, stack_frames: HashMap::new(), thread_states: HashMap::new(), thread_id: None, @@ -158,6 +160,10 @@ impl Client { ) } + pub fn starting_request_args(&self) -> &Option { + &self.starting_request_args + } + pub async fn tcp_process( cmd: &str, args: Vec<&str>, @@ -356,14 +362,25 @@ impl Client { pub fn launch(&mut self, args: serde_json::Value) -> impl Future> { self.connection_type = Some(ConnectionType::Launch); + self.starting_request_args = Some(args.clone()); self.call::(args) } pub fn attach(&mut self, args: serde_json::Value) -> impl Future> { self.connection_type = Some(ConnectionType::Attach); + self.starting_request_args = Some(args.clone()); self.call::(args) } + pub fn restart(&self) -> impl Future> { + let args = if let Some(args) = &self.starting_request_args { + args.clone() + } else { + Value::Null + }; + self.call::(args) + } + pub async fn set_breakpoints( &self, file: PathBuf, diff --git a/helix-dap/src/types.rs b/helix-dap/src/types.rs index c598790b2..bbaf53a60 100644 --- a/helix-dap/src/types.rs +++ b/helix-dap/src/types.rs @@ -378,7 +378,7 @@ pub mod requests { impl Request for Launch { type Arguments = Value; - type Result = Value; + type Result = (); const COMMAND: &'static str = "launch"; } @@ -387,7 +387,7 @@ pub mod requests { impl Request for Attach { type Arguments = Value; - type Result = Value; + type Result = (); const COMMAND: &'static str = "attach"; } @@ -402,6 +402,15 @@ pub mod requests { pub suspend_debuggee: Option, } + #[derive(Debug)] + pub enum Restart {} + + impl Request for Restart { + type Arguments = Value; + type Result = (); + const COMMAND: &'static str = "restart"; + } + #[derive(Debug)] pub enum Disconnect {} diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index cd7626ebd..c76e9f2bd 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -430,6 +430,7 @@ impl MappableCommand { goto_next_paragraph, "Goto next paragraph", goto_prev_paragraph, "Goto previous paragraph", dap_launch, "Launch debug target", + dap_restart, "Restart debugging session", dap_toggle_breakpoint, "Toggle breakpoint", dap_continue, "Continue program execution", dap_pause, "Pause program execution", diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index 023ed3779..dac1e9d52 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -289,6 +289,36 @@ pub fn dap_launch(cx: &mut Context) { )))); } +pub fn dap_restart(cx: &mut Context) { + let debugger = match &cx.editor.debugger { + Some(debugger) => debugger, + None => { + cx.editor.set_error("Debugger is not running"); + return; + } + }; + if !debugger + .capabilities() + .supports_restart_request + .unwrap_or(false) + { + cx.editor + .set_error("Debugger does not support session restarts"); + return; + } + if debugger.starting_request_args().is_none() { + cx.editor + .set_error("No arguments found with which to restart the sessions"); + return; + } + + dap_callback( + cx.jobs, + debugger.restart(), + |editor, _compositor, _resp: ()| editor.set_status("Debugging session restarted"), + ); +} + fn debug_parameter_prompt( completions: Vec, config_name: String, diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 7425c8155..9bd002809 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -223,6 +223,7 @@ pub fn default() -> HashMap { "'" => last_picker, "g" => { "Debug (experimental)" sticky=true "l" => dap_launch, + "r" => dap_restart, "b" => dap_toggle_breakpoint, "c" => dap_continue, "h" => dap_pause, From 5ebe1014ac1dffaab19f8f9f0ebe55df3202fbf9 Mon Sep 17 00:00:00 2001 From: cinerea0 <49368915+cinerea0@users.noreply.github.com> Date: Mon, 6 Mar 2023 09:20:24 +0000 Subject: [PATCH 0049/1169] docs: describe tab-width and unit subkeys (#5862) --- book/src/languages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/languages.md b/book/src/languages.md index 0646b9af9..74d090eb7 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -56,7 +56,7 @@ These configuration keys are available: | `auto-format` | Whether to autoformat this language when saving | | `diagnostic-severity` | Minimal severity of diagnostic for it to be displayed. (Allowed values: `Error`, `Warning`, `Info`, `Hint`) | | `comment-token` | The token to use as a comment-token | -| `indent` | The indent to use. Has sub keys `tab-width` and `unit` | +| `indent` | The indent to use. Has sub keys `unit` (the text inserted into the document when indenting; usually set to N spaces or `"\t"` for tabs) and `tab-width` (the number of spaces rendered for a tab) | | `language-server` | The Language Server to run. See the Language Server configuration section below. | | `config` | Language Server configuration | | `grammar` | The tree-sitter grammar to use (defaults to the value of `name`) | From 707457c632e3b79f71d6b7ad2f62716e98080af2 Mon Sep 17 00:00:00 2001 From: David Else <12832280+David-Else@users.noreply.github.com> Date: Mon, 6 Mar 2023 09:27:17 +0000 Subject: [PATCH 0050/1169] Rewrite and refactor all documentation (#5534) * Rewrite and refactor all documentation * Rewrite and refactor the guides * update runtime directory instructions for windows * Update the Ubuntu 3rd party repo section with 22.10 * Merge from upstream * Rewrite and refactor all documentation * Apply suggestions from code review Apply the suggestions that can be committed from the GitHub web interface. Co-authored-by: Michael Davis * Add Windows themes folder Co-authored-by: digidoor <37601466+digidoor@users.noreply.github.com> * Apply the rest of the suggestions from the code review * Revert "Apply the rest of the suggestions from the code review" This reverts commit 498be1b7a1aec3ff567b95130148628beeef9b77. * Revert "Merge branch 'rewrite-and-refactor-all-documentation' of github.com:David-Else/helix into rewrite-and-refactor-all-documentation" This reverts commit 7c8404248ffef73b80b9051d5a4359c5bcfa5d1a, reversing changes made to d932969cfc9fadda12a74cc01665919dee7152fb. * Apply code review suggestions * Changes after re-reading all documents * Missed a full stop * Code review suggestions and remove macOS and Windows specific sections * Add OpenBSD to heading * Add back macOS and Windows sections and further simplify and improve * Change wording to nightly * Remove README installation section and turn into a link * Simplify building from source and follow code review suggestions * Code review revisions * Fix copy paste mistake * Apply the latest code review suggestions * More small code review items * Change minor modes for code review * Fix link and typos * Add note that you need a c++ compiler to install the tree-sitter grammars * Add pacman example * Make sure all headings are lower case * Revert to the original passage adding a reference to Windows that was missing * Update book/src/guides/adding_languages.md Fix grammar typo Co-authored-by: Michael Davis * Update book/src/install.md Fix tree sitter typo Co-authored-by: Michael Davis * Remove TOC links to main heading --------- Co-authored-by: CptPotato <3957610+CptPotato@users.noreply.github.com> Co-authored-by: Michael Davis Co-authored-by: digidoor <37601466+digidoor@users.noreply.github.com> --- README.md | 84 +-------- book/src/SUMMARY.md | 10 +- book/src/commands.md | 2 +- book/src/configuration.md | 89 +++++----- book/src/guides/README.md | 2 +- book/src/guides/adding_languages.md | 77 ++++---- book/src/guides/indent.md | 4 +- book/src/guides/textobject.md | 16 +- book/src/install.md | 261 ++++++++++++++++------------ book/src/keymap.md | 40 ++--- book/src/lang-support.md | 4 +- book/src/languages.md | 24 +-- book/src/remapping.md | 19 +- book/src/themes.md | 98 ++++++----- book/src/usage.md | 206 ++++++++++++---------- 15 files changed, 465 insertions(+), 471 deletions(-) diff --git a/README.md b/README.md index a26673219..cba52a7a1 100644 --- a/README.md +++ b/README.md @@ -45,92 +45,10 @@ Note: Only certain languages have indentation definitions at the moment. Check # Installation -Packages are available for various distributions (see [Installation docs](https://docs.helix-editor.com/install.html)). - -If you would like to build from source: - -```shell -git clone https://github.com/helix-editor/helix -cd helix -cargo install --locked --path helix-term -``` - -This will install the `hx` binary to `$HOME/.cargo/bin` and build tree-sitter grammars in `./runtime/grammars`. - -Helix needs its runtime files so make sure to copy/symlink the `runtime/` directory into the -config directory (for example `~/.config/helix/runtime` on Linux/macOS, or `%AppData%/helix/runtime` on Windows). - -| OS | Command | -| -------------------- | ------------------------------------------------ | -| Windows (Cmd) | `xcopy /e /i runtime %AppData%\helix\runtime` | -| Windows (PowerShell) | `xcopy /e /i runtime $Env:AppData\helix\runtime` | -| Linux / macOS | `ln -s $PWD/runtime ~/.config/helix/runtime` | - -Starting with Windows Vista you can also create symbolic links on Windows. Note that this requires -elevated privileges - i.e. PowerShell or Cmd must be run as administrator. - -**PowerShell:** - -```powershell -New-Item -ItemType Junction -Target "runtime" -Path "$Env:AppData\helix\runtime" -``` -Note: "runtime" must be absolute path to the runtime directory. - -**Cmd:** - -```cmd -cd %appdata%\helix -mklink /D runtime "\runtime" -``` - -The runtime location can be overridden via the `HELIX_RUNTIME` environment variable. - -> NOTE: if `HELIX_RUNTIME` is set prior to calling `cargo install --locked --path helix-term`, -> tree-sitter grammars will be built in `$HELIX_RUNTIME/grammars`. - -If you plan on keeping the repo locally, an alternative to copying/symlinking -runtime files is to set `HELIX_RUNTIME=/path/to/helix/runtime` -(`HELIX_RUNTIME=$PWD/runtime` if you're in the helix repo directory). - -Packages already solve this for you by wrapping the `hx` binary with a wrapper -that sets the variable to the install dir. - -> NOTE: running via cargo also doesn't require setting explicit `HELIX_RUNTIME` path, it will automatically -> detect the `runtime` directory in the project root. - -If you want to customize your `languages.toml` config, -tree-sitter grammars may be manually fetched and built with `hx --grammar fetch` and `hx --grammar build`. - -In order to use LSP features like auto-complete, you will need to -[install the appropriate Language Server](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers) -for a language. +[Installation documentation](https://docs.helix-editor.com/install.html). [![Packaging status](https://repology.org/badge/vertical-allrepos/helix.svg)](https://repology.org/project/helix/versions) -## Adding Helix to your desktop environment - -If installing from source, to use Helix in desktop environments that supports [XDG desktop menu](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html), including Gnome and KDE, copy the provided `.desktop` file to the correct folder: - -```bash -cp contrib/Helix.desktop ~/.local/share/applications -cp contrib/helix.png ~/.local/share/icons -``` - -To use another terminal than the default, you will need to modify the `.desktop` file. For example, to use `kitty`: - -```bash -sed -i "s|Exec=hx %F|Exec=kitty hx %F|g" ~/.local/share/applications/Helix.desktop -sed -i "s|Terminal=true|Terminal=false|g" ~/.local/share/applications/Helix.desktop -``` - -## macOS - -Helix can be installed on macOS through homebrew: - -``` -brew install helix -``` - # Contributing Contributing guidelines can be found [here](./docs/CONTRIBUTING.md). diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index eaf0c4f48..6e780b87f 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -6,13 +6,13 @@ - [Usage](./usage.md) - [Keymap](./keymap.md) - [Commands](./commands.md) - - [Language Support](./lang-support.md) + - [Language support](./lang-support.md) - [Migrating from Vim](./from-vim.md) - [Configuration](./configuration.md) - [Themes](./themes.md) - - [Key Remapping](./remapping.md) + - [Key remapping](./remapping.md) - [Languages](./languages.md) - [Guides](./guides/README.md) - - [Adding Languages](./guides/adding_languages.md) - - [Adding Textobject Queries](./guides/textobject.md) - - [Adding Indent Queries](./guides/indent.md) + - [Adding languages](./guides/adding_languages.md) + - [Adding textobject queries](./guides/textobject.md) + - [Adding indent queries](./guides/indent.md) diff --git a/book/src/commands.md b/book/src/commands.md index d9a113866..047a30a91 100644 --- a/book/src/commands.md +++ b/book/src/commands.md @@ -1,5 +1,5 @@ # Commands -Command mode can be activated by pressing `:`, similar to Vim. Built-in commands: +Command mode can be activated by pressing `:`. The built-in commands are: {{#include ./generated/typable-cmd.md}} diff --git a/book/src/configuration.md b/book/src/configuration.md index 0b9ebe966..5410024ba 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -2,10 +2,10 @@ To override global configuration parameters, create a `config.toml` file located in your config directory: -* Linux and Mac: `~/.config/helix/config.toml` -* Windows: `%AppData%\helix\config.toml` +- Linux and Mac: `~/.config/helix/config.toml` +- Windows: `%AppData%\helix\config.toml` -> Hint: You can easily open the config file by typing `:config-open` within Helix normal mode. +> 💡 You can easily open the config file by typing `:config-open` within Helix normal mode. Example config: @@ -25,12 +25,10 @@ select = "underline" hidden = false ``` -You may also specify a file to use for configuration with the `-c` or -`--config` CLI argument: `hx -c path/to/custom-config.toml`. - -It is also possible to trigger configuration file reloading by sending the `USR1` -signal to the helix process, e.g. via `pkill -USR1 hx`. This is only supported -on unix operating systems. +You can use a custom configuration file by specifying it with the `-c` or +`--config` command line argument, for example `hx -c path/to/custom-config.toml`. +Additionally, you can reload the configuration file by sending the USR1 +signal to the Helix process on Unix operating systems, such as by using the command `pkill -USR1 hx`. ## Editor @@ -38,23 +36,23 @@ on unix operating systems. | Key | Description | Default | |--|--|---------| -| `scrolloff` | Number of lines of padding around the edge of the screen when scrolling. | `5` | -| `mouse` | Enable mouse mode. | `true` | -| `middle-click-paste` | Middle click paste support. | `true` | -| `scroll-lines` | Number of lines to scroll per scroll wheel step. | `3` | -| `shell` | Shell to use when running external commands. | Unix: `["sh", "-c"]`
Windows: `["cmd", "/C"]` | -| `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers. | `absolute` | -| `cursorline` | Highlight all lines with a cursor. | `false` | -| `cursorcolumn` | Highlight all columns with a cursor. | `false` | +| `scrolloff` | Number of lines of padding around the edge of the screen when scrolling | `5` | +| `mouse` | Enable mouse mode | `true` | +| `middle-click-paste` | Middle click paste support | `true` | +| `scroll-lines` | Number of lines to scroll per scroll wheel step | `3` | +| `shell` | Shell to use when running external commands | Unix: `["sh", "-c"]`
Windows: `["cmd", "/C"]` | +| `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers | `absolute` | +| `cursorline` | Highlight all lines with a cursor | `false` | +| `cursorcolumn` | Highlight all columns with a cursor | `false` | | `gutters` | Gutters to display: Available are `diagnostics` and `diff` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` | -| `auto-completion` | Enable automatic pop up of auto-completion. | `true` | -| `auto-format` | Enable automatic formatting on save. | `true` | -| `auto-save` | Enable automatic saving on focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal. | `false` | -| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` | +| `auto-completion` | Enable automatic pop up of auto-completion | `true` | +| `auto-format` | Enable automatic formatting on save | `true` | +| `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | +| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant | `400` | | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | -| `auto-info` | Whether to display infoboxes | `true` | -| `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` | -| `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` | +| `auto-info` | Whether to display info boxes | `true` | +| `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative | `false` | +| `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file | `[]` | | `bufferline` | Renders a line at the top of the editor displaying open buffers. Can be `always`, `never` or `multiple` (only shown if more than one buffer is in use) | `never` | | `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` | @@ -125,10 +123,12 @@ The following statusline elements can be configured: ### `[editor.cursor-shape]` Section -Defines the shape of cursor in each mode. Note that due to limitations -of the terminal environment, only the primary cursor can change shape. +Defines the shape of cursor in each mode. Valid values for these options are `block`, `bar`, `underline`, or `hidden`. +> 💡 Due to limitations of the terminal environment, only the primary cursor can +> change shape. + | Key | Description | Default | | --- | ----------- | ------- | | `normal` | Cursor shape in [normal mode][normal mode] | `block` | @@ -141,25 +141,22 @@ Valid values for these options are `block`, `bar`, `underline`, or `hidden`. ### `[editor.file-picker]` Section -Sets options for file picker and global search. All but the last key listed in -the default file-picker configuration below are IgnoreOptions: whether hidden -files and files listed within ignore files are ignored by (not visible in) the -helix file picker and global search. There is also one other key, `max-depth` -available, which is not defined by default. +Set options for file picker and global search. Ignoring a file means it is +not visible in the Helix file picker and global search. All git related options are only enabled in a git repository. | Key | Description | Default | |--|--|---------| -|`hidden` | Enables ignoring hidden files. | true +|`hidden` | Enables ignoring hidden files | true |`follow-links` | Follow symlinks instead of ignoring them | true |`deduplicate-links` | Ignore symlinks that point at files already shown in the picker | true -|`parents` | Enables reading ignore files from parent directories. | true -|`ignore` | Enables reading `.ignore` files. | true -|`git-ignore` | Enables reading `.gitignore` files. | true -|`git-global` | Enables reading global .gitignore, whose path is specified in git's config: `core.excludefile` option. | true -|`git-exclude` | Enables reading `.git/info/exclude` files. | true -|`max-depth` | Set with an integer value for maximum depth to recurse. | Defaults to `None`. +|`parents` | Enables reading ignore files from parent directories | true +|`ignore` | Enables reading `.ignore` files | true +|`git-ignore` | Enables reading `.gitignore` files | true +|`git-global` | Enables reading global `.gitignore`, whose path is specified in git's config: `core.excludefile` option | true +|`git-exclude` | Enables reading `.git/info/exclude` files | true +|`max-depth` | Set with an integer value for maximum depth to recurse | Defaults to `None`. ### `[editor.auto-pairs]` Section @@ -211,7 +208,7 @@ Search specific options. | Key | Description | Default | |--|--|---------| -| `smart-case` | Enable smart case regex searching (case insensitive unless pattern contains upper case characters) | `true` | +| `smart-case` | Enable smart case regex searching (case-insensitive unless pattern contains upper case characters) | `true` | | `wrap-around`| Whether the search should wrap after depleting the matches | `true` | ### `[editor.whitespace]` Section @@ -220,7 +217,7 @@ Options for rendering whitespace with visible characters. Use `:set whitespace.r | Key | Description | Default | |-----|-------------|---------| -| `render` | Whether to render whitespace. May either be `"all"` or `"none"`, or a table with sub-keys `space`, `nbsp`, `tab`, and `newline`. | `"none"` | +| `render` | Whether to render whitespace. May either be `"all"` or `"none"`, or a table with sub-keys `space`, `nbsp`, `tab`, and `newline` | `"none"` | | `characters` | Literal characters to use when rendering whitespace. Sub-keys may be any of `tab`, `space`, `nbsp`, `newline` or `tabpad` | See example below | Example @@ -248,7 +245,7 @@ Options for rendering vertical indent guides. | Key | Description | Default | | --- | --- | --- | -| `render` | Whether to render indent guides. | `false` | +| `render` | Whether to render indent guides | `false` | | `character` | Literal character to use for rendering the indent guide | `│` | | `skip-levels` | Number of indent levels to skip | `0` | @@ -273,7 +270,7 @@ gutters = ["diff", "diagnostics", "line-numbers", "spacer"] To customize the behavior of gutters, the `[editor.gutters]` section must be used. This section contains top level settings, as well as settings for -specific gutter components as sub-sections. +specific gutter components as subsections. | Key | Description | Default | | --- | --- | --- | @@ -315,13 +312,13 @@ Currently unused ### `[editor.soft-wrap]` Section -Options for soft wrapping lines that exceed the view width +Options for soft wrapping lines that exceed the view width: | Key | Description | Default | | --- | --- | --- | -| `enable` | Whether soft wrapping is enabled. | `false` | -| `max-wrap` | Maximum free space left at the end of the line. | `20` | -| `max-indent-retain` | Maximum indentation to carry over when soft wrapping a line. | `40` | +| `enable` | Whether soft wrapping is enabled | `false` | +| `max-wrap` | Maximum free space left at the end of the line | `20` | +| `max-indent-retain` | Maximum indentation to carry over when soft wrapping a line | `40` | | `wrap-indicator` | Text inserted before soft wrapped lines, highlighted with `ui.virtual.wrap` | `↪ ` | Example: diff --git a/book/src/guides/README.md b/book/src/guides/README.md index e0c44ce7d..c25768e68 100644 --- a/book/src/guides/README.md +++ b/book/src/guides/README.md @@ -1,4 +1,4 @@ # Guides This section contains guides for adding new language server configurations, -tree-sitter grammars, textobject queries, etc. +tree-sitter grammars, textobject queries, and other similar items. diff --git a/book/src/guides/adding_languages.md b/book/src/guides/adding_languages.md index 6598b9bf7..b92af4028 100644 --- a/book/src/guides/adding_languages.md +++ b/book/src/guides/adding_languages.md @@ -1,45 +1,52 @@ -# Adding languages +# Adding new languages to Helix + +In order to add a new language to Helix, you will need to follow the steps +below. ## Language configuration -To add a new language, you need to add a `[[language]]` entry to the -`languages.toml` (see the [language configuration section]). +1. Add a new `[[language]]` entry in the `languages.toml` file and provide the + necessary configuration for the new language. For more information on + language configuration, refer to the + [language configuration section](../languages.md) of the documentation. +2. If you are adding a new language or updating an existing language server + configuration, run the command `cargo xtask docgen` to update the + [Language Support](../lang-support.md) documentation. -When adding a new language or Language Server configuration for an existing -language, run `cargo xtask docgen` to add the new configuration to the -[Language Support][lang-support] docs before creating a pull request. -When adding a Language Server configuration, be sure to update the -[Language Server Wiki][install-lsp-wiki] with installation notes. +> 💡 If you are adding a new Language Server configuration, make sure to update +> the +> [Language Server Wiki](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers) +> with the installation instructions. ## Grammar configuration -If a tree-sitter grammar is available for the language, add a new `[[grammar]]` -entry to `languages.toml`. - -You may use the `source.path` key rather than `source.git` with an absolute path -to a locally available grammar for testing, but switch to `source.git` before -submitting a pull request. +1. If a tree-sitter grammar is available for the new language, add a new + `[[grammar]]` entry to the `languages.toml` file. +2. If you are testing the grammar locally, you can use the `source.path` key + with an absolute path to the grammar. However, before submitting a pull + request, make sure to switch to using `source.git`. ## Queries -For a language to have syntax-highlighting and indentation among -other things, you have to add queries. Add a directory for your -language with the path `runtime/queries//`. The tree-sitter -[website](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries) -gives more info on how to write queries. - -> NOTE: When evaluating queries, the first matching query takes -precedence, which is different from other editors like Neovim where -the last matching query supersedes the ones before it. See -[this issue][neovim-query-precedence] for an example. - -## Common Issues - -- If you get errors when running after switching branches, you may have to update the tree-sitter grammars. Run `hx --grammar fetch` to fetch the grammars and `hx --grammar build` to build any out-of-date grammars. - -- If a parser is segfaulting or you want to remove the parser, make sure to remove the compiled parser in `runtime/grammar/.so` - -[language configuration section]: ../languages.md -[neovim-query-precedence]: https://github.com/helix-editor/helix/pull/1170#issuecomment-997294090 -[install-lsp-wiki]: https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers -[lang-support]: ../lang-support.md +1. In order to provide syntax highlighting and indentation for the new language, + you will need to add queries. +2. Create a new directory for the language with the path + `runtime/queries//`. +3. Refer to the + [tree-sitter website](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries) + for more information on writing queries. + +> 💡 In Helix, the first matching query takes precedence when evaluating +> queries, which is different from other editors such as Neovim where the last +> matching query supersedes the ones before it. See +> [this issue](https://github.com/helix-editor/helix/pull/1170#issuecomment-997294090) +> for an example. + +## Common issues + +- If you encounter errors when running Helix after switching branches, you may + need to update the tree-sitter grammars. Run the command `hx --grammar fetch` + to fetch the grammars and `hx --grammar build` to build any out-of-date + grammars. +- If a parser is causing a segfault, or you want to remove it, make sure to + remove the compiled parser located at `runtime/grammars/.so`. diff --git a/book/src/guides/indent.md b/book/src/guides/indent.md index 0e2592897..b660d7857 100644 --- a/book/src/guides/indent.md +++ b/book/src/guides/indent.md @@ -1,4 +1,4 @@ -# Adding Indent Queries +# Adding indent queries Helix uses tree-sitter to correctly indent new lines. This requires a tree-sitter grammar and an `indent.scm` query file placed in @@ -36,7 +36,7 @@ changed by using a `#set!` declaration anywhere in the pattern: (#set! "scope" "all")) ``` -## Capture Types +## Capture types - `@indent` (default scope `tail`): Increase the indent level by 1. Multiple occurrences in the same line diff --git a/book/src/guides/textobject.md b/book/src/guides/textobject.md index 8a2173547..405f11c1b 100644 --- a/book/src/guides/textobject.md +++ b/book/src/guides/textobject.md @@ -1,14 +1,14 @@ -# Adding Textobject Queries +# Adding textobject queries -Textobjects that are language specific ([like functions, classes, etc][textobjects]) -require an accompanying tree-sitter grammar and a `textobjects.scm` query file +Helix supports textobjects that are language specific, such as functions, classes, etc. +These textobjects require an accompanying tree-sitter grammar and a `textobjects.scm` query file to work properly. Tree-sitter allows us to query the source code syntax tree and capture specific parts of it. The queries are written in a lisp dialect. More information on how to write queries can be found in the [official tree-sitter documentation][tree-sitter-queries]. Query files should be placed in `runtime/queries/{language}/textobjects.scm` -when contributing. Note that to test the query files locally you should put +when contributing to Helix. Note that to test the query files locally you should put them under your local runtime directory (`~/.config/helix/runtime` on Linux for example). @@ -28,9 +28,9 @@ The following [captures][tree-sitter-captures] are recognized: [Example query files][textobject-examples] can be found in the helix GitHub repository. -## Queries for Textobject Based Navigation +## Queries for textobject based navigation -[Tree-sitter based navigation][textobjects-nav] is done using captures in the +Tree-sitter based navigation in Helix is done using captures in the following order: - `object.movement` @@ -38,12 +38,10 @@ following order: - `object.inside` For example if a `function.around` capture has been already defined for a language -in it's `textobjects.scm` file, function navigation should also work automatically. +in its `textobjects.scm` file, function navigation should also work automatically. `function.movement` should be defined only if the node captured by `function.around` doesn't make sense in a navigation context. -[textobjects]: ../usage.md#textobjects -[textobjects-nav]: ../usage.md#tree-sitter-textobject-based-navigation [tree-sitter-queries]: https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax [tree-sitter-captures]: https://tree-sitter.github.io/tree-sitter/using-parsers#capturing-nodes [textobject-examples]: https://github.com/search?q=repo%3Ahelix-editor%2Fhelix+filename%3Atextobjects.scm&type=Code&ref=advsearch&l=&l= diff --git a/book/src/install.md b/book/src/install.md index 7df9e6c77..f9cf9a3ba 100644 --- a/book/src/install.md +++ b/book/src/install.md @@ -1,180 +1,223 @@ -# Installation - -We provide pre-built binaries on the [GitHub Releases page](https://github.com/helix-editor/helix/releases). +# Installing Helix + + +- [Pre-built binaries](#pre-built-binaries) +- [Linux, macOS, Windows and OpenBSD packaging status](#linux-macos-windows-and-openbsd-packaging-status) +- [Linux](#linux) + - [Ubuntu](#ubuntu) + - [Fedora/RHEL](#fedorarhel) + - [Arch Linux community](#arch-linux-community) + - [NixOS](#nixos) +- [macOS](#macos) + - [Homebrew Core](#homebrew-core) +- [Windows](#windows) + - [Scoop](#scoop) + - [Chocolatey](#chocolatey) + - [MSYS2](#msys2) +- [Building from source](#building-from-source) + - [Configuring Helix's runtime files](#configuring-helixs-runtime-files) + - [Validating the installation](#validating-the-installation) + - [Configure the desktop shortcut](#configure-the-desktop-shortcut) + + +To install Helix, follow the instructions specific to your operating system. +Note that: + +- To get the latest nightly version of Helix, you need to + [build from source](#building-from-source). + +- To take full advantage of Helix, install the language servers for your + preferred programming languages. See the + [wiki](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers) + for instructions. + +## Pre-built binaries + +Download pre-built binaries from the +[GitHub Releases page](https://github.com/helix-editor/helix/releases). Add the binary to your system's `$PATH` to use it from the command +line. + +## Linux, macOS, Windows and OpenBSD packaging status + +Helix is available for Linux, macOS and Windows via the official repositories listed below. [![Packaging status](https://repology.org/badge/vertical-allrepos/helix.svg)](https://repology.org/project/helix/versions) -## OSX +## Linux -Helix is available in homebrew-core: +The following third party repositories are available: -``` -brew install helix -``` +### Ubuntu -## Linux +Helix is available via [Maveonair's PPA](https://launchpad.net/~maveonair/+archive/ubuntu/helix-editor): -### NixOS +```sh +sudo add-apt-repository ppa:maveonair/helix-editor +sudo apt update +sudo apt install helix +``` -A [flake](https://nixos.wiki/wiki/Flakes) containing the package is available in -the project root. The flake can also be used to spin up a reproducible development -shell for working on Helix with `nix develop`. +### Fedora/RHEL -Flake outputs are cached for each push to master using -[Cachix](https://www.cachix.org/). The flake is configured to -automatically make use of this cache assuming the user accepts -the new settings on first use. +Helix is available via `copr`: -If you are using a version of Nix without flakes enabled you can -[install Cachix cli](https://docs.cachix.org/installation); `cachix use helix` will -configure Nix to use cached outputs when possible. +```sh +sudo dnf copr enable varlad/helix +sudo dnf install helix +``` -### Arch Linux +### Arch Linux community -Releases are available in the `community` repository. +Releases are available in the `community` repository: -A [helix-git](https://aur.archlinux.org/packages/helix-git/) package is also available on the AUR, which builds the master branch. +```sh +sudo pacman -S helix +``` +Additionally, a [helix-git](https://aur.archlinux.org/packages/helix-git/) package is available +in the AUR, which builds the master branch. -### Fedora Linux +### NixOS -You can install the COPR package for Helix via +Helix is available as a [flake](https://nixos.wiki/wiki/Flakes) in the project +root. Use `nix develop` to spin up a reproducible development shell. Outputs are +cached for each push to master using [Cachix](https://www.cachix.org/). The +flake is configured to automatically make use of this cache assuming the user +accepts the new settings on first use. -``` -sudo dnf copr enable varlad/helix -sudo dnf install helix -``` +If you are using a version of Nix without flakes enabled, +[install Cachix CLI](https://docs.cachix.org/installation) and use +`cachix use helix` to configure Nix to use cached outputs when possible. + +## macOS -### Void Linux +### Homebrew Core -``` -sudo xbps-install helix +```sh +brew install helix ``` ## Windows -Helix can be installed using [Scoop](https://scoop.sh/), [Chocolatey](https://chocolatey.org/) +Install on Windows using [Scoop](https://scoop.sh/), [Chocolatey](https://chocolatey.org/) or [MSYS2](https://msys2.org/). -**Scoop:** +### Scoop -``` +```sh scoop install helix ``` -**Chocolatey:** +### Chocolatey -``` +```sh choco install helix ``` -**MSYS2:** - -Choose the [proper command](https://www.msys2.org/docs/package-naming/) for your system from below: - - - For 32 bit Windows 7 or above: - -``` -pacman -S mingw-w64-i686-helix -``` - - - For 64 bit Windows 7 or above: - -``` -pacman -S mingw-w64-x86_64-helix -``` +### MSYS2 - - For 64 bit Windows 8.1 or above: +For 64-bit Windows 8.1 or above: -``` +```sh pacman -S mingw-w64-ucrt-x86_64-helix ``` -## Build from source +## Building from source -``` +Clone the repository: + +```sh git clone https://github.com/helix-editor/helix cd helix -cargo install --path helix-term --locked ``` -This will install the `hx` binary to `$HOME/.cargo/bin` and build tree-sitter grammars in `./runtime/grammars`. - -If you are using the musl-libc instead of glibc the following environment variable must be set during the build -to ensure tree sitter grammars can be loaded correctly: +Compile from source: +```sh +cargo install --path helix-term --locked ``` -RUSTFLAGS="-C target-feature=-crt-static" -``` - -Helix also needs its runtime files so make sure to copy/symlink the `runtime/` directory into the -config directory (for example `~/.config/helix/runtime` on Linux/macOS). This location can be overridden -via the `HELIX_RUNTIME` environment variable. +This command will create the `hx` executable and construct the tree-sitter +grammars either in the `runtime` folder, or in the folder specified in `HELIX_RUNTIME` +(as described below). To build the tree-sitter grammars requires a c++ compiler to be installed, for example `gcc-c++`. -| OS | Command | -| -------------------- | ------------------------------------------------ | -| Windows (Cmd) | `xcopy /e /i runtime %AppData%\helix\runtime` | -| Windows (PowerShell) | `xcopy /e /i runtime $Env:AppData\helix\runtime` | -| Linux / macOS | `ln -s $PWD/runtime ~/.config/helix/runtime` | +> 💡 If you are using the musl-libc instead of glibc the following environment variable must be set during the build +> to ensure tree-sitter grammars can be loaded correctly: +> +> ```sh +> RUSTFLAGS="-C target-feature=-crt-static" +> ``` -Starting with Windows Vista you can also create symbolic links on Windows. Note that this requires -elevated privileges - i.e. PowerShell or Cmd must be run as administrator. +> 💡 Tree-sitter grammars can be fetched and compiled if not pre-packaged. Fetch +> grammars with `hx --grammar fetch` (requires `git`) and compile them with +> `hx --grammar build` (requires a C++ compiler). -**PowerShell:** +### Configuring Helix's runtime files -```powershell -New-Item -ItemType Junction -Target "runtime" -Path "$Env:AppData\helix\runtime" -``` -Note: "runtime" must be the absolute path to the runtime directory. +- **Linux and macOS** -**Cmd:** +Either set the `HELIX_RUNTIME` environment variable to point to the runtime files and add it to your `~/.bashrc` or equivalent: -```cmd -cd %appdata%\helix -mklink /D runtime "\runtime" +```sh +HELIX_RUNTIME=/home/user-name/src/helix/runtime ``` -The runtime location can be overridden via the `HELIX_RUNTIME` environment variable. +Or, create a symlink in `~/.config/helix` that links to the source code directory: -> NOTE: if `HELIX_RUNTIME` is set prior to calling `cargo install --path helix-term --locked`, -> tree-sitter grammars will be built in `$HELIX_RUNTIME/grammars`. +```sh +ln -s $PWD/runtime ~/.config/helix/runtime +``` -If you plan on keeping the repo locally, an alternative to copying/symlinking -runtime files is to set `HELIX_RUNTIME=/path/to/helix/runtime` -(`HELIX_RUNTIME=$PWD/runtime` if you're in the helix repo directory). +- **Windows** -To use Helix in desktop environments that supports [XDG desktop menu](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html), including Gnome and KDE, copy the provided `.desktop` file to the correct folder: +Either set the `HELIX_RUNTIME` environment variable to point to the runtime files using the Windows setting (search for +`Edit environment variables for your account`) or use the `setx` command in +Cmd: -```bash -cp contrib/Helix.desktop ~/.local/share/applications +```sh +setx HELIX_RUNTIME "%userprofile%\source\repos\helix\runtime" ``` -To use another terminal than the default, you will need to modify the `.desktop` file. For example, to use `kitty`: +> 💡 `%userprofile%` resolves to your user directory like +> `C:\Users\Your-Name\` for example. -```bash -sed -i "s|Exec=hx %F|Exec=kitty hx %F|g" ~/.local/share/applications/Helix.desktop -sed -i "s|Terminal=true|Terminal=false|g" ~/.local/share/applications/Helix.desktop -``` +Or, create a symlink in `%appdata%\helix\` that links to the source code directory: -Please note: there is no icon for Helix yet, so the system default will be used. + | Method | Command | + | ---------- | -------------------------------------------------------------------------------------- | + | PowerShell | `New-Item -ItemType Junction -Target "runtime" -Path "$Env:AppData\helix\runtime"` | + | Cmd | `cd %appdata%\helix`
`mklink /D runtime "%userprofile%\src\helix\runtime"` | -## Finishing up the installation + > 💡 On Windows, creating a symbolic link may require running PowerShell or + > Cmd as an administrator. -To make sure everything is set up as expected you should finally run the helix healthcheck via +### Validating the installation -``` +To make sure everything is set up as expected you should run the Helix health +check: + +```sh hx --health ``` -For more information on the information displayed in the health check results refer to [Healthcheck](https://github.com/helix-editor/helix/wiki/Healthcheck). +For more information on the health check results refer to +[Health check](https://github.com/helix-editor/helix/wiki/Healthcheck). -### Building tree-sitter grammars +### Configure the desktop shortcut -Tree-sitter grammars must be fetched and compiled if not pre-packaged. -Fetch grammars with `hx --grammar fetch` (requires `git`) and compile them -with `hx --grammar build` (requires a C++ compiler). +If your desktop environment supports the +[XDG desktop menu](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html) +you can configure Helix to show up in the application menu by copying the +provided `.desktop` and icon files to their correct folders: -### Installing language servers +```sh +cp contrib/Helix.desktop ~/.local/share/applications +cp contrib/helix.png ~/.icons # or ~/.local/share/icons +``` + +To use another terminal than the system default, you can modify the `.desktop` +file. For example, to use `kitty`: -Language servers can optionally be installed if you want their features (auto-complete, diagnostics etc.). -Follow the [instructions on the wiki page](https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers) to add your language servers of choice. +```sh +sed -i "s|Exec=hx %F|Exec=kitty hx %F|g" ~/.local/share/applications/Helix.desktop +sed -i "s|Terminal=true|Terminal=false|g" ~/.local/share/applications/Helix.desktop +``` diff --git a/book/src/keymap.md b/book/src/keymap.md index bc16aa1a7..173728f27 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -14,14 +14,14 @@ - [Space mode](#space-mode) - [Popup](#popup) - [Unimpaired](#unimpaired) -- [Insert Mode](#insert-mode) -- [Select / extend mode](#select--extend-mode) +- [Insert mode](#insert-mode) +- [Select / extend mode](#select-extend-mode) - [Picker](#picker) - [Prompt](#prompt) > 💡 Mappings marked (**LSP**) require an active language server for the file. -> 💡 Mappings marked (**TS**) require a tree-sitter grammar for the filetype. +> 💡 Mappings marked (**TS**) require a tree-sitter grammar for the file type. ## Normal mode @@ -109,7 +109,7 @@ | Key | Description | Command | | ----- | ----------- | ------- | | `s` | Select all regex matches inside selections | `select_regex` | -| `S` | Split selection into subselections on regex matches | `split_selection` | +| `S` | Split selection into sub selections on regex matches | `split_selection` | | `Alt-s` | Split selection on newlines | `split_selection_on_newline` | | `Alt-_ ` | Merge consecutive selections | `merge_consecutive_selections` | | `&` | Align selection in columns | `align_selections` | @@ -141,7 +141,7 @@ ### Search -Search commands all operate on the `/` register by default. Use `"` to operate on a different one. +Search commands all operate on the `/` register by default. To use a different register, use `"`. | Key | Description | Command | | ----- | ----------- | ------- | @@ -175,9 +175,8 @@ Accessed by typing `z` in [normal mode](#normal-mode). View mode is intended for scrolling and manipulating the view without changing the selection. The "sticky" variant of this mode (accessed by typing `Z` in -normal mode) is persistent; use the Escape key to return to normal mode after -usage (useful when you're simply looking over text and not actively editing -it). +normal mode) is persistent and can be exited using the escape key. This is +useful when you're simply looking over text and not actively editing it. | Key | Description | Command | @@ -225,7 +224,7 @@ Jumps to various locations. Accessed by typing `m` in [normal mode](#normal-mode). See the relevant section in [Usage](./usage.md) for an explanation about -[surround](./usage.md#surround) and [textobject](./usage.md#textobjects) usage. +[surround](./usage.md#surround) and [textobject](./usage.md#navigating-using-tree-sitter-textobjects) usage. | Key | Description | Command | | ----- | ----------- | ------- | @@ -242,7 +241,7 @@ TODO: Mappings for selecting syntax nodes (a superset of `[`). Accessed by typing `Ctrl-w` in [normal mode](#normal-mode). -This layer is similar to Vim keybindings as Kakoune does not support window. +This layer is similar to Vim keybindings as Kakoune does not support windows. | Key | Description | Command | | ----- | ------------- | ------- | @@ -293,7 +292,7 @@ This layer is a kludge of mappings, mostly pickers. | `/` | Global search in workspace folder | `global_search` | | `?` | Open command palette | `command_palette` | -> TIP: Global search displays results in a fuzzy picker, use `Space + '` to bring it back up after opening a file. +> 💡 Global search displays results in a fuzzy picker, use `Space + '` to bring it back up after opening a file. ##### Popup @@ -306,7 +305,7 @@ Displays documentation for item under cursor. #### Unimpaired -Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaired). +These mappings are in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaired). | Key | Description | Command | | ----- | ----------- | ------- | @@ -335,12 +334,13 @@ Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaire ## Insert mode -Insert mode bindings are somewhat minimal by default. Helix is designed to +Insert mode bindings are minimal by default. Helix is designed to be a modal editor, and this is reflected in the user experience and internal -mechanics. For example, changes to the text are only saved for undos when -escaping from insert mode to normal mode. For this reason, new users are -strongly encouraged to learn the modal editing paradigm to get the smoothest -experience. +mechanics. Changes to the text are only saved for undos when +escaping from insert mode to normal mode. + +> 💡 New users are strongly encouraged to learn the modal editing paradigm +> to get the smoothest experience. | Key | Description | Command | | ----- | ----------- | ------- | @@ -370,8 +370,8 @@ with modal editors. | `Home` | Move to line start | `goto_line_start` | | `End` | Move to line end | `goto_line_end_newline` | -If you want to disable them in insert mode as you become more comfortable with modal editing, you can use -the following in your `config.toml`: +As you become more comfortable with modal editing, you may want to disable some +insert mode bindings. You can do this by editing your `config.toml` file. ```toml [keys.insert] @@ -387,7 +387,7 @@ end = "no_op" ## Select / extend mode -This mode echoes Normal mode, but changes any movements to extend +Select mode echoes Normal mode, but changes any movements to extend selections rather than replace them. Goto motions are also changed to extend, so that `vgl` for example extends the selection to the end of the line. diff --git a/book/src/lang-support.md b/book/src/lang-support.md index 6a08cd699..3f96673bc 100644 --- a/book/src/lang-support.md +++ b/book/src/lang-support.md @@ -1,10 +1,10 @@ # Language Support -The following languages and Language Servers are supported. In order to use +The following languages and Language Servers are supported. To use Language Server features, you must first [install][lsp-install-wiki] the appropriate Language Server. -Check the language support in your installed helix version with `hx --health`. +You can check the language support in your installed helix version with `hx --health`. Also see the [Language Configuration][lang-config] docs and the [Adding Languages][adding-languages] guide for more language configuration information. diff --git a/book/src/languages.md b/book/src/languages.md index 74d090eb7..8a8f3bb61 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -5,13 +5,15 @@ in `languages.toml` files. ## `languages.toml` files -There are three possible `languages.toml` files. The first is compiled into -Helix and lives in the [Helix repository](https://github.com/helix-editor/helix/blob/master/languages.toml). -This provides the default configurations for languages and language servers. +There are three possible locations for a `languages.toml` file: -You may define a `languages.toml` in your [configuration directory](./configuration.md) -which overrides values from the built-in language configuration. For example -to disable auto-LSP-formatting in Rust: +1. In the Helix source code, this lives in the + [Helix repository](https://github.com/helix-editor/helix/blob/master/languages.toml). + It provides the default configurations for languages and language servers. + +2. In your [configuration directory](./configuration.md). This overrides values + from the built-in language configuration. For example to disable + auto-LSP-formatting in Rust: ```toml # in /helix/languages.toml @@ -21,10 +23,10 @@ name = "rust" auto-format = false ``` -Language configuration may also be overridden local to a project by creating -a `languages.toml` file under a `.helix` directory. Its settings will be merged -with the language configuration in the configuration directory and the built-in -configuration. +3. In a `.helix` folder in your project. Language configuration may also be + overridden local to a project by creating a `languages.toml` file in a + `.helix` folder. Its settings will be merged with the language configuration + in the configuration directory and the built-in configuration. ## Language configuration @@ -65,7 +67,7 @@ These configuration keys are available: ### File-type detection and the `file-types` key -Helix determines which language configuration to use with the `file-types` key +Helix determines which language configuration to use based on the `file-types` key from the above section. `file-types` is a list of strings or tables, for example: diff --git a/book/src/remapping.md b/book/src/remapping.md index 8339e05fc..d762c6add 100644 --- a/book/src/remapping.md +++ b/book/src/remapping.md @@ -1,18 +1,18 @@ -# Key Remapping +# Key remapping -One-way key remapping is temporarily supported via a simple TOML configuration +Helix currently supports one-way key remapping through a simple TOML configuration file. (More powerful solutions such as rebinding via commands will be available in the future). -To remap keys, write a `config.toml` file in your `helix` configuration -directory (default `~/.config/helix` in Linux systems) with a structure like +To remap keys, create a `config.toml` file in your `helix` configuration +directory (default `~/.config/helix` on Linux systems) with a structure like this: ```toml # At most one section each of 'keys.normal', 'keys.insert' and 'keys.select' [keys.normal] -C-s = ":w" # Maps the Ctrl-s to the typable command :w which is an alias for :write (save file) -C-o = ":open ~/.config/helix/config.toml" # Maps the Ctrl-o to opening of the helix config file +C-s = ":w" # Maps Ctrl-s to the typable command :w which is an alias for :write (save file) +C-o = ":open ~/.config/helix/config.toml" # Maps Ctrl-o to opening of the helix config file a = "move_char_left" # Maps the 'a' key to the move_char_left command w = "move_line_up" # Maps the 'w' key move_line_up "C-S-esc" = "extend_line" # Maps Ctrl-Shift-Escape to extend_line @@ -20,10 +20,9 @@ g = { a = "code_action" } # Maps `ga` to show possible code actions "ret" = ["open_below", "normal_mode"] # Maps the enter key to open_below then re-enter normal mode [keys.insert] -"A-x" = "normal_mode" # Maps Alt-X to enter normal mode +"A-x" = "normal_mode" # Maps Alt-X to enter normal mode j = { k = "normal_mode" } # Maps `jk` to exit insert mode ``` -> NOTE: Typable commands can also be remapped, remember to keep the `:` prefix to indicate it's a typable command. ## Minor modes @@ -76,5 +75,5 @@ Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes Keys can be disabled by binding them to the `no_op` command. -Commands can be found at [Keymap](https://docs.helix-editor.com/keymap.html) Commands. -> Commands can also be found in the source code at [`helix-term/src/commands.rs`](https://github.com/helix-editor/helix/blob/master/helix-term/src/commands.rs) at the invocation of `static_commands!` macro and the `TypableCommandList`. +A list of commands is available in the [Keymap](https://docs.helix-editor.com/keymap.html) documentation + and in the source code at [`helix-term/src/commands.rs`](https://github.com/helix-editor/helix/blob/master/helix-term/src/commands.rs) at the invocation of `static_commands!` macro and the `TypableCommandList`. diff --git a/book/src/themes.md b/book/src/themes.md index 9b7e97a1c..af238e949 100644 --- a/book/src/themes.md +++ b/book/src/themes.md @@ -1,14 +1,15 @@ # Themes -To use a theme add `theme = ""` to your [`config.toml`](./configuration.md) at the very top of the file before the first section or select it during runtime using `:theme `. +To use a theme add `theme = ""` to the top of your [`config.toml`](./configuration.md) file, or select it during runtime using `:theme `. ## Creating a theme -Create a file with the name of your theme as file name (i.e `mytheme.toml`) and place it in your `themes` directory (i.e `~/.config/helix/themes`). The directory might have to be created beforehand. +Create a file with the name of your theme as the file name (i.e `mytheme.toml`) and place it in your `themes` directory (i.e `~/.config/helix/themes` or `%AppData%\helix\themes` on Windows). The directory might have to be created beforehand. -The names "default" and "base16_default" are reserved for the builtin themes and cannot be overridden by user defined themes. +> 💡 The names "default" and "base16_default" are reserved for built-in themes +> and cannot be overridden by user-defined themes. -The default theme.toml can be found [here](https://github.com/helix-editor/helix/blob/master/theme.toml), and user submitted themes [here](https://github.com/helix-editor/helix/blob/master/runtime/themes). +### Overview Each line in the theme file is specified as below: @@ -16,7 +17,7 @@ Each line in the theme file is specified as below: key = { fg = "#ffffff", bg = "#000000", underline = { color = "#ff0000", style = "curl"}, modifiers = ["bold", "italic"] } ``` -where `key` represents what you want to style, `fg` specifies the foreground color, `bg` the background color, `underline` the underline `style`/`color`, and `modifiers` is a list of style modifiers. `bg`, `underline` and `modifiers` can be omitted to defer to the defaults. +Where `key` represents what you want to style, `fg` specifies the foreground color, `bg` the background color, `underline` the underline `style`/`color`, and `modifiers` is a list of style modifiers. `bg`, `underline` and `modifiers` can be omitted to defer to the defaults. To specify only the foreground color: @@ -24,15 +25,30 @@ To specify only the foreground color: key = "#ffffff" ``` -if the key contains a dot `'.'`, it must be quoted to prevent it being parsed as a [dotted key](https://toml.io/en/v1.0.0#keys). +If the key contains a dot `'.'`, it must be quoted to prevent it being parsed as a [dotted key](https://toml.io/en/v1.0.0#keys). ```toml "key.key" = "#ffffff" ``` +For inspiration, you can find the default `theme.toml` +[here](https://github.com/helix-editor/helix/blob/master/theme.toml) and +user-submitted themes +[here](https://github.com/helix-editor/helix/blob/master/runtime/themes). + +### Using the linter + +Use the supplied linting tool to check for errors and missing scopes: + +```sh +cargo xtask themelint onedark # replace onedark with +``` + +## The details of theme creation + ### Color palettes -It's recommended define a palette of named colors, and refer to them from the +It's recommended to define a palette of named colors, and refer to them in the configuration values in your theme. To do this, add a table called `palette` to your theme file: @@ -45,8 +61,8 @@ white = "#ffffff" black = "#000000" ``` -Remember that the `[palette]` table includes all keys after its header, -so you should define the palette after normal theme options. +Keep in mind that the `[palette]` table includes all keys after its header, +so it should be defined after the normal theme options. The default palette uses the terminal's default 16 colors, and the colors names are listed below. The `[palette]` section in the config file takes precedence @@ -73,9 +89,8 @@ over it and is merged into the default palette. ### Modifiers -The following values may be used as modifiers. - -Less common modifiers might not be supported by your terminal emulator. +The following values may be used as modifier, provided they are supported by +your terminal emulator. | Modifier | | --- | @@ -89,14 +104,13 @@ Less common modifiers might not be supported by your terminal emulator. | `hidden` | | `crossed_out` | -> Note: The `underlined` modifier is deprecated and only available for backwards compatibility. +> 💡 The `underlined` modifier is deprecated and only available for backwards compatibility. > Its behavior is equivalent to setting `underline.style="line"`. -### Underline Style - -One of the following values may be used as a value for `underline.style`. +### Underline style -Some styles might not be supported by your terminal emulator. +One of the following values may be used as a value for `underline.style`, providing it is +supported by your terminal emulator. | Modifier | | --- | @@ -109,7 +123,7 @@ Some styles might not be supported by your terminal emulator. ### Inheritance -Extend upon other themes by setting the `inherits` property to an existing theme. +Extend other themes by setting the `inherits` property to an existing theme. ```toml inherits = "boo_berry" @@ -124,19 +138,19 @@ berry = "#2A2A4D" ### Scopes -The following is a list of scopes available to use for styling. +The following is a list of scopes available to use for styling: #### Syntax highlighting These keys match [tree-sitter scopes](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#theme). -For a given highlight produced, styling will be determined based on the longest matching theme key. For example, the highlight `function.builtin.static` would match the key `function.builtin` rather than `function`. +When determining styling for a highlight, the longest matching theme key will be used. For example, if the highlight is `function.builtin.static`, the key `function.builtin` will be used instead of `function`. We use a similar set of scopes as -[SublimeText](https://www.sublimetext.com/docs/scope_naming.html). See also +[Sublime Text](https://www.sublimetext.com/docs/scope_naming.html). See also [TextMate](https://macromates.com/manual/en/language_grammars) scopes. -- `attribute` - Class attributes, html tag attributes +- `attribute` - Class attributes, HTML tag attributes - `type` - Types - `builtin` - Primitive types provided by the language (`int`, `usize`) @@ -144,7 +158,7 @@ We use a similar set of scopes as - `variant` - `constructor` -- `constant` (TODO: constant.other.placeholder for %v) +- `constant` (TODO: constant.other.placeholder for `%v`) - `builtin` Special constants provided by the language (`true`, `false`, `nil` etc) - `boolean` - `character` @@ -162,11 +176,11 @@ We use a similar set of scopes as - `comment` - Code comments - `line` - Single line comments (`//`) - - `block` - Block comments (e.g. (`/* */`) + - `block` - Block comments (e.g. (`/* */`) - `documentation` - Documentation comments (e.g. `///` in Rust) - `variable` - Variables - - `builtin` - Reserved language variables (`self`, `this`, `super`, etc) + - `builtin` - Reserved language variables (`self`, `this`, `super`, etc.) - `parameter` - Function parameters - `other` - `member` - Fields of composite data types (e.g. structs, unions) @@ -186,10 +200,10 @@ We use a similar set of scopes as - `return` - `exception` - `operator` - `or`, `in` - - `directive` - Preprocessor directives (`#if` in C) + - `directive` - Preprocessor directives (`#if` in C) - `function` - `fn`, `func` - `storage` - Keywords describing how things are stored - - `type` - The type of something, `class`, `function`, `var`, `let`, etc. + - `type` - The type of something, `class`, `function`, `var`, `let`, etc. - `modifier` - Storage modifiers like `static`, `mut`, `const`, `ref`, etc. - `operator` - `||`, `+=`, `>` @@ -216,9 +230,9 @@ We use a similar set of scopes as - `bold` - `italic` - `link` - - `url` - urls pointed to by links - - `label` - non-url link references - - `text` - url and image descriptions in links + - `url` - URLs pointed to by links + - `label` - non-URL link references + - `text` - URL and image descriptions in links - `quote` - `raw` - `inline` @@ -232,19 +246,19 @@ We use a similar set of scopes as #### Interface -These scopes are used for theming the editor interface. +These scopes are used for theming the editor interface: - `markup` - `normal` - - `completion` - for completion doc popup ui - - `hover` - for hover popup ui + - `completion` - for completion doc popup UI + - `hover` - for hover popup UI - `heading` - - `completion` - for completion doc popup ui - - `hover` - for hover popup ui + - `completion` - for completion doc popup UI + - `hover` - for hover popup UI - `raw` - `inline` - - `completion` - for completion doc popup ui - - `hover` - for hover popup ui + - `completion` - for completion doc popup UI + - `hover` - for hover popup UI | Key | Notes | @@ -270,9 +284,9 @@ These scopes are used for theming the editor interface. | `ui.statusline.insert` | Statusline mode during insert mode ([only if `editor.color-modes` is enabled][editor-section]) | | `ui.statusline.select` | Statusline mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) | | `ui.statusline.separator` | Separator character in statusline | -| `ui.popup` | Documentation popups (e.g Space + k) | +| `ui.popup` | Documentation popups (e.g. Space + k) | | `ui.popup.info` | Prompt for multiple key options | -| `ui.window` | Border lines separating splits | +| `ui.window` | Borderlines separating splits | | `ui.help` | Description box for commands | | `ui.text` | Command prompts, popup text, etc. | | `ui.text.focus` | | @@ -301,10 +315,4 @@ These scopes are used for theming the editor interface. | `diagnostic.warning` | Diagnostics warning (editing area) | | `diagnostic.error` | Diagnostics error (editing area) | -You can check compliance to spec with - -```shell -cargo xtask themelint onedark # replace onedark with -``` - [editor-section]: ./configuration.md#editor-section diff --git a/book/src/usage.md b/book/src/usage.md index a6eb9ec1d..81cf83725 100644 --- a/book/src/usage.md +++ b/book/src/usage.md @@ -1,22 +1,43 @@ -# Usage +# Using Helix -(Currently not fully documented, see the [keymappings](./keymap.md) list for more.) + +- [Registers](#registers) + - [User-defined registers](#user-defined-registers) + - [Special registers](#special-registers) +- [Surround](#surround) +- [Selecting and manipulating text with textobjects](#selecting-and-manipulating-text-with-textobjects) +- [Navigating using tree-sitter textobjects](#navigating-using-tree-sitter-textobjects) +- [Moving the selection with syntax-aware motions](#moving-the-selection-with-syntax-aware-motions) + -See [tutor](https://github.com/helix-editor/helix/blob/master/runtime/tutor) (accessible via `hx --tutor` or `:tutor`) for a vimtutor-like introduction. +For a full interactive introduction to Helix, refer to the +[tutor](https://github.com/helix-editor/helix/blob/master/runtime/tutor) which +can be accessed via the command `hx --tutor` or `:tutor`. + +> 💡 Currently, not all functionality is fully documented, please refer to the +> [key mappings](./keymap.md) list. ## Registers -Vim-like registers can be used to yank and store text to be pasted later. Usage is similar, with `"` being used to select a register: +In Helix, registers are storage locations for text and other data, such as the +result of a search. Registers can be used to cut, copy, and paste text, similar +to the clipboard in other text editors. Usage is similar to Vim, with `"` being +used to select a register. + +### User-defined registers + +Helix allows you to create your own named registers for storing text, for +example: - `"ay` - Yank the current selection to register `a`. - `"op` - Paste the text in register `o` after the selection. -If there is a selected register before invoking a change or delete command, the selection will be stored in the register and the action will be carried out: +If a register is selected before invoking a change or delete command, the selection will be stored in the register and the action will be carried out: - `"hc` - Store the selection in register `h` and then change it (delete and enter insert mode). - `"md` - Store the selection in register `m` and delete it. -### Special Registers +### Special registers | Register character | Contains | | --- | --- | @@ -25,41 +46,90 @@ If there is a selected register before invoking a change or delete command, the | `"` | Last yanked text | | `_` | Black hole | -> There is no special register for copying to system clipboard, instead special commands and keybindings are provided. See the [keymap](keymap.md#space-mode) for the specifics. -> The black hole register works as a no-op register, meaning no data will be written to / read from it. +The system clipboard is not directly supported by a special register. Instead, special commands and keybindings are provided. Refer to the +[key map](keymap.md#space-mode) for more details. + +The black hole register is a no-op register, meaning that no data will be read or written to it. ## Surround -Functionality similar to [vim-surround](https://github.com/tpope/vim-surround) is built into -helix. The keymappings have been inspired from [vim-sandwich](https://github.com/machakann/vim-sandwich): +Helix includes built-in functionality similar to [vim-surround](https://github.com/tpope/vim-surround). +The keymappings have been inspired from [vim-sandwich](https://github.com/machakann/vim-sandwich): -![surround demo](https://user-images.githubusercontent.com/23398472/122865801-97073180-d344-11eb-8142-8f43809982c6.gif) +![Surround demo](https://user-images.githubusercontent.com/23398472/122865801-97073180-d344-11eb-8142-8f43809982c6.gif) -- `ms` - Add surround characters -- `mr` - Replace surround characters -- `md` - Delete surround characters +| Key Sequence | Action | +| --------------------------------- | --------------------------------------- | +| `ms` (after selecting text) | Add surround characters to selection | +| `mr` | Replace the closest surround characters | +| `md` | Delete the closest surround characters | -`ms` acts on a selection, so select the text first and use `ms`. `mr` and `md` work -on the closest pairs found and selections are not required; use counts to act in outer pairs. +You can use counts to act on outer pairs. -It can also act on multiple selections (yay!). For example, to change every occurrence of `(use)` to `[use]`: +Surround can also act on multiple selections. For example, to change every occurrence of `(use)` to `[use]`: -- `%` to select the whole file -- `s` to split the selections on a search term -- Input `use` and hit Enter -- `mr([` to replace the parens with square brackets +1. `%` to select the whole file +2. `s` to split the selections on a search term +3. Input `use` and hit Enter +4. `mr([` to replace the parentheses with square brackets -Multiple characters are currently not supported, but planned. +Multiple characters are currently not supported, but planned for future release. -## Syntax-tree Motions +## Selecting and manipulating text with textobjects -`Alt-p`, `Alt-o`, `Alt-i`, and `Alt-n` (or `Alt` and arrow keys) move the primary -selection according to the selection's place in the syntax tree. Let's walk -through an example to get familiar with them. Many languages have a syntax like -so for function calls: +In Helix, textobjects are a way to select, manipulate and operate on a piece of +text in a structured way. They allow you to refer to blocks of text based on +their structure or purpose, such as a word, sentence, paragraph, or even a +function or block of code. -``` -func(arg1, arg2, arg3) +![Textobject demo](https://user-images.githubusercontent.com/23398472/124231131-81a4bb00-db2d-11eb-9d10-8e577ca7b177.gif) +![Textobject tree-sitter demo](https://user-images.githubusercontent.com/23398472/132537398-2a2e0a54-582b-44ab-a77f-eb818942203d.gif) + +- `ma` - Select around the object (`va` in Vim, `` in Kakoune) +- `mi` - Select inside the object (`vi` in Vim, `` in Kakoune) + +| Key after `mi` or `ma` | Textobject selected | +| --- | --- | +| `w` | Word | +| `W` | WORD | +| `p` | Paragraph | +| `(`, `[`, `'`, etc. | Specified surround pairs | +| `m` | The closest surround pair | +| `f` | Function | +| `c` | Class | +| `a` | Argument/parameter | +| `o` | Comment | +| `t` | Test | +| `g` | Change | + +> 💡 `f`, `c`, etc. need a tree-sitter grammar active for the current +document and a special tree-sitter query file to work properly. [Only +some grammars][lang-support] currently have the query file implemented. +Contributions are welcome! + +## Navigating using tree-sitter textobjects + +Navigating between functions, classes, parameters, and other elements is +possible using tree-sitter and textobject queries. For +example to move to the next function use `]f`, to move to previous +class use `[c`, and so on. + +![Tree-sitter-nav-demo][tree-sitter-nav-demo] + +For the full reference see the [unimpaired][unimpaired-keybinds] section of the key bind +documentation. + +> 💡 This feature relies on tree-sitter textobjects +> and requires the corresponding query file to work properly. + +## Moving the selection with syntax-aware motions + +`Alt-p`, `Alt-o`, `Alt-i`, and `Alt-n` (or `Alt` and arrow keys) allow you to move the +selection according to its location in the syntax tree. For example, many languages have the +following syntax for function calls: + +```js +func(arg1, arg2, arg3); ``` A function call might be parsed by tree-sitter into a tree like the following. @@ -93,77 +163,29 @@ a more intuitive tree format: └──────────┘ └──────────┘ └──────────┘ ``` -Say we have a selection that wraps `arg1`. The selection is on the `arg1` leaf -in the tree above. +If you have a selection that wraps `arg1` (see the tree above), and you use +`Alt-n`, it will select the next sibling in the syntax tree: `arg2`. -``` +```js +// before func([arg1], arg2, arg3) +// after +func(arg1, [arg2], arg3); ``` -Using `Alt-n` would select the next sibling in the syntax tree: `arg2`. +Similarly, `Alt-o` will expand the selection to the parent node, in this case, the +arguments node. -``` -func(arg1, [arg2], arg3) -``` - -While `Alt-o` would expand the selection to the parent node. In the tree above we -can see that we would select the `arguments` node. - -``` -func[(arg1, arg2, arg3)] +```js +func[(arg1, arg2, arg3)]; ``` There is also some nuanced behavior that prevents you from getting stuck on a -node with no sibling. If we have a selection on `arg1`, `Alt-p` would bring us -to the previous child node. Since `arg1` doesn't have a sibling to its left, -though, we climb the syntax tree and then take the previous selection. So -`Alt-p` will move the selection over to the "func" `identifier`. - -``` -[func](arg1, arg2, arg3) -``` - -## Textobjects - -![textobject-demo](https://user-images.githubusercontent.com/23398472/124231131-81a4bb00-db2d-11eb-9d10-8e577ca7b177.gif) -![textobject-treesitter-demo](https://user-images.githubusercontent.com/23398472/132537398-2a2e0a54-582b-44ab-a77f-eb818942203d.gif) - -- `ma` - Select around the object (`va` in Vim, `` in Kakoune) -- `mi` - Select inside the object (`vi` in Vim, `` in Kakoune) - -| Key after `mi` or `ma` | Textobject selected | -| --- | --- | -| `w` | Word | -| `W` | WORD | -| `p` | Paragraph | -| `(`, `[`, `'`, etc | Specified surround pairs | -| `m` | Closest surround pair | -| `f` | Function | -| `c` | Class | -| `a` | Argument/parameter | -| `o` | Comment | -| `t` | Test | -| `g` | Change | - -> NOTE: `f`, `c`, etc need a tree-sitter grammar active for the current -document and a special tree-sitter query file to work properly. [Only -some grammars][lang-support] currently have the query file implemented. -Contributions are welcome! - -## Tree-sitter Textobject Based Navigation - -Navigating between functions, classes, parameters, etc is made -possible by leveraging tree-sitter and textobjects queries. For -example to move to the next function use `]f`, to move to previous -class use `[c`, and so on. - -![tree-sitter-nav-demo][tree-sitter-nav-demo] - -See the [unimpaired][unimpaired-keybinds] section of the keybind -documentation for the full reference. - -> NOTE: This feature is dependent on tree-sitter based textobjects -and therefore requires the corresponding query file to work properly. +node with no sibling. When using `Alt-p` with a selection on `arg1`, the previous +child node will be selected. In the event that `arg1` does not have a previous +sibling, the selection will move up the syntax tree and select the previous +element. As a result, using `Alt-p` with a selection on `arg1` will move the +selection to the "func" `identifier`. [lang-support]: ./lang-support.md [unimpaired-keybinds]: ./keymap.md#unimpaired From cfb9986d84764c7f8b3a903f2b092ba0a61dd951 Mon Sep 17 00:00:00 2001 From: Erasin Wang Date: Mon, 6 Mar 2023 23:29:06 +0800 Subject: [PATCH 0051/1169] Update onelight theme (#6192) --- runtime/themes/onelight.toml | 211 ++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 78 deletions(-) diff --git a/runtime/themes/onelight.toml b/runtime/themes/onelight.toml index 7c2669793..c2d4e16e3 100644 --- a/runtime/themes/onelight.toml +++ b/runtime/themes/onelight.toml @@ -1,129 +1,184 @@ -# One Light # Author : erasin "attribute" = { fg = "yellow" } -"comment" = { fg = "gray", modifiers = ["italic"] } +"constructor" = { fg = "brown" } +"label" = { fg = "cyan" } +"operator" = { fg = "red" } +"tag" = { fg = "cyan" } +"namespace" = { fg = "blue" } +"special" = { fg = "deep-purple" } +"property" = { fg = "purple" } +"module" = { fg = "cyan" } -"constant" = { fg = "cyan" } +"type" = { fg = "gold" } +"type.builtin" = { fg = "light-blue" } +"type.enum" = { fg = "cyan" } +"type.enum.variant" = { fg = "cyan" } + +"constant" = { fg = "cyan", modifiers = ["bold"] } +"constant.builtin" = { fg = "deep-purple" } +"constant.builtin.boolean" = { fg = "deep-purple" } +"constant.character" = { fg = "green" } +"constant.character.escape" = { fg = "brown" } "constant.numeric" = { fg = "gold" } -"constant.builtin" = { fg = "gold" } -"constant.character.escape" = { fg = "gold" } +"constant.numeric.integer" = { fg = "gold" } +"constant.numeric.float" = { fg = "gold" } -"constructor" = { fg = "yellow" } +"string" = { fg = "green" } +"string.regexp" = { fg = "purple" } +"string.special" = { fg = "green" } +"string.special.path" = { fg = "blue" } +"string.special.url" = { fg = "light-blue" } +"string.special.symbol" = { fg = "pink" } + +"comment" = { fg = "grey", modifiers = ["italic"] } +"comment.line" = { fg = "grey", modifiers = ["italic"] } +"comment.block" = { fg = "grey", modifiers = ["italic"] } +"comment.block.documentation" = { fg = "grey", modifiers = ["italic"] } + +# "variable" = { fg = "black" } +"variable.builtin" = { fg = "light-blue" } +"variable.parameter" = { fg = "red" } +"variable.other" = { fg = "pink" } +"variable.other.member" = { fg = "pink" } -"function" = { fg = "blue" } -"function.builtin" = { fg = "cyan" } -"function.macro" = { fg = "red" } +"punctuation" = { fg = "black" } +"punctuation.delimiter" = { fg = "purple" } +"punctuation.bracket" = { fg = "brown" } +"punctuation.special" = { fg = "brown" } "keyword" = { fg = "purple" } -"keyword.function" = { fg = "purple" } "keyword.control" = { fg = "purple" } -"keyword.control.import" = { fg = "purple" } +"keyword.control.conditional" = { fg = "red", modifiers = ["bold"] } +"keyword.control.repeat" = { fg = "pink", modifiers = ["bold"] } +"keyword.control.import" = { fg = "red" } +"keyword.control.return" = { fg = "deep-purple", modifiers = ["bold"] } +"keyword.control.exception" = { fg = "purple" } +"keyword.operator" = { fg = "red" } "keyword.directive" = { fg = "purple" } -"keyword.operator" = { fg = "purple" } +"keyword.function" = { fg = "purple" } +"keyword.storage" = { fg = "purple" } "keyword.storage.type" = { fg = "purple" } +"keyword.storage.modifier" = { fg = "purple", modifiers = ["bold"] } -"tag" = "cyan" -"label" = { fg = "cyan" } -"namespace" = { fg = "red" } -"operator" = { fg = "red" } -"special" = { fg = "purple" } -"string" = { fg = "green" } -"module" = { fg = "cyan" } - -"type" = { fg = "yellow" } -"type.builtin" = { fg = "purple" } - -"punctuation" = { fg = "gray" } -"punctuation.delimiter" = { fg = "black" } -"punctuation.bracket" = { fg = "gray" } - -"variable" = { fg = "black" } -"variable.builtin" = { fg = "light-blue" } -"variable.parameter" = { fg = "red" } -"variable.other.member" = { fg = "red" } +"function" = { fg = "blue" } +"function.builtin" = { fg = "cyan" } +"function.method" = { fg = "light-blue" } +"function.macro" = { fg = "pink", modifiers = ["bold"] } +"function.special" = { fg = "cyan" } "markup.heading" = { fg = "red" } -"markup.raw" = { fg = "gray" } -"markup.raw.inline" = { fg = "green", bg = "grey-200" } -"markup.bold" = { fg = "yellow", modifiers = ["bold"] } -"markup.italic" = { fg = "purple", modifiers = ["italic"] } -"markup.strikethrough" = { modifiers = ["crossed_out"] } -"markup.list" = { fg = "light-blue" } -"markup.quote" = { fg = "gray" } -"markup.link.url" = { fg = "cyan", modifiers = ["underlined"] } -"markup.link.text" = { fg = "light-blue" } +"markup.heading.marker" = { fg = "red" } "markup.heading.1" = { fg = "red", modifiers = ["bold"] } -"markup.heading.2" = { fg = "gold", modifiers = ["bold"] } +"markup.heading.2" = { fg = "gold", modifiers = [ + "bold", +], underline = { style = "line" } } "markup.heading.3" = { fg = "yellow", modifiers = ["bold"] } "markup.heading.4" = { fg = "green", modifiers = ["bold"] } "markup.heading.5" = { fg = "blue", modifiers = ["bold"] } "markup.heading.6" = { fg = "purple", modifiers = ["bold"] } +"markup.list" = { fg = "light-blue" } +"markup.list.unnumbered" = { fg = "light-blue" } +"markup.list.numbered" = { fg = "light-blue" } +"markup.bold" = { fg = "yellow", modifiers = ["bold"] } +"markup.italic" = { fg = "purple", modifiers = ["italic"] } +"markup.link" = { fg = "light-blue" } +"markup.link.url" = { fg = "cyan", modifiers = ["underlined"] } +"markup.link.text" = { fg = "light-blue" } +"markup.quote" = { fg = "grey" } +"markup.raw" = { fg = "brown" } +"markup.raw.inline" = { fg = "green" } +"markup.raw.block" = { fg = "grey" } -"diff.plus" = "green" -"diff.delta" = "gold" -"diff.minus" = "red" - -"diagnostic.info".underline = { color = "blue", style = "curl" } -"diagnostic.hint".underline = { color = "green", style = "curl" } -"diagnostic.warning".underline = { color = "yellow", style = "curl" } -"diagnostic.error".underline = { color = "red", style = "curl" } - -"info" = { fg = "blue", modifiers = ["bold"] } -"hint" = { fg = "green", modifiers = ["bold"] } -"warning" = { fg = "yellow", modifiers = ["bold"] } -"error" = { fg = "red", modifiers = ["bold"] } +"diff" = { fg = "red" } +"diff.plus" = { fg = "green" } +"diff.minus" = { fg = "red" } +"diff.delta" = { fg = "cyan" } +"diff.delta.moved" = { fg = "cyan" } "ui.background" = { bg = "white" } +"ui.background.separator" = { bg = "white" } -"ui.cursor" = { fg = "white", bg = "gray" } +"ui.cursor" = { fg = "white", bg = "grey" } +"ui.cursor.normal" = { fg = "white", bg = "grey" } +"ui.cursor.insert" = { fg = "white", bg = "grey" } +"ui.cursor.select" = { fg = "white", bg = "grey" } +"ui.cursor.match" = { bg = "light-white", modifiers = ["bold"] } "ui.cursor.primary" = { fg = "white", bg = "black" } -"ui.cursor.match" = { bg = "light-gray" } +"ui.cursor.primary.normal" = { fg = "white", bg = "black" } +"ui.cursor.primary.insert" = { fg = "red", bg = "black" } +"ui.cursor.primary.select" = { fg = "white", bg = "black" } -"ui.cursorline.primary" = { fg = "white", bg = "grey-100" } -# "ui.cursorline.secondary" = { fg = "white", bg = "grey-200" } - -"ui.highlight" = { bg = "light-white" } - -"ui.selection" = { bg = "light-white", modifiers = ["dim"] } -"ui.selection.primary" = { bg = "light-white" } - -"ui.virtual" = { fg = "light-white" } -"ui.virtual.indent-guide" = { fg = "grey-500" } -"ui.virtual.ruler" = { bg = "light-white" } -"ui.virtual.whitespace" = { fg = "light-white" } +"ui.gutter" = { fg = "grey-500" } +"ui.gutter.selected" = { fg = "black" } "ui.linenr" = { fg = "grey-500" } -"ui.linenr.selected" = { fg = "black", modifiers = ["dim"] } +"ui.linenr.selected" = { fg = "black", modifiers = ["bold"] } "ui.statusline" = { fg = "black", bg = "light-white" } -"ui.statusline.inactive" = { fg = "gray", bg = "light-white" } +"ui.statusline.inactive" = { fg = "grey", bg = "grey-200" } "ui.statusline.normal" = { fg = "light-white", bg = "light-blue" } "ui.statusline.insert" = { fg = "light-white", bg = "green" } "ui.statusline.select" = { fg = "light-white", bg = "purple" } +"ui.popup" = { fg = "black", bg = "grey-200" } +"ui.popup.info" = { fg = "black", bg = "grey-200" } +"ui.window" = { fg = "grey-500", bg = "grey-100" } +"ui.help" = { fg = "black", bg = "grey-200" } + "ui.text" = { fg = "black" } "ui.text.focus" = { fg = "red", bg = "light-white", modifiers = ["bold"] } +"ui.text.inactive" = { fg = "grey" } +"ui.text.info" = { fg = "black" } + +"ui.virtual" = { fg = "light-white" } +"ui.virtual.ruler" = { bg = "light-white" } +"ui.virtual.wrap" = { bg = "light-white" } +"ui.virtual.whitespace" = { fg = "light-white" } +"ui.virtual.indent-guide" = { fg = "grey-500" } +"ui.virtual.inlay-hint" = { fg = "grey" } -"ui.help" = { fg = "black", bg = "grey-200" } -"ui.popup" = { fg = "black", bg = "grey-200" } -"ui.window" = { fg = "black", bg = "light-white" } "ui.menu" = { fg = "black", bg = "light-white" } "ui.menu.selected" = { fg = "white", bg = "light-blue" } +"ui.menu.scroll" = { fg = "light-blue", bg = "white" } + +"ui.selection" = { bg = "light-white", modifiers = ["dim"] } +"ui.selection.primary" = { bg = "light-white" } + +"ui.cursorline.primary" = { fg = "white", bg = "grey-100" } +"ui.cursorline.secondary" = { fg = "white", bg = "grey-200" } + +"ui.cursorcolumn.primary" = { fg = "white", bg = "grey-100" } +"ui.cursorcolumn.secondary" = { fg = "white", bg = "grey-200" } + +"ui.highlight" = { bg = "light-white" } + +"diagnostic.info" = { underline = { color = "blue", style = "dotted" } } +"diagnostic.hint" = { underline = { color = "green", style = "dashed" } } +"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } } +"diagnostic.error" = { underline = { color = "red", style = "curl" } } + +"info" = { fg = "blue", modifiers = ["bold"] } +"hint" = { fg = "green", modifiers = ["bold"] } +"warning" = { fg = "yellow", modifiers = ["bold"] } +"error" = { fg = "red", modifiers = ["bold"] } [palette] white = "#FAFAFA" -yellow = "#A06600" +yellow = "#FF6F00" +gold = "#D35400" +brown = "#4E342E" blue = "#0061FF" -light-blue = "#1877F2" -red = "#DC003F" +light-blue = "#0091EA" +red = "#D50000" +pink = "#C2185B" purple = "#B500A9" +deep-purple = "#651FFF" green = "#24A443" -gold = "#D35400" cyan = "#0086C1" black = "#282C34" light-white = "#E3E3E3" -gray = "#5C6370" +grey = "#5C6370" grey-100 = "#F3F3F3" grey-200 = "#EDEDED" grey-500 = "#9E9E9E" From bc50502b1eef4971d3d3a5518386888bf43ccf8b Mon Sep 17 00:00:00 2001 From: Erasin Wang Date: Tue, 7 Mar 2023 00:15:03 +0800 Subject: [PATCH 0052/1169] Update highlight for ecma/js/ts (#6205) --- runtime/queries/ecma/highlights.scm | 59 +++++++++++++++++++---------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/runtime/queries/ecma/highlights.scm b/runtime/queries/ecma/highlights.scm index 212bb8754..7285ab960 100644 --- a/runtime/queries/ecma/highlights.scm +++ b/runtime/queries/ecma/highlights.scm @@ -167,55 +167,76 @@ ] @punctuation.bracket [ - "as" "async" "debugger" "delete" "extends" "from" - "function" "get" - "in" - "instanceof" "new" - "of" "set" - "static" "target" - "try" "typeof" + "instanceof" "void" "with" ] @keyword +[ + "of" + "as" + "in" +] @keyword.operator + +[ + "function" +] @keyword.function + [ "class" "let" - "const" "var" ] @keyword.storage.type [ - "switch" - "case" + "const" + "static" +] @keyword.storage.modifier + +[ "default" - "if" - "else" "yield" - "throw" "finally" - "return" - "catch" - "continue" - "while" - "break" - "for" "do" "await" ] @keyword.control +[ + "if" + "else" + "switch" + "case" + "while" +] @keyword.control.conditional + +[ + "for" +] @keyword.control.repeat + [ "import" "export" ] @keyword.control.import +[ + "return" + "break" + "continue" +] @keyword.control.return + +[ + "throw" + "try" + "catch" +] @keyword.control.exception + From 77d6ed150c746098020cc7104943fcb64453f388 Mon Sep 17 00:00:00 2001 From: workingj <19818596+workingj@users.noreply.github.com> Date: Tue, 7 Mar 2023 00:35:32 +0100 Subject: [PATCH 0053/1169] feat(theme): Update pop-dark for color-modes (#6208) --- runtime/themes/pop-dark.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/themes/pop-dark.toml b/runtime/themes/pop-dark.toml index 57db2d2c5..d2010ed62 100644 --- a/runtime/themes/pop-dark.toml +++ b/runtime/themes/pop-dark.toml @@ -28,8 +28,10 @@ error = { fg = 'brownD', bg = 'redE', modifiers = ['bold'] } 'ui.linenr' = { bg = 'brownU', fg = 'greyL' } 'ui.linenr.selected' = { fg = 'orangeH' } 'ui.cursorline' = { bg = 'brownH' } -'ui.statusline' = { fg = 'greyT', bg = 'brownD' } 'ui.statusline.inactive' = { fg = 'greyT', bg = 'brownN' } +'ui.statusline.normal' = { fg = 'greyT', bg = 'brownD', modifiers = ['bold'] } +'ui.statusline.select' = { bg = 'blueL', fg = 'brownD', modifiers = ['bold'] } +'ui.statusline.insert' = { bg = 'orangeL', fg = 'brownD', modifiers = ['bold'] } 'ui.help' = { fg = 'greyT', bg = 'brownD' } 'ui.highlight' = { bg = 'brownH' } 'ui.virtual' = { fg = 'brownV' } From c00baf7da6f0119a2c58defb5d25a6333183e59c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:18:45 -0600 Subject: [PATCH 0054/1169] build(deps): bump grep-searcher from 0.1.10 to 0.1.11 (#6213) Bumps [grep-searcher](https://github.com/BurntSushi/ripgrep) from 0.1.10 to 0.1.11. - [Release notes](https://github.com/BurntSushi/ripgrep/releases) - [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md) - [Commits](https://github.com/BurntSushi/ripgrep/compare/grep-searcher-0.1.10...0.1.11) --- updated-dependencies: - dependency-name: grep-searcher dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 10 +++++----- helix-term/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 090b5f027..a282992dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,9 +1033,9 @@ dependencies = [ [[package]] name = "grep-matcher" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d27563c33062cd33003b166ade2bb4fd82db1fd6a86db764dfdad132d46c1cc" +checksum = "3902ca28f26945fe35cad349d776f163981d777fee382ccd6ef451126f51b319" dependencies = [ "memchr", ] @@ -1057,11 +1057,11 @@ dependencies = [ [[package]] name = "grep-searcher" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48852bd08f9b4eb3040ecb6d2f4ade224afe880a9a0909c5563cc59fa67932cc" +checksum = "5601c4b9f480f0c9ebb40b1f6cbf447b8a50c5369223937a6c5214368c58779f" dependencies = [ - "bstr 0.2.17", + "bstr 1.3.0", "bytecount", "encoding_rs", "encoding_rs_io", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index b1d63a04a..ae0d08ab0 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -64,7 +64,7 @@ serde = { version = "1.0", features = ["derive"] } # ripgrep for global search grep-regex = "0.1.10" -grep-searcher = "0.1.10" +grep-searcher = "0.1.11" [target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100 signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } From b1e7d4d9a0f28bea3ab0b5e8ecb438d8234ddb57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:19:10 -0600 Subject: [PATCH 0055/1169] build(deps): bump serde_json from 1.0.93 to 1.0.94 (#6211) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.93 to 1.0.94. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.93...v1.0.94) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a282992dd..c7968384f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1815,9 +1815,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", From 53c8dcea5bc2e77706a1272cf0562121005bf54f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:20:51 -0600 Subject: [PATCH 0056/1169] build(deps): bump ignore from 0.4.18 to 0.4.20 (#6214) Bumps [ignore](https://github.com/BurntSushi/ripgrep) from 0.4.18 to 0.4.20. - [Release notes](https://github.com/BurntSushi/ripgrep/releases) - [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md) - [Commits](https://github.com/BurntSushi/ripgrep/compare/ignore-0.4.18...ignore-0.4.20) --- updated-dependencies: - dependency-name: ignore dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7968384f..7ac8144d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,15 +215,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossterm" version = "0.26.1" @@ -1020,12 +1011,12 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", - "bstr 0.2.17", + "bstr 1.3.0", "fnv", "log", "regex", @@ -1324,11 +1315,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ - "crossbeam-utils", "globset", "lazy_static", "log", From 2417ac8a4b11b4614724b0d42214992a0178a8ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 18:43:24 -0600 Subject: [PATCH 0057/1169] build(deps): bump thiserror from 1.0.38 to 1.0.39 (#6216) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.38 to 1.0.39. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.38...1.0.39) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ac8144d8..90ce177f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2006,18 +2006,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", From 8228fb0cf7634a627f2eb79289d105ac941361b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:53:10 +0900 Subject: [PATCH 0058/1169] build(deps): bump tokio from 1.25.0 to 1.26.0 (#6212) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.25.0 to 1.26.0. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.25.0...tokio-1.26.0) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 70 +++++++++++++++++++++++++++++--------------- helix-lsp/Cargo.toml | 2 +- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90ce177f8..e25c47d6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -425,7 +425,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1382,7 +1382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1508,7 +1508,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1606,7 +1606,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1753,7 +1753,7 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1972,7 +1972,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -2088,9 +2088,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -2103,7 +2103,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -2397,47 +2397,71 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "xtask" diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 133ead298..c1f091107 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -23,6 +23,6 @@ lsp-types = { version = "0.94" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -tokio = { version = "1.25", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } +tokio = { version = "1.26", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio-stream = "0.1.12" which = "4.4" From 84be5cd52c61a53f8d47be60b33bbaaed63652e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Mar 2023 19:18:28 -0600 Subject: [PATCH 0059/1169] build(deps): bump grep-regex from 0.1.10 to 0.1.11 (#6215) Bumps [grep-regex](https://github.com/BurntSushi/ripgrep) from 0.1.10 to 0.1.11. - [Release notes](https://github.com/BurntSushi/ripgrep/releases) - [Changelog](https://github.com/BurntSushi/ripgrep/blob/master/CHANGELOG.md) - [Commits](https://github.com/BurntSushi/ripgrep/compare/grep-regex-0.1.10...0.1.11) --- updated-dependencies: - dependency-name: grep-regex dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 59 ++++++++++++++++++------------------------- helix-term/Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e25c47d6f..a184cf3a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,17 +73,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", -] - [[package]] name = "bstr" version = "1.3.0" @@ -557,7 +546,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc22b0cdc52237667c301dd7cdc6ead8f8f73c9f824e9942c8ebd6b764f6c0bf" dependencies = [ - "bstr 1.3.0", + "bstr", "btoi", "gix-date", "itoa", @@ -571,7 +560,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2231a25934a240d0a4b6f4478401c73ee81d8be52de0293eedbc172334abf3e1" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-features", "gix-glob", "gix-path", @@ -604,7 +593,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2c6f75c1e0f924de39e750880a6e21307194bb1ab773efe3c7d2d787277f8ab" dependencies = [ - "bstr 1.3.0", + "bstr", ] [[package]] @@ -613,7 +602,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52c62e26ce11f607712e4f49a0a192ed87675d30187fd61be070abbd607d12f1" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-config-value", "gix-features", "gix-glob", @@ -635,7 +624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d4a4ba0531e46fe558459557a5b29fb86c3e4b2666c1c0861d93c7c678331" dependencies = [ "bitflags", - "bstr 1.3.0", + "bstr", "gix-path", "libc", "thiserror", @@ -647,7 +636,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be32b5fe339a31b8e53fa854081dc914c45020dcb64637f3c21baf69c96fc1b" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-command", "gix-config-value", "gix-path", @@ -663,7 +652,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b96271912ce39822501616f177dea7218784e6c63be90d5f36322ff3a722aae2" dependencies = [ - "bstr 1.3.0", + "bstr", "itoa", "thiserror", "time", @@ -687,7 +676,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91c204adba5ebd211c74735cbb65817d277e154486bac0dffa3701f163b80350" dependencies = [ - "bstr 1.3.0", + "bstr", "dunce", "gix-hash", "gix-path", @@ -720,7 +709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93e43efd776bc543f46f0fd0ca3d920c37af71a764a16f2aebd89765e9ff2993" dependencies = [ "bitflags", - "bstr 1.3.0", + "bstr", ] [[package]] @@ -751,7 +740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c12caf7886c7ba06f2b28835cdc2be1dca86bd047d00299d2d49e707ce1c2616" dependencies = [ "bitflags", - "bstr 1.3.0", + "bstr", "btoi", "filetime", "gix-bitmap", @@ -783,7 +772,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b66aea5e52875cd4915f4957a6f4b75831a36981e2ec3f5fad9e370e444fe1a" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-actor", "thiserror", ] @@ -794,7 +783,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df068db9180ee935fbb70504848369e270bdcb576b05c0faa8b9fd3b86fc017" dependencies = [ - "bstr 1.3.0", + "bstr", "btoi", "gix-actor", "gix-features", @@ -853,7 +842,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6c104a66dec149cb8f7aaafc6ab797654cf82d67f050fd0cb7e7294e328354b" dependencies = [ - "bstr 1.3.0", + "bstr", "thiserror", ] @@ -876,7 +865,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a282f5a8d9ee0b09ec47390ac727350c48f2f5c76d803cd8da6b3e7ad56e0bcb" dependencies = [ - "bstr 1.3.0", + "bstr", "btoi", "thiserror", ] @@ -906,7 +895,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aba332462bda2e8efeae4302b39a6ed01ad56ef772fd5b7ef197cf2798294d65" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-hash", "gix-revision", "gix-validate", @@ -920,7 +909,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed98e4a0254953c64bc913bd23146a1de662067d5cf974cbdde396958b39e5b0" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-date", "gix-hash", "gix-hashtable", @@ -973,7 +962,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "044072b7ce8601b62dcec841b92129f5cc677072823324121b395d766ac5f528" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-features", "gix-path", "home", @@ -987,7 +976,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b69ddb780ea1465255e66818d75b7098371c58dbc9560da4488a44b9f5c7e443" dependencies = [ - "bstr 1.3.0", + "bstr", "thiserror", ] @@ -997,7 +986,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7cb9af6e56152953d8fe113c4f9d7cf60cf7a982362711e9200a255579b49cb" dependencies = [ - "bstr 1.3.0", + "bstr", "gix-attributes", "gix-features", "gix-glob", @@ -1016,7 +1005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", - "bstr 1.3.0", + "bstr", "fnv", "log", "regex", @@ -1033,12 +1022,12 @@ dependencies = [ [[package]] name = "grep-regex" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1345f8d33c89f2d5b081f2f2a41175adef9fd0bed2fea6a26c96c2deb027e58e" +checksum = "997598b41d53a37a2e3fc5300d5c11d825368c054420a9c65125b8fe1078463f" dependencies = [ "aho-corasick", - "bstr 0.2.17", + "bstr", "grep-matcher", "log", "regex", @@ -1052,7 +1041,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5601c4b9f480f0c9ebb40b1f6cbf447b8a50c5369223937a6c5214368c58779f" dependencies = [ - "bstr 1.3.0", + "bstr", "bytecount", "encoding_rs", "encoding_rs_io", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index ae0d08ab0..bca567c28 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -63,7 +63,7 @@ serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } # ripgrep for global search -grep-regex = "0.1.10" +grep-regex = "0.1.11" grep-searcher = "0.1.11" [target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100 From 136d1164e06c8ae6f23d611e8fcc2c3e53b9bd80 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 6 Mar 2023 20:46:46 -0600 Subject: [PATCH 0060/1169] Pin tree-sitter at git master (#6218) Tree-sitter has some unreleased improvements that can speed up small queries and prevent hangs due to error recovery in some parsers. This change pins tree-sitter to the latest master. Neovim also pins tree-sitter to a commit on master. --- Cargo.lock | 3 +-- Cargo.toml | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a184cf3a6..96c9fcf96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2154,8 +2154,7 @@ dependencies = [ [[package]] name = "tree-sitter" version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4423c784fe11398ca91e505cdc71356b07b1a924fc8735cfab5333afe3e18bc" +source = "git+https://github.com/tree-sitter/tree-sitter?rev=c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14#c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14" dependencies = [ "cc", "regex", diff --git a/Cargo.toml b/Cargo.toml index c7e254728..016267fe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,6 @@ inherits = "test" package.helix-core.opt-level = 2 package.helix-tui.opt-level = 2 package.helix-term.opt-level = 2 + +[patch.crates-io] +tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "c51896d32dcc11a38e41f36e3deb1a6a9c4f4b14" } From 622f90a157cd3cdac71437851d4a2a2bd38e0a79 Mon Sep 17 00:00:00 2001 From: Erasin Wang Date: Wed, 8 Mar 2023 08:28:44 +0800 Subject: [PATCH 0061/1169] Update highlight for PHP (#6203) - update tree-sitter-php - add basic types, operator - refine keyword --- languages.toml | 2 +- runtime/queries/php/highlights.scm | 267 ++++++++++++++++++++++++----- 2 files changed, 228 insertions(+), 41 deletions(-) diff --git a/languages.toml b/languages.toml index c34135244..d8b57e976 100644 --- a/languages.toml +++ b/languages.toml @@ -571,7 +571,7 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "php" -source = { git = "https://github.com/tree-sitter/tree-sitter-php", rev = "57f855461aeeca73bd4218754fb26b5ac143f98f" } +source = { git = "https://github.com/tree-sitter/tree-sitter-php", rev = "f860e598194f4a71747f91789bf536b393ad4a56" } [[language]] name = "twig" diff --git a/runtime/queries/php/highlights.scm b/runtime/queries/php/highlights.scm index 77c9424fe..4bf313d84 100644 --- a/runtime/queries/php/highlights.scm +++ b/runtime/queries/php/highlights.scm @@ -2,15 +2,63 @@ "?>" @tag ; Types +[ + (primitive_type) + (cast_type) +] @type.builtin + +(named_type + [ (name) @type + (qualified_name (name) @type)]) + +(base_clause + [ (name) @type + (qualified_name (name) @type)]) + +(enum_declaration + name: (name) @type.enum) -(primitive_type) @type.builtin -(cast_type) @type.builtin -(named_type (name) @type) @type -(named_type (qualified_name) @type) @type +(interface_declaration + name: (name) @constructor) + +(class_declaration + name: (name) @constructor) + +(trait_declaration + name:(name) @constructor) (namespace_definition name: (namespace_name (name) @namespace)) +(namespace_name_as_prefix + (namespace_name (name) @namespace)) + +(namespace_use_clause + [ (name) @namespace + (qualified_name (name) @type) ]) + +(namespace_aliasing_clause (name) @namespace) + +(class_interface_clause + [(name) @type + (qualified_name (name) @type)]) + +(scoped_call_expression + scope: [(name) @type + (qualified_name (name) @type)]) + +(class_constant_access_expression + . [(name) @constructor + (qualified_name (name) @constructor)] + (name) @constant) + +(use_declaration (name) @type) + +(binary_expression + operator: "instanceof" + right: [(name) @type + (qualified_name (name) @type)]) + ; Superglobals (subscript_expression (variable_name(name) @constant.builtin @@ -36,6 +84,21 @@ (function_definition name: (name) @function) +(nullsafe_member_call_expression + name: (name) @function.method) + +(object_creation_expression + [(name) @constructor + (qualified_name (name) @constructor)]) + +; Parameters +[ + (simple_parameter) + (variadic_parameter) +] @variable.parameter + +(argument + (name) @variable.parameter) ; Member @@ -62,68 +125,192 @@ (variable_name) @variable +; Attributes +(attribute_list) @attribute + ; Basic tokens -(string) @string -(heredoc) @string +[ + (string) + (encapsed_string) + (heredoc_body) + (nowdoc_body) + (shell_command_expression) +] @string +(escape_sequence) @constant.character.escape + (boolean) @constant.builtin.boolean (null) @constant.builtin (integer) @constant.numeric.integer (float) @constant.numeric.float (comment) @comment -"$" @operator +(goto_statement (name) @label) +(named_label_statement (name) @label) ; Keywords [ - "abstract" - "as" - "break" - "case" - "catch" - "class" - "const" - "continue" - "declare" "default" - "do" "echo" - "else" - "elseif" - "enddeclare" - "endforeach" - "endif" - "endswitch" - "endwhile" "enum" "extends" "final" - "finally" - "foreach" - "fn" - "function" + "goto" "global" - "if" "implements" - "include_once" - "include" "insteadof" - "interface" - "match" - "namespace" "new" "private" "protected" "public" +] @keyword + +[ + "if" + "else" + "elseif" + "endif" + "switch" + "endswitch" + "case" + "match" + "declare" + "enddeclare" + "??" +] @keyword.control.conditional + +[ + "for" + "endfor" + "foreach" + "endforeach" + "while" + "endwhile" + "do" +] @keyword.control.repeat + +[ + + "include_once" + "include" "require_once" "require" + "use" +] @keyword.control.import + +[ "return" - "static" - "switch" + "break" + "continue" + "yield" +] @keyword.control.return + +[ "throw" - "trait" "try" - "use" - "while" -] @keyword + "catch" + "finally" +] @keyword.control.exception + +[ + "as" + "or" + "xor" + "and" + "instanceof" +] @keyword.operator + +[ + "fn" + "function" +] @keyword.function + +[ + "namespace" + "class" + "interface" + "trait" + "abstract" +] @keyword.storage.type + +[ + "static" + "const" +] @keyword.storage.modifier + +[ + "," + ";" + ":" + "\\" + ] @punctuation.delimiter + +[ + (php_tag) + "?>" + "(" + ")" + "[" + "]" + "{" + "}" + "#[" +] @punctuation.bracket + +[ + "=" + + "." + "-" + "*" + "/" + "+" + "%" + "**" + + "~" + "|" + "^" + "&" + "<<" + ">>" + + "->" + "?->" + + "=>" + + "<" + "<=" + ">=" + ">" + "<>" + "==" + "!=" + "===" + "!==" + + "!" + "&&" + "||" + + ".=" + "-=" + "+=" + "*=" + "/=" + "%=" + "**=" + "&=" + "|=" + "^=" + "<<=" + ">>=" + "??=" + "--" + "++" + + "@" + "::" +] @operator From 0e5a4e55a497c58f68859edb48fd85854403b866 Mon Sep 17 00:00:00 2001 From: Erasin Wang Date: Wed, 8 Mar 2023 08:33:13 +0800 Subject: [PATCH 0062/1169] Update highlights for golang (#6204) - update tree-sitter-go - refine keywords - set package as namespace - add label --- languages.toml | 2 +- runtime/queries/go/highlights.scm | 75 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/languages.toml b/languages.toml index d8b57e976..0fd37a006 100644 --- a/languages.toml +++ b/languages.toml @@ -324,7 +324,7 @@ args = { mode = "local", processId = "{0}" } [[grammar]] name = "go" -source = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "05900faa3cdb5d2d8c8bd5e77ee698487e0a8611" } +source = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "64457ea6b73ef5422ed1687178d4545c3e91334a" } [[language]] name = "gomod" diff --git a/runtime/queries/go/highlights.scm b/runtime/queries/go/highlights.scm index 927bd95b0..b2d81e45d 100644 --- a/runtime/queries/go/highlights.scm +++ b/runtime/queries/go/highlights.scm @@ -19,6 +19,9 @@ (method_declaration name: (field_identifier) @function.method) +(method_spec + name: (field_identifier) @function.method) + ; Identifiers ((identifier) @constant (match? @constant "^[A-Z][A-Z\\d_]+$")) @@ -32,10 +35,19 @@ (match? @type.builtin "^(any|bool|byte|comparable|complex128|complex64|error|float32|float64|int|int16|int32|int64|int8|rune|string|uint|uint16|uint32|uint64|uint8|uintptr)$")) (type_identifier) @type +(type_spec + name: (type_identifier) @constructor) (field_identifier) @variable.other.member (identifier) @variable -(package_identifier) @variable +(package_identifier) @namespace + +(parameter_declaration (identifier) @variable.parameter) +(variadic_parameter_declaration (identifier) @variable.parameter) +(label_name) @label + +(const_spec + name: (identifier) @constant) ; Operators @@ -82,36 +94,57 @@ ; Keywords [ - "break" - "case" - "chan" - "const" - "continue" "default" - "defer" + "type" +] @keyword + +[ + "if" "else" - "fallthrough" + "switch" + "select" + "case" +] @keyword.control.conditional + +[ "for" - "func" - "go" - "goto" - "if" - "interface" - "map" "range" - "return" - "select" - "struct" - "switch" - "type" - "var" -] @keyword +] @keyword.control.repeat [ "import" "package" ] @keyword.control.import +[ + "return" + "continue" + "break" + "fallthrough" +] @keyword.control.return + +[ + "func" +] @keyword.function + +[ + "var" + "chan" + "interface" + "map" + "struct" +] @keyword.storage.type + +[ + "const" +] @keyword.storage.modifier + +[ + "defer" + "goto" + "go" +] @function.macro + ; Delimiters [ From f976c004e2efa4cb583b06827b44fef84bf925f5 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 8 Mar 2023 01:34:31 +0100 Subject: [PATCH 0063/1169] Allow LSP server to be stopped (#5964) --- book/src/generated/typable-cmd.md | 1 + helix-lsp/src/lib.rs | 10 ++++++++ helix-term/src/commands/typed.rs | 38 +++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index ab36997c7..badadc43d 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -49,6 +49,7 @@ | `:update` | Write changes only if the file has been modified. | | `:lsp-workspace-command` | Open workspace command picker | | `:lsp-restart` | Restarts the Language Server that is in use by the current doc | +| `:lsp-stop` | Stops 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. | | `:debug-remote`, `:dbg-tcp` | Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters. | diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 341d4a547..ca9d17ace 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -476,6 +476,16 @@ impl Registry { } } + pub fn stop(&mut self, language_config: &LanguageConfiguration) { + let scope = language_config.scope.clone(); + + if let Some((_, client)) = self.inner.remove(&scope) { + tokio::spawn(async move { + let _ = client.force_shutdown().await; + }); + } + } + pub fn get( &mut self, language_config: &LanguageConfiguration, diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index b0fd18a76..0ddca6df7 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1354,6 +1354,37 @@ fn lsp_restart( Ok(()) } +fn lsp_stop( + cx: &mut compositor::Context, + _args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + let doc = doc!(cx.editor); + + let ls_id = doc + .language_server() + .map(|ls| ls.id()) + .context("LSP not running for the current document")?; + + let config = doc + .language_config() + .context("LSP not defined for the current document")?; + cx.editor.language_servers.stop(config); + + for doc in cx.editor.documents_mut() { + if doc.language_server().map_or(false, |ls| ls.id() == ls_id) { + doc.set_language_server(None); + doc.set_diagnostics(Default::default()); + } + } + + Ok(()) +} + fn tree_sitter_scopes( cx: &mut compositor::Context, _args: &[Cow], @@ -2349,6 +2380,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: lsp_restart, completer: None, }, + TypableCommand { + name: "lsp-stop", + aliases: &[], + doc: "Stops the Language Server that is in use by the current doc", + fun: lsp_stop, + completer: None, + }, TypableCommand { name: "tree-sitter-scopes", aliases: &[], From c8e6857affdd286a5aff1e8f72fc428ea216076e Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 7 Feb 2023 09:18:44 +0100 Subject: [PATCH 0064/1169] Add a parser-combinator crate Parser-combinators are one of the simpler tools for building ad-hoc parsers. They're a good fit because they are... * Small: each parser / parser-combinator is around 10 LOC. * Functional: helix_core strives to be a functional set of utilities usable throughout the rest of the editor. * Flexible: use them to build any sort of ad-hoc parser. In the child commit, we'll parse LSP Snippet syntax using these new parser combinators. Why not use an existing parser-combinator crate? Existing popular parser-combinator crates have histories of making breaking changes (for example nom and combine). > Implementation note: I tried to not introduce a new trait since the > types can be expressed in terms of `impl Fn`s. The trait is necessary > to build `seq` implementations without a proc macro though, and also > allows us to use `&'static str`s very conveniently: see the trait > implementation for `&'static str`. --- Cargo.lock | 7 + Cargo.toml | 1 + helix-parsec/Cargo.toml | 14 + helix-parsec/src/lib.rs | 560 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 582 insertions(+) create mode 100644 helix-parsec/Cargo.toml create mode 100644 helix-parsec/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 96c9fcf96..affc6bd90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1149,6 +1149,13 @@ dependencies = [ "which", ] +[[package]] +name = "helix-parsec" +version = "0.6.0" +dependencies = [ + "regex", +] + [[package]] name = "helix-term" version = "0.6.0" diff --git a/Cargo.toml b/Cargo.toml index 016267fe3..aaa21659a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "helix-dap", "helix-loader", "helix-vcs", + "helix-parsec", "xtask", ] diff --git a/helix-parsec/Cargo.toml b/helix-parsec/Cargo.toml new file mode 100644 index 000000000..562df8ddd --- /dev/null +++ b/helix-parsec/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "helix-parsec" +version = "0.6.0" +authors = ["Blaž Hrastnik "] +edition = "2021" +license = "MPL-2.0" +description = "Parser combinators for Helix" +categories = ["editor"] +repository = "https://github.com/helix-editor/helix" +homepage = "https://helix-editor.com" +include = ["src/**/*", "README.md"] + +[dependencies] +regex = "1" diff --git a/helix-parsec/src/lib.rs b/helix-parsec/src/lib.rs new file mode 100644 index 000000000..c86a1a056 --- /dev/null +++ b/helix-parsec/src/lib.rs @@ -0,0 +1,560 @@ +//! Parser-combinator functions +//! +//! This module provides parsers and parser combinators which can be used +//! together to build parsers by functional composition. + +use regex::Regex; + +// This module implements parser combinators following https://bodil.lol/parser-combinators/. +// `sym` (trait implementation for `&'static str`), `map`, `pred` (filter), `one_or_more`, +// `zero_or_more`, as well as the `Parser` trait originate mostly from that post. +// The remaining parsers and parser combinators are either based on +// https://github.com/archseer/snippets.nvim/blob/a583da6ef130d2a4888510afd8c4e5ffd62d0dce/lua/snippet/parser.lua#L5-L138 +// or are novel. + +// When a parser matches the input successfully, it returns `Ok((next_input, some_value))` +// where the type of the returned value depends on the parser. If the parser fails to match, +// it returns `Err(input)`. +type ParseResult<'a, Output> = Result<(&'a str, Output), &'a str>; + +/// A parser or parser-combinator. +/// +/// Parser-combinators compose multiple parsers together to parse input. +/// For example, two basic parsers (`&'static str`s) may be combined with +/// a parser-combinator like [or] to produce a new parser. +/// +/// ``` +/// use helix_parsec::{or, Parser}; +/// let foo = "foo"; // matches "foo" literally +/// let bar = "bar"; // matches "bar" literally +/// let foo_or_bar = or(foo, bar); // matches either "foo" or "bar" +/// assert_eq!(Ok(("", "foo")), foo_or_bar.parse("foo")); +/// assert_eq!(Ok(("", "bar")), foo_or_bar.parse("bar")); +/// assert_eq!(Err("baz"), foo_or_bar.parse("baz")); +/// ``` +pub trait Parser<'a> { + type Output; + + fn parse(&self, input: &'a str) -> ParseResult<'a, Self::Output>; +} + +// Most parser-combinators are written as higher-order functions which take some +// parser(s) as input and return a new parser: a function that takes input and returns +// a parse result. The underlying implementation of [Parser::parse] for these functions +// is simply application. +#[doc(hidden)] +impl<'a, F, T> Parser<'a> for F +where + F: Fn(&'a str) -> ParseResult, +{ + type Output = T; + + fn parse(&self, input: &'a str) -> ParseResult<'a, Self::Output> { + self(input) + } +} + +/// A parser which matches the string literal exactly. +/// +/// This parser succeeds if the next characters in the input are equal to the given +/// string literal. +/// +/// Note that [str::parse] interferes with calling [Parser::parse] on string literals +/// directly; this trait implementation works when used within any parser combinator +/// but does not work on its own. To call [Parser::parse] on a parser for a string +/// literal, use the [token] parser. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{or, Parser}; +/// let parser = or("foo", "bar"); +/// assert_eq!(Ok(("", "foo")), parser.parse("foo")); +/// assert_eq!(Ok(("", "bar")), parser.parse("bar")); +/// assert_eq!(Err("baz"), parser.parse("baz")); +/// ``` +impl<'a> Parser<'a> for &'static str { + type Output = &'a str; + + fn parse(&self, input: &'a str) -> ParseResult<'a, Self::Output> { + match input.get(0..self.len()) { + Some(actual) if actual == *self => Ok((&input[self.len()..], &input[0..self.len()])), + _ => Err(input), + } + } +} + +// Parsers + +/// A parser which matches the given string literally. +/// +/// This function is a convenience for interpreting string literals as parsers +/// and is only necessary to avoid conflict with [str::parse]. See the documentation +/// for the `&'static str` implementation of [Parser]. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{token, Parser}; +/// let parser = token("foo"); +/// assert_eq!(Ok(("", "foo")), parser.parse("foo")); +/// assert_eq!(Err("bar"), parser.parse("bar")); +/// ``` +pub fn token<'a>(literal: &'static str) -> impl Parser<'a, Output = &'a str> { + literal +} + +/// A parser which matches the pattern described by the given regular expression. +/// +/// The pattern must match from the beginning of the input as if the regular expression +/// included the `^` anchor. Using a `^` anchor in the regular expression is +/// recommended in order to reduce any work done by the regex on non-matching input. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{pattern, Parser}; +/// use regex::Regex; +/// let regex = Regex::new(r"Hello, \w+!").unwrap(); +/// let parser = pattern(®ex); +/// assert_eq!(Ok(("", "Hello, world!")), parser.parse("Hello, world!")); +/// assert_eq!(Err("Hey, you!"), parser.parse("Hey, you!")); +/// assert_eq!(Err("Oh Hello, world!"), parser.parse("Oh Hello, world!")); +/// ``` +pub fn pattern<'a>(regex: &'a Regex) -> impl Parser<'a, Output = &'a str> { + move |input: &'a str| match regex.find(input) { + Some(match_) if match_.start() == 0 => { + Ok((&input[match_.end()..], &input[0..match_.end()])) + } + _ => Err(input), + } +} + +/// A parser which matches all values until the specified pattern is found. +/// +/// If the pattern is not found, this parser does not match. The input up to the +/// character which returns `true` is returned but not that character itself. +/// +/// If the pattern function returns true on the first input character, this +/// parser fails. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{take_until, Parser}; +/// let parser = take_until(|c| c == '.'); +/// assert_eq!(Ok((".bar", "foo")), parser.parse("foo.bar")); +/// assert_eq!(Err(".foo"), parser.parse(".foo")); +/// assert_eq!(Err("foo"), parser.parse("foo")); +/// ``` +pub fn take_until<'a, F>(pattern: F) -> impl Parser<'a, Output = &'a str> +where + F: Fn(char) -> bool, +{ + move |input: &'a str| match input.find(&pattern) { + Some(index) if index != 0 => Ok((&input[index..], &input[0..index])), + _ => Err(input), + } +} + +// Variadic parser combinators + +/// A parser combinator which matches a sequence of parsers in an all-or-nothing fashion. +/// +/// The returned value is a tuple containing the outputs of all parsers in order. Each +/// parser in the sequence may be typed differently. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{seq, Parser}; +/// let parser = seq!("<", "a", ">"); +/// assert_eq!(Ok(("", ("<", "a", ">"))), parser.parse("")); +/// assert_eq!(Err(""), parser.parse("")); +/// ``` +#[macro_export] +macro_rules! seq { + ($($parsers: expr),+ $(,)?) => { + ($($parsers),+) + } +} + +// Seq is implemented using trait-implementations of Parser for various size tuples. +// This allows sequences to be typed heterogeneously. +macro_rules! seq_impl { + ($($parser:ident),+) => { + #[allow(non_snake_case)] + impl<'a, $($parser),+> Parser<'a> for ($($parser),+) + where + $($parser: Parser<'a>),+ + { + type Output = ($($parser::Output),+); + + fn parse(&self, input: &'a str) -> ParseResult<'a, Self::Output> { + let ($($parser),+) = self; + seq_body_impl!(input, input, $($parser),+ ; ) + } + } + } +} + +macro_rules! seq_body_impl { + ($input:expr, $next_input:expr, $head:ident, $($tail:ident),+ ; $(,)? $($acc:ident),*) => { + match $head.parse($next_input) { + Ok((next_input, $head)) => seq_body_impl!($input, next_input, $($tail),+ ; $($acc),*, $head), + Err(_) => Err($input), + } + }; + ($input:expr, $next_input:expr, $last:ident ; $(,)? $($acc:ident),*) => { + match $last.parse($next_input) { + Ok((next_input, last)) => Ok((next_input, ($($acc),+, last))), + Err(_) => Err($input), + } + } +} + +seq_impl!(A, B); +seq_impl!(A, B, C); +seq_impl!(A, B, C, D); +seq_impl!(A, B, C, D, E); +seq_impl!(A, B, C, D, E, F); +seq_impl!(A, B, C, D, E, F, G); +seq_impl!(A, B, C, D, E, F, G, H); +seq_impl!(A, B, C, D, E, F, G, H, I); +seq_impl!(A, B, C, D, E, F, G, H, I, J); + +/// A parser combinator which chooses the first of the input parsers which matches +/// successfully. +/// +/// All input parsers must have the same output type. This is a variadic form for [or]. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{choice, or, Parser}; +/// let parser = choice!("foo", "bar", "baz"); +/// assert_eq!(Ok(("", "foo")), parser.parse("foo")); +/// assert_eq!(Ok(("", "bar")), parser.parse("bar")); +/// assert_eq!(Err("quiz"), parser.parse("quiz")); +/// ``` +#[macro_export] +macro_rules! choice { + ($parser: expr $(,)?) => { + $parser + }; + ($parser: expr, $($rest: expr),+ $(,)?) => { + or($parser, choice!($($rest),+)) + } +} + +// Ordinary parser combinators + +/// A parser combinator which takes a parser as input and maps the output using the +/// given transformation function. +/// +/// This corresponds to [Result::map]. The value is only mapped if the input parser +/// matches against input. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{map, Parser}; +/// let parser = map("123", |s| s.parse::().unwrap()); +/// assert_eq!(Ok(("", 123)), parser.parse("123")); +/// assert_eq!(Err("abc"), parser.parse("abc")); +/// ``` +pub fn map<'a, P, F, T>(parser: P, map_fn: F) -> impl Parser<'a, Output = T> +where + P: Parser<'a>, + F: Fn(P::Output) -> T, +{ + move |input| { + parser + .parse(input) + .map(|(next_input, result)| (next_input, map_fn(result))) + } +} + +/// A parser combinator which succeeds if the given parser matches the input and +/// the given `filter_map_fn` returns `Some`. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{filter_map, take_until, Parser}; +/// let parser = filter_map(take_until(|c| c == '.'), |s| s.parse::().ok()); +/// assert_eq!(Ok((".456", 123)), parser.parse("123.456")); +/// assert_eq!(Err("abc.def"), parser.parse("abc.def")); +/// ``` +pub fn filter_map<'a, P, F, T>(parser: P, filter_map_fn: F) -> impl Parser<'a, Output = T> +where + P: Parser<'a>, + F: Fn(P::Output) -> Option, +{ + move |input| match parser.parse(input) { + Ok((next_input, value)) => match filter_map_fn(value) { + Some(value) => Ok((next_input, value)), + None => Err(input), + }, + Err(_) => Err(input), + } +} + +/// A parser combinator which succeeds if the first given parser matches the input and +/// the second given parse also matches. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{reparse_as, take_until, one_or_more, Parser}; +/// let parser = reparse_as(take_until(|c| c == '/'), one_or_more("a")); +/// assert_eq!(Ok(("/bb", vec!["a", "a"])), parser.parse("aa/bb")); +/// ``` +pub fn reparse_as<'a, P1, P2, T>(parser1: P1, parser2: P2) -> impl Parser<'a, Output = T> +where + P1: Parser<'a, Output = &'a str>, + P2: Parser<'a, Output = T>, +{ + filter_map(parser1, move |str| { + parser2.parse(str).map(|(_, value)| value).ok() + }) +} + +/// A parser combinator which only matches the input when the predicate function +/// returns true. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{filter, take_until, Parser}; +/// let parser = filter(take_until(|c| c == '.'), |s| s == &"123"); +/// assert_eq!(Ok((".456", "123")), parser.parse("123.456")); +/// assert_eq!(Err("456.123"), parser.parse("456.123")); +/// ``` +pub fn filter<'a, P, F, T>(parser: P, pred_fn: F) -> impl Parser<'a, Output = T> +where + P: Parser<'a, Output = T>, + F: Fn(&P::Output) -> bool, +{ + move |input| { + if let Ok((next_input, value)) = parser.parse(input) { + if pred_fn(&value) { + return Ok((next_input, value)); + } + } + Err(input) + } +} + +/// A parser combinator which matches either of the input parsers. +/// +/// Both parsers must have the same output type. For a variadic form which +/// can take any number of parsers, use `choice!`. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{or, Parser}; +/// let parser = or("foo", "bar"); +/// assert_eq!(Ok(("", "foo")), parser.parse("foo")); +/// assert_eq!(Ok(("", "bar")), parser.parse("bar")); +/// assert_eq!(Err("baz"), parser.parse("baz")); +/// ``` +pub fn or<'a, P1, P2, T>(parser1: P1, parser2: P2) -> impl Parser<'a, Output = T> +where + P1: Parser<'a, Output = T>, + P2: Parser<'a, Output = T>, +{ + move |input| match parser1.parse(input) { + ok @ Ok(_) => ok, + Err(_) => parser2.parse(input), + } +} + +/// A parser combinator which attempts to match the given parser, returning a +/// `None` output value if the parser does not match. +/// +/// The parser produced with this combinator always succeeds. If the given parser +/// succeeds, `Some(value)` is returned where `value` is the output of the given +/// parser. Otherwise, `None`. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{optional, Parser}; +/// let parser = optional("foo"); +/// assert_eq!(Ok(("bar", Some("foo"))), parser.parse("foobar")); +/// assert_eq!(Ok(("bar", None)), parser.parse("bar")); +/// ``` +pub fn optional<'a, P, T>(parser: P) -> impl Parser<'a, Output = Option> +where + P: Parser<'a, Output = T>, +{ + move |input| match parser.parse(input) { + Ok((next_input, value)) => Ok((next_input, Some(value))), + Err(_) => Ok((input, None)), + } +} + +/// A parser combinator which runs the given parsers in sequence and returns the +/// value of `left` if both are matched. +/// +/// This is useful for two-element sequences in which you only want the output +/// value of the `left` parser. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{left, Parser}; +/// let parser = left("foo", "bar"); +/// assert_eq!(Ok(("", "foo")), parser.parse("foobar")); +/// ``` +pub fn left<'a, L, R, T>(left: L, right: R) -> impl Parser<'a, Output = T> +where + L: Parser<'a, Output = T>, + R: Parser<'a>, +{ + map(seq!(left, right), |(left_value, _)| left_value) +} + +/// A parser combinator which runs the given parsers in sequence and returns the +/// value of `right` if both are matched. +/// +/// This is useful for two-element sequences in which you only want the output +/// value of the `right` parser. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{right, Parser}; +/// let parser = right("foo", "bar"); +/// assert_eq!(Ok(("", "bar")), parser.parse("foobar")); +/// ``` +pub fn right<'a, L, R, T>(left: L, right: R) -> impl Parser<'a, Output = T> +where + L: Parser<'a>, + R: Parser<'a, Output = T>, +{ + map(seq!(left, right), |(_, right_value)| right_value) +} + +/// A parser combinator which matches the given parser against the input zero or +/// more times. +/// +/// This parser always succeeds and returns the empty Vec when it matched zero +/// times. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{zero_or_more, Parser}; +/// let parser = zero_or_more("a"); +/// assert_eq!(Ok(("", vec![])), parser.parse("")); +/// assert_eq!(Ok(("", vec!["a"])), parser.parse("a")); +/// assert_eq!(Ok(("", vec!["a", "a"])), parser.parse("aa")); +/// assert_eq!(Ok(("bb", vec![])), parser.parse("bb")); +/// ``` +pub fn zero_or_more<'a, P, T>(parser: P) -> impl Parser<'a, Output = Vec> +where + P: Parser<'a, Output = T>, +{ + move |mut input| { + let mut values = Vec::new(); + + while let Ok((next_input, value)) = parser.parse(input) { + input = next_input; + values.push(value); + } + + Ok((input, values)) + } +} + +/// A parser combinator which matches the given parser against the input one or +/// more times. +/// +/// This parser combinator acts the same as [zero_or_more] but must match at +/// least once. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{one_or_more, Parser}; +/// let parser = one_or_more("a"); +/// assert_eq!(Err(""), parser.parse("")); +/// assert_eq!(Ok(("", vec!["a"])), parser.parse("a")); +/// assert_eq!(Ok(("", vec!["a", "a"])), parser.parse("aa")); +/// assert_eq!(Err("bb"), parser.parse("bb")); +/// ``` +pub fn one_or_more<'a, P, T>(parser: P) -> impl Parser<'a, Output = Vec> +where + P: Parser<'a, Output = T>, +{ + move |mut input| { + let mut values = Vec::new(); + + match parser.parse(input) { + Ok((next_input, value)) => { + input = next_input; + values.push(value); + } + Err(err) => return Err(err), + } + + while let Ok((next_input, value)) = parser.parse(input) { + input = next_input; + values.push(value); + } + + Ok((input, values)) + } +} + +/// A parser combinator which matches one or more instances of the given parser +/// interspersed with the separator parser. +/// +/// Output values of the separator parser are discarded. +/// +/// This is typically used to parse function arguments or list items. +/// +/// # Examples +/// +/// ```rust +/// use helix_parsec::{sep, Parser}; +/// let parser = sep("a", ","); +/// assert_eq!(Ok(("", vec!["a", "a", "a"])), parser.parse("a,a,a")); +/// ``` +pub fn sep<'a, P, S, T>(parser: P, separator: S) -> impl Parser<'a, Output = Vec> +where + P: Parser<'a, Output = T>, + S: Parser<'a>, +{ + move |mut input| { + let mut values = Vec::new(); + + match parser.parse(input) { + Ok((next_input, value)) => { + input = next_input; + values.push(value); + } + Err(err) => return Err(err), + } + + loop { + match separator.parse(input) { + Ok((next_input, _)) => input = next_input, + Err(_) => break, + } + + match parser.parse(input) { + Ok((next_input, value)) => { + input = next_input; + values.push(value); + } + Err(_) => break, + } + } + + Ok((input, values)) + } +} From 9c12e0fb765f065b43788da670ffb98159d64a5f Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 3 Oct 2022 20:41:31 -0500 Subject: [PATCH 0065/1169] Add parser for LSP snippet --- Cargo.lock | 1 + helix-lsp/Cargo.toml | 2 + helix-lsp/src/lib.rs | 1 + helix-lsp/src/snippet.rs | 367 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 371 insertions(+) create mode 100644 helix-lsp/src/snippet.rs diff --git a/Cargo.lock b/Cargo.lock index affc6bd90..d069cf41d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1141,6 +1141,7 @@ dependencies = [ "helix-loader", "log", "lsp-types", + "once_cell", "serde", "serde_json", "thiserror", diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index c1f091107..7c71fa9f2 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -14,6 +14,7 @@ homepage = "https://helix-editor.com" [dependencies] helix-core = { version = "0.6", path = "../helix-core" } helix-loader = { version = "0.6", path = "../helix-loader" } +helix-parsec = { version = "0.6", path = "../helix-parsec" } anyhow = "1.0" futures-executor = "0.3" @@ -26,3 +27,4 @@ thiserror = "1.0" tokio = { version = "1.26", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio-stream = "0.1.12" which = "4.4" +once_cell = "1.15" diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index ca9d17ace..cce848ab1 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -1,5 +1,6 @@ mod client; pub mod jsonrpc; +pub mod snippet; mod transport; pub use client::Client; diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs new file mode 100644 index 000000000..529c3b975 --- /dev/null +++ b/helix-lsp/src/snippet.rs @@ -0,0 +1,367 @@ +use anyhow::{anyhow, Result}; + +use crate::{util::lsp_pos_to_pos, OffsetEncoding}; + +#[derive(Debug, PartialEq, Eq)] +pub enum CaseChange { + Upcase, + Downcase, + Capitalize, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum FormatItem<'a> { + Text(&'a str), + Capture(usize), + CaseChange(usize, CaseChange), + Conditional(usize, Option<&'a str>, Option<&'a str>), +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Regex<'a> { + value: &'a str, + replacement: Vec>, + options: Option<&'a str>, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum SnippetElement<'a> { + Tabstop { + tabstop: usize, + }, + Placeholder { + tabstop: usize, + value: Box>, + }, + Choice { + tabstop: usize, + choices: Vec<&'a str>, + }, + Variable { + name: &'a str, + default: Option<&'a str>, + regex: Option>, + }, + Text(&'a str), +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Snippet<'a> { + elements: Vec>, +} + +pub fn parse<'a>(s: &'a str) -> Result> { + parser::parse(s).map_err(|rest| anyhow!("Failed to parse snippet. Remaining input: {}", rest)) +} + +mod parser { + use helix_core::regex; + use once_cell::sync::Lazy; + + use helix_parsec::*; + + use super::{CaseChange, FormatItem, Regex, Snippet, SnippetElement}; + + /* + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax + + any ::= tabstop | placeholder | choice | variable | text + tabstop ::= '$' int | '${' int '}' + placeholder ::= '${' int ':' any '}' + choice ::= '${' int '|' text (',' text)* '|}' + variable ::= '$' var | '${' var }' + | '${' var ':' any '}' + | '${' var '/' regex '/' (format | text)+ '/' options '}' + format ::= '$' int | '${' int '}' + | '${' int ':' '/upcase' | '/downcase' | '/capitalize' '}' + | '${' int ':+' if '}' + | '${' int ':?' if ':' else '}' + | '${' int ':-' else '}' | '${' int ':' else '}' + regex ::= Regular Expression value (ctor-string) + options ::= Regular Expression option (ctor-options) + var ::= [_a-zA-Z] [_a-zA-Z0-9]* + int ::= [0-9]+ + text ::= .* + if ::= text + else ::= text + */ + + static DIGIT: Lazy = Lazy::new(|| regex::Regex::new(r"^[0-9]+").unwrap()); + static VARIABLE: Lazy = + Lazy::new(|| regex::Regex::new(r"^[_a-zA-Z][_a-zA-Z0-9]*").unwrap()); + static TEXT: Lazy = Lazy::new(|| regex::Regex::new(r"^[^\$]+").unwrap()); + + fn var<'a>() -> impl Parser<'a, Output = &'a str> { + pattern(&VARIABLE) + } + + fn digit<'a>() -> impl Parser<'a, Output = usize> { + filter_map(pattern(&DIGIT), |s| s.parse().ok()) + } + + fn case_change<'a>() -> impl Parser<'a, Output = CaseChange> { + use CaseChange::*; + + choice!( + map("upcase", |_| Upcase), + map("downcase", |_| Downcase), + map("capitalize", |_| Capitalize), + ) + } + + fn format<'a>() -> impl Parser<'a, Output = FormatItem<'a>> { + use FormatItem::*; + + choice!( + // '$' int + map(right("$", digit()), Capture), + // '${' int '}' + map(seq!("${", digit(), "}"), |seq| Capture(seq.1)), + // '${' int ':' '/upcase' | '/downcase' | '/capitalize' '}' + map(seq!("${", digit(), ":/", case_change(), "}"), |seq| { + CaseChange(seq.1, seq.3) + }), + // '${' int ':+' if '}' + map( + seq!("${", digit(), ":+", take_until(|c| c == '}'), "}"), + |seq| { Conditional(seq.1, Some(seq.3), None) } + ), + // '${' int ':?' if ':' else '}' + map( + seq!( + "${", + digit(), + ":?", + take_until(|c| c == ':'), + ":", + take_until(|c| c == '}'), + "}" + ), + |seq| { Conditional(seq.1, Some(seq.3), Some(seq.5)) } + ), + // '${' int ':-' else '}' | '${' int ':' else '}' + map( + seq!( + "${", + digit(), + ":", + optional("-"), + take_until(|c| c == '}'), + "}" + ), + |seq| { Conditional(seq.1, None, Some(seq.4)) } + ), + // Any text + map(pattern(&TEXT), Text), + ) + } + + fn regex<'a>() -> impl Parser<'a, Output = Regex<'a>> { + let replacement = reparse_as(take_until(|c| c == '/'), one_or_more(format())); + + map( + seq!( + "/", + take_until(|c| c == '/'), + "/", + replacement, + "/", + optional(take_until(|c| c == '}')), + ), + |(_, value, _, replacement, _, options)| Regex { + value, + replacement, + options, + }, + ) + } + + fn tabstop<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { + map( + or( + right("$", digit()), + map(seq!("${", digit(), "}"), |values| values.1), + ), + |digit| SnippetElement::Tabstop { tabstop: digit }, + ) + } + + fn placeholder<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { + // TODO: why doesn't parse_as work? + // let value = reparse_as(take_until(|c| c == '}'), anything()); + let value = filter_map(take_until(|c| c == '}'), |s| { + anything().parse(s).map(|parse_result| parse_result.1).ok() + }); + + map(seq!("${", digit(), ":", value, "}"), |seq| { + SnippetElement::Placeholder { + tabstop: seq.1, + value: Box::new(seq.3), + } + }) + } + + fn choice<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { + map( + seq!( + "${", + digit(), + "|", + sep(take_until(|c| c == ',' || c == '|'), ","), + "|}", + ), + |seq| SnippetElement::Choice { + tabstop: seq.1, + choices: seq.3, + }, + ) + } + + fn variable<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { + choice!( + // $var + map(right("$", var()), |name| SnippetElement::Variable { + name, + default: None, + regex: None, + }), + // ${var:default} + map( + seq!("${", var(), ":", take_until(|c| c == '}'), "}",), + |values| SnippetElement::Variable { + name: values.1, + default: Some(values.3), + regex: None, + } + ), + // ${var/value/format/options} + map(seq!("${", var(), regex(), "}"), |values| { + SnippetElement::Variable { + name: values.1, + default: None, + regex: Some(values.2), + } + }), + ) + } + + fn text<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { + map(pattern(&TEXT), SnippetElement::Text) + } + + fn anything<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { + choice!(tabstop(), placeholder(), choice(), variable(), text()) + } + + fn snippet<'a>() -> impl Parser<'a, Output = Snippet<'a>> { + map(one_or_more(anything()), |parts| Snippet { elements: parts }) + } + + pub fn parse(s: &str) -> Result { + snippet().parse(s).map(|(_input, elements)| elements) + } + + #[cfg(test)] + mod test { + use super::SnippetElement::*; + use super::*; + + #[test] + fn empty_string_is_error() { + assert_eq!(Err(""), parse("")); + } + + #[test] + fn parse_placeholders_in_function_call() { + assert_eq!( + Ok(Snippet { + elements: vec![ + Text("match("), + Placeholder { + tabstop: 1, + value: Box::new(Text("Arg1")), + }, + Text(")") + ] + }), + parse("match(${1:Arg1})") + ) + } + + #[test] + fn parse_placeholders_in_statement() { + assert_eq!( + Ok(Snippet { + elements: vec![ + Text("local "), + Placeholder { + tabstop: 1, + value: Box::new(Text("var")), + }, + Text(" = "), + Placeholder { + tabstop: 1, + value: Box::new(Text("value")), + }, + ] + }), + parse("local ${1:var} = ${1:value}") + ) + } + + #[test] + fn parse_all() { + assert_eq!( + Ok(Snippet { + elements: vec![ + Text("hello "), + Tabstop { tabstop: 1 }, + Tabstop { tabstop: 2 }, + Text(" "), + Choice { + tabstop: 1, + choices: vec!["one", "two", "three"] + }, + Text(" "), + Variable { + name: "name", + default: Some("foo"), + regex: None + }, + Text(" "), + Variable { + name: "var", + default: None, + regex: None + }, + Text(" "), + Variable { + name: "TM", + default: None, + regex: None + }, + ] + }), + parse("hello $1${2} ${1|one,two,three|} ${name:foo} $var $TM") + ); + } + + #[test] + fn regex_capture_replace() { + assert_eq!( + Ok(Snippet { + elements: vec![Variable { + name: "TM_FILENAME", + default: None, + regex: Some(Regex { + value: "(.*).+$", + replacement: vec![FormatItem::Capture(1)], + options: None, + }), + }] + }), + parse("${TM_FILENAME/(.*).+$/$1/}") + ); + } + } +} From e973b71c83dcefbdb3a748f28ce3bfd51e9cd842 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 7 Feb 2023 20:15:39 +0100 Subject: [PATCH 0066/1169] Optimize LSP snippet parsing --- Cargo.lock | 1 - helix-lsp/Cargo.toml | 1 - helix-lsp/src/snippet.rs | 45 +++++++++++++++++++++++++--------------- helix-parsec/src/lib.rs | 29 ++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d069cf41d..affc6bd90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1141,7 +1141,6 @@ dependencies = [ "helix-loader", "log", "lsp-types", - "once_cell", "serde", "serde_json", "thiserror", diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 7c71fa9f2..9d76822dc 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -27,4 +27,3 @@ thiserror = "1.0" tokio = { version = "1.26", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio-stream = "0.1.12" which = "4.4" -once_cell = "1.15" diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index 529c3b975..27b103d5d 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -50,14 +50,11 @@ pub struct Snippet<'a> { elements: Vec>, } -pub fn parse<'a>(s: &'a str) -> Result> { +pub fn parse(s: &str) -> Result> { parser::parse(s).map_err(|rest| anyhow!("Failed to parse snippet. Remaining input: {}", rest)) } mod parser { - use helix_core::regex; - use once_cell::sync::Lazy; - use helix_parsec::*; use super::{CaseChange, FormatItem, Regex, Snippet, SnippetElement}; @@ -86,17 +83,34 @@ mod parser { else ::= text */ - static DIGIT: Lazy = Lazy::new(|| regex::Regex::new(r"^[0-9]+").unwrap()); - static VARIABLE: Lazy = - Lazy::new(|| regex::Regex::new(r"^[_a-zA-Z][_a-zA-Z0-9]*").unwrap()); - static TEXT: Lazy = Lazy::new(|| regex::Regex::new(r"^[^\$]+").unwrap()); - fn var<'a>() -> impl Parser<'a, Output = &'a str> { - pattern(&VARIABLE) + // var = [_a-zA-Z][_a-zA-Z0-9]* + move |input: &'a str| match input + .char_indices() + .take_while(|(p, c)| { + *c == '_' + || if *p == 0 { + c.is_ascii_alphabetic() + } else { + c.is_ascii_alphanumeric() + } + }) + .last() + { + Some((index, c)) if index >= 1 => { + let index = index + c.len_utf8(); + Ok((&input[index..], &input[0..index])) + } + _ => Err(input), + } + } + + fn text<'a>() -> impl Parser<'a, Output = &'a str> { + take_while(|c| c != '$') } fn digit<'a>() -> impl Parser<'a, Output = usize> { - filter_map(pattern(&DIGIT), |s| s.parse().ok()) + filter_map(take_while(|c| c.is_ascii_digit()), |s| s.parse().ok()) } fn case_change<'a>() -> impl Parser<'a, Output = CaseChange> { @@ -152,7 +166,7 @@ mod parser { |seq| { Conditional(seq.1, None, Some(seq.4)) } ), // Any text - map(pattern(&TEXT), Text), + map(text(), Text), ) } @@ -245,12 +259,9 @@ mod parser { ) } - fn text<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { - map(pattern(&TEXT), SnippetElement::Text) - } - fn anything<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { - choice!(tabstop(), placeholder(), choice(), variable(), text()) + let text = map(text(), SnippetElement::Text); + choice!(tabstop(), placeholder(), choice(), variable(), text) } fn snippet<'a>() -> impl Parser<'a, Output = Snippet<'a>> { diff --git a/helix-parsec/src/lib.rs b/helix-parsec/src/lib.rs index c86a1a056..bfa981e58 100644 --- a/helix-parsec/src/lib.rs +++ b/helix-parsec/src/lib.rs @@ -157,6 +157,35 @@ where } } +/// A parser which matches all values until the specified pattern no longer match. +/// +/// This parser only ever fails if the input has a length of zero. +/// +/// # Examples +/// +/// ``` +/// use helix_parsec::{take_while, Parser}; +/// let parser = take_while(|c| c == '1'); +/// assert_eq!(Ok(("2", "11")), parser.parse("112")); +/// assert_eq!(Err("22"), parser.parse("22")); +/// ``` +pub fn take_while<'a, F>(pattern: F) -> impl Parser<'a, Output = &'a str> +where + F: Fn(char) -> bool, +{ + move |input: &'a str| match input + .char_indices() + .take_while(|(_p, c)| pattern(*c)) + .last() + { + Some((index, c)) => { + let index = index + c.len_utf8(); + Ok((&input[index..], &input[0..index])) + } + _ => Err(input), + } +} + // Variadic parser combinators /// A parser combinator which matches a sequence of parsers in an all-or-nothing fashion. From 3f90dafa3c4875cb33f404392552edc2381e6bf7 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 16 Feb 2023 11:21:53 +0100 Subject: [PATCH 0067/1169] Remove now unused the pattern combinator --- Cargo.lock | 3 --- helix-parsec/Cargo.toml | 1 - helix-parsec/src/lib.rs | 28 ---------------------------- 3 files changed, 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index affc6bd90..eec2a9766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1152,9 +1152,6 @@ dependencies = [ [[package]] name = "helix-parsec" version = "0.6.0" -dependencies = [ - "regex", -] [[package]] name = "helix-term" diff --git a/helix-parsec/Cargo.toml b/helix-parsec/Cargo.toml index 562df8ddd..505a4247e 100644 --- a/helix-parsec/Cargo.toml +++ b/helix-parsec/Cargo.toml @@ -11,4 +11,3 @@ homepage = "https://helix-editor.com" include = ["src/**/*", "README.md"] [dependencies] -regex = "1" diff --git a/helix-parsec/src/lib.rs b/helix-parsec/src/lib.rs index bfa981e58..e09814b81 100644 --- a/helix-parsec/src/lib.rs +++ b/helix-parsec/src/lib.rs @@ -3,8 +3,6 @@ //! This module provides parsers and parser combinators which can be used //! together to build parsers by functional composition. -use regex::Regex; - // This module implements parser combinators following https://bodil.lol/parser-combinators/. // `sym` (trait implementation for `&'static str`), `map`, `pred` (filter), `one_or_more`, // `zero_or_more`, as well as the `Parser` trait originate mostly from that post. @@ -104,32 +102,6 @@ pub fn token<'a>(literal: &'static str) -> impl Parser<'a, Output = &'a str> { literal } -/// A parser which matches the pattern described by the given regular expression. -/// -/// The pattern must match from the beginning of the input as if the regular expression -/// included the `^` anchor. Using a `^` anchor in the regular expression is -/// recommended in order to reduce any work done by the regex on non-matching input. -/// -/// # Examples -/// -/// ``` -/// use helix_parsec::{pattern, Parser}; -/// use regex::Regex; -/// let regex = Regex::new(r"Hello, \w+!").unwrap(); -/// let parser = pattern(®ex); -/// assert_eq!(Ok(("", "Hello, world!")), parser.parse("Hello, world!")); -/// assert_eq!(Err("Hey, you!"), parser.parse("Hey, you!")); -/// assert_eq!(Err("Oh Hello, world!"), parser.parse("Oh Hello, world!")); -/// ``` -pub fn pattern<'a>(regex: &'a Regex) -> impl Parser<'a, Output = &'a str> { - move |input: &'a str| match regex.find(input) { - Some(match_) if match_.start() == 0 => { - Ok((&input[match_.end()..], &input[0..match_.end()])) - } - _ => Err(input), - } -} - /// A parser which matches all values until the specified pattern is found. /// /// If the pattern is not found, this parser does not match. The input up to the From b9b1ec22084cfe82ced7e34412dab0351828fc53 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sat, 22 Oct 2022 09:52:25 -0500 Subject: [PATCH 0068/1169] Apply snippets as transactions --- Cargo.lock | 1 + helix-lsp/src/snippet.rs | 108 ++++++++++++++++++++++++++++++++ helix-term/src/ui/completion.rs | 46 +++++++++++--- 3 files changed, 145 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eec2a9766..cc7265f33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1139,6 +1139,7 @@ dependencies = [ "futures-util", "helix-core", "helix-loader", + "helix-parsec", "log", "lsp-types", "serde", diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index 27b103d5d..f74237496 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use anyhow::{anyhow, Result}; use crate::{util::lsp_pos_to_pos, OffsetEncoding}; @@ -54,6 +56,112 @@ pub fn parse(s: &str) -> Result> { parser::parse(s).map_err(|rest| anyhow!("Failed to parse snippet. Remaining input: {}", rest)) } +pub fn into_transaction<'a>( + snippet: Snippet<'a>, + doc: &helix_core::Rope, + selection: &helix_core::Selection, + edit: &lsp_types::TextEdit, + line_ending: &str, + offset_encoding: OffsetEncoding, +) -> helix_core::Transaction { + use helix_core::{smallvec, Range, Selection, Transaction}; + use SnippetElement::*; + + let text = doc.slice(..); + let primary_cursor = selection.primary().cursor(text); + + let start_offset = match lsp_pos_to_pos(doc, edit.range.start, offset_encoding) { + Some(start) => start as i128 - primary_cursor as i128, + None => return Transaction::new(doc), + }; + let end_offset = match lsp_pos_to_pos(doc, edit.range.end, offset_encoding) { + Some(end) => end as i128 - primary_cursor as i128, + None => return Transaction::new(doc), + }; + + let newline_with_offset = format!( + "{line_ending}{blank:width$}", + width = edit.range.start.character as usize, + blank = "" + ); + + let mut insert = String::new(); + let mut offset = (primary_cursor as i128 + start_offset) as usize; + let mut tabstops: Vec = Vec::new(); + + for element in snippet.elements { + match element { + Text(text) => { + // small optimization to avoid calling replace when it's unnecessary + let text = if text.contains('\n') { + Cow::Owned(text.replace('\n', &newline_with_offset)) + } else { + Cow::Borrowed(text) + }; + offset += text.chars().count(); + insert.push_str(&text); + } + Variable { + name: _name, + regex: None, + r#default, + } => { + // TODO: variables. For now, fall back to the default, which defaults to "". + let text = r#default.unwrap_or_default(); + offset += text.chars().count(); + insert.push_str(text); + } + Tabstop { .. } => { + // TODO: tabstop indexing: 0 is final cursor position. 1,2,.. are positions. + // TODO: merge tabstops with the same index + tabstops.push(Range::point(offset)); + } + Placeholder { + tabstop: _tabstop, + value, + } => match value.as_ref() { + // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html + // would make this a bit nicer + Text(text) => { + let len_chars = text.chars().count(); + tabstops.push(Range::new(offset, offset + len_chars + 1)); + offset += len_chars; + insert.push_str(text); + } + other => { + log::error!( + "Discarding snippet: generating a transaction for placeholder contents {:?} is unimplemented.", + other + ); + return Transaction::new(doc); + } + }, + other => { + log::error!( + "Discarding snippet: generating a transaction for {:?} is unimplemented.", + other + ); + return Transaction::new(doc); + } + } + } + + let transaction = Transaction::change_by_selection(doc, selection, |range| { + let cursor = range.cursor(text); + ( + (cursor as i128 + start_offset) as usize, + (cursor as i128 + end_offset) as usize, + Some(insert.clone().into()), + ) + }); + + if let Some(first) = tabstops.first() { + transaction.with_selection(Selection::new(smallvec![*first], 0)) + } else { + transaction + } +} + mod parser { use helix_parsec::*; diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index a24da20a9..6897305de 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -119,7 +119,9 @@ impl Completion { start_offset: usize, trigger_offset: usize, ) -> Transaction { - let transaction = if let Some(edit) = &item.text_edit { + use helix_lsp::snippet; + + if let Some(edit) = &item.text_edit { let edit = match edit { lsp::CompletionTextEdit::Edit(edit) => edit.clone(), lsp::CompletionTextEdit::InsertAndReplace(item) => { @@ -128,12 +130,38 @@ impl Completion { } }; - util::generate_transaction_from_completion_edit( - doc.text(), - doc.selection(view_id), - edit, - offset_encoding, // TODO: should probably transcode in Client - ) + if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET)) + || matches!( + item.insert_text_format, + Some(lsp::InsertTextFormat::SNIPPET) + ) + { + match snippet::parse(&edit.new_text) { + Ok(snippet) => snippet::into_transaction( + snippet, + doc.text(), + doc.selection(view_id), + &edit, + doc.line_ending.as_str(), + offset_encoding, + ), + Err(err) => { + log::error!( + "Failed to parse snippet: {:?}, remaining output: {}", + &edit.new_text, + err + ); + Transaction::new(doc.text()) + } + } + } else { + util::generate_transaction_from_completion_edit( + doc.text(), + doc.selection(view_id), + edit, + offset_encoding, // TODO: should probably transcode in Client + ) + } } else { let text = item.insert_text.as_ref().unwrap_or(&item.label); // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯ @@ -157,9 +185,7 @@ impl Completion { (cursor, cursor, Some(text.into())) }) - }; - - transaction + } } fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec { From d2af31b916114dd7ebbc3390eb44c0d72e06ec50 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sun, 30 Oct 2022 16:39:27 -0500 Subject: [PATCH 0069/1169] LSP: Advertise snippet support --- helix-lsp/src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 3f88b3523..95f3ea348 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -320,7 +320,7 @@ impl Client { text_document: Some(lsp::TextDocumentClientCapabilities { completion: Some(lsp::CompletionClientCapabilities { completion_item: Some(lsp::CompletionItemCapability { - snippet_support: Some(false), + snippet_support: Some(true), resolve_support: Some(lsp::CompletionItemCapabilityResolveSupport { properties: vec![ String::from("documentation"), From ded4381728bcbbcee3d3fec1b861038229b806d3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 8 Feb 2023 09:36:15 +0100 Subject: [PATCH 0070/1169] Implement LSP snippet tabstops sorting and merging --- helix-lsp/src/snippet.rs | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index f74237496..87c839f94 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use anyhow::{anyhow, Result}; +use helix_core::SmallVec; use crate::{util::lsp_pos_to_pos, OffsetEncoding}; @@ -87,7 +88,7 @@ pub fn into_transaction<'a>( let mut insert = String::new(); let mut offset = (primary_cursor as i128 + start_offset) as usize; - let mut tabstops: Vec = Vec::new(); + let mut tabstops: Vec<(usize, Range)> = Vec::new(); for element in snippet.elements { match element { @@ -111,20 +112,15 @@ pub fn into_transaction<'a>( offset += text.chars().count(); insert.push_str(text); } - Tabstop { .. } => { - // TODO: tabstop indexing: 0 is final cursor position. 1,2,.. are positions. - // TODO: merge tabstops with the same index - tabstops.push(Range::point(offset)); + Tabstop { tabstop } => { + tabstops.push((tabstop, Range::point(offset))); } - Placeholder { - tabstop: _tabstop, - value, - } => match value.as_ref() { + Placeholder { tabstop, value } => match value.as_ref() { // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html // would make this a bit nicer Text(text) => { let len_chars = text.chars().count(); - tabstops.push(Range::new(offset, offset + len_chars + 1)); + tabstops.push((tabstop, Range::new(offset, offset + len_chars + 1))); offset += len_chars; insert.push_str(text); } @@ -155,8 +151,25 @@ pub fn into_transaction<'a>( ) }); - if let Some(first) = tabstops.first() { - transaction.with_selection(Selection::new(smallvec![*first], 0)) + // sort in ascending order (except for 0, which should always be the last one (per lsp doc)) + tabstops.sort_unstable_by_key(|(n, _range)| if *n == 0 { usize::MAX } else { *n }); + + // merge tabstops with the same index (we take advantage of the fact that we just sorted them + // above to simply look backwards) + let mut ntabstops = Vec::>::new(); + let mut prev = None; + for (tabstop, range) in tabstops { + if prev == Some(tabstop) { + let len_1 = ntabstops.len() - 1; + ntabstops[len_1].push(range); + } else { + prev = Some(tabstop); + ntabstops.push(smallvec![range]); + } + } + + if let Some(first) = ntabstops.first() { + transaction.with_selection(Selection::new(first.clone(), 0)) } else { transaction } From ba24cfe9125eda97346e3ceee42686fb9f46046f Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sat, 11 Feb 2023 14:20:49 +0100 Subject: [PATCH 0071/1169] Delete snippet placeholders when accepting completion When accepting a snippet completion we automatically delete the placeholders for now as doing so manual is quite cumbersome. In the future we should keep these as a mark + virtual text that is automatically removed once the cursor moves there. --- helix-lsp/src/snippet.rs | 13 +++++++++---- helix-term/src/ui/completion.rs | 4 ++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index 87c839f94..441c419f8 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -64,6 +64,7 @@ pub fn into_transaction<'a>( edit: &lsp_types::TextEdit, line_ending: &str, offset_encoding: OffsetEncoding, + include_placeholer: bool, ) -> helix_core::Transaction { use helix_core::{smallvec, Range, Selection, Transaction}; use SnippetElement::*; @@ -119,10 +120,14 @@ pub fn into_transaction<'a>( // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html // would make this a bit nicer Text(text) => { - let len_chars = text.chars().count(); - tabstops.push((tabstop, Range::new(offset, offset + len_chars + 1))); - offset += len_chars; - insert.push_str(text); + if include_placeholer { + let len_chars = text.chars().count(); + tabstops.push((tabstop, Range::new(offset, offset + len_chars + 1))); + offset += len_chars; + insert.push_str(text); + } else { + tabstops.push((tabstop, Range::point(offset))); + } } other => { log::error!( diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 6897305de..c7955a3dd 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -118,6 +118,7 @@ impl Completion { offset_encoding: helix_lsp::OffsetEncoding, start_offset: usize, trigger_offset: usize, + include_placeholder: bool, ) -> Transaction { use helix_lsp::snippet; @@ -144,6 +145,7 @@ impl Completion { &edit, doc.line_ending.as_str(), offset_encoding, + include_placeholder, ), Err(err) => { log::error!( @@ -216,6 +218,7 @@ impl Completion { offset_encoding, start_offset, trigger_offset, + true, ); // initialize a savepoint @@ -238,6 +241,7 @@ impl Completion { offset_encoding, start_offset, trigger_offset, + false, ); doc.apply(&transaction, view.id); From ec6e575a408372400b7789b90cdf6ac271f51182 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 11 Feb 2023 18:45:30 +0100 Subject: [PATCH 0072/1169] Correctly handle multiple cursors with LSP snippets --- helix-core/src/selection.rs | 10 ++++++++ helix-lsp/src/snippet.rs | 48 ++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 0ac2c6802..0db7634c9 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -578,6 +578,16 @@ impl Selection { self.normalize() } + /// Takes a closure and maps each `Range` over the closure to multiple `Range`s. + pub fn transform_iter(mut self, f: F) -> Self + where + F: FnMut(Range) -> I, + I: Iterator, + { + self.ranges = self.ranges.into_iter().flat_map(f).collect(); + self.normalize() + } + // Ensures the selection adheres to the following invariants: // 1. All ranges are grapheme aligned. // 2. All ranges are at least 1 character wide, unless at the diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index 441c419f8..ab0f406d0 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -66,7 +66,7 @@ pub fn into_transaction<'a>( offset_encoding: OffsetEncoding, include_placeholer: bool, ) -> helix_core::Transaction { - use helix_core::{smallvec, Range, Selection, Transaction}; + use helix_core::{smallvec, Range, Transaction}; use SnippetElement::*; let text = doc.slice(..); @@ -87,9 +87,9 @@ pub fn into_transaction<'a>( blank = "" ); + let mut offset = 0; let mut insert = String::new(); - let mut offset = (primary_cursor as i128 + start_offset) as usize; - let mut tabstops: Vec<(usize, Range)> = Vec::new(); + let mut tabstops: Vec<(usize, usize, usize)> = Vec::new(); for element in snippet.elements { match element { @@ -114,7 +114,7 @@ pub fn into_transaction<'a>( insert.push_str(text); } Tabstop { tabstop } => { - tabstops.push((tabstop, Range::point(offset))); + tabstops.push((tabstop, offset, offset)); } Placeholder { tabstop, value } => match value.as_ref() { // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html @@ -122,11 +122,11 @@ pub fn into_transaction<'a>( Text(text) => { if include_placeholer { let len_chars = text.chars().count(); - tabstops.push((tabstop, Range::new(offset, offset + len_chars + 1))); + tabstops.push((tabstop, offset, offset + len_chars + 1)); offset += len_chars; insert.push_str(text); } else { - tabstops.push((tabstop, Range::point(offset))); + tabstops.push((tabstop, offset, offset)); } } other => { @@ -157,24 +157,38 @@ pub fn into_transaction<'a>( }); // sort in ascending order (except for 0, which should always be the last one (per lsp doc)) - tabstops.sort_unstable_by_key(|(n, _range)| if *n == 0 { usize::MAX } else { *n }); + tabstops.sort_unstable_by_key(|(n, _o1, _o2)| if *n == 0 { usize::MAX } else { *n }); // merge tabstops with the same index (we take advantage of the fact that we just sorted them // above to simply look backwards) - let mut ntabstops = Vec::>::new(); - let mut prev = None; - for (tabstop, range) in tabstops { - if prev == Some(tabstop) { - let len_1 = ntabstops.len() - 1; - ntabstops[len_1].push(range); - } else { - prev = Some(tabstop); - ntabstops.push(smallvec![range]); + let mut ntabstops = Vec::>::new(); + { + let mut prev = None; + for (tabstop, o1, o2) in tabstops { + if prev == Some(tabstop) { + let len_1 = ntabstops.len() - 1; + ntabstops[len_1].push((o1, o2)); + } else { + prev = Some(tabstop); + ntabstops.push(smallvec![(o1, o2)]); + } } } if let Some(first) = ntabstops.first() { - transaction.with_selection(Selection::new(first.clone(), 0)) + let cursor_offset = insert.chars().count() as i128 - (end_offset - start_offset); + let mut extra_offset = start_offset; + transaction.with_selection(selection.clone().transform_iter(|range| { + let cursor = range.cursor(text); + let iter = first.iter().map(move |first| { + Range::new( + (cursor as i128 + first.0 as i128 + extra_offset) as usize, + (cursor as i128 + first.1 as i128 + extra_offset) as usize, + ) + }); + extra_offset += cursor_offset; + iter + })) } else { transaction } From 1866b43cd355ff6d41d579b4b710a0f602aa79d1 Mon Sep 17 00:00:00 2001 From: Andrii Grynenko Date: Fri, 17 Feb 2023 07:51:00 -0800 Subject: [PATCH 0073/1169] Render every LSP snippets for every cursor This refactors the snippet logic to be largely unaware of the rest of the document. The completion application logic is moved into generate_transaction_from_snippet which is extended to support dynamically computing replacement text. --- helix-lsp/src/lib.rs | 79 ++++++++++++++ helix-lsp/src/snippet.rs | 187 ++++++++++++++------------------ helix-term/src/ui/completion.rs | 8 +- 3 files changed, 166 insertions(+), 108 deletions(-) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index cce848ab1..5b4f7ee4e 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -60,6 +60,7 @@ pub mod util { use super::*; use helix_core::line_ending::{line_end_byte_index, line_end_char_index}; use helix_core::{diagnostic::NumberOrString, Range, Rope, Selection, Tendril, Transaction}; + use helix_core::{smallvec, SmallVec}; /// Converts a diagnostic in the document to [`lsp::Diagnostic`]. /// @@ -282,6 +283,84 @@ pub mod util { }) } + /// Creates a [Transaction] from the [snippet::Snippet] in a completion response. + /// The transaction applies the edit to all cursors. + pub fn generate_transaction_from_snippet( + doc: &Rope, + selection: &Selection, + edit_range: &lsp::Range, + snippet: snippet::Snippet, + line_ending: &str, + include_placeholder: bool, + offset_encoding: OffsetEncoding, + ) -> Transaction { + let text = doc.slice(..); + let primary_cursor = selection.primary().cursor(text); + + let start_offset = match lsp_pos_to_pos(doc, edit_range.start, offset_encoding) { + Some(start) => start as i128 - primary_cursor as i128, + None => return Transaction::new(doc), + }; + let end_offset = match lsp_pos_to_pos(doc, edit_range.end, offset_encoding) { + Some(end) => end as i128 - primary_cursor as i128, + None => return Transaction::new(doc), + }; + + // For each cursor store offsets for the first tabstop + let mut cursor_tabstop_offsets = Vec::>::new(); + let transaction = Transaction::change_by_selection(doc, selection, |range| { + let cursor = range.cursor(text); + let replacement_start = (cursor as i128 + start_offset) as usize; + let replacement_end = (cursor as i128 + end_offset) as usize; + let newline_with_offset = format!( + "{line_ending}{blank:width$}", + line_ending = line_ending, + width = replacement_start - doc.line_to_char(doc.char_to_line(replacement_start)), + blank = "" + ); + + let (replacement, tabstops) = + snippet::render(&snippet, newline_with_offset, include_placeholder); + + let replacement_len = replacement.chars().count(); + cursor_tabstop_offsets.push( + tabstops + .first() + .unwrap_or(&smallvec![(replacement_len, replacement_len)]) + .iter() + .map(|(from, to)| -> (i128, i128) { + ( + *from as i128 - replacement_len as i128, + *to as i128 - replacement_len as i128, + ) + }) + .collect(), + ); + + (replacement_start, replacement_end, Some(replacement.into())) + }); + + // Create new selection based on the cursor tabstop from above + let mut cursor_tabstop_offsets_iter = cursor_tabstop_offsets.iter(); + let selection = selection + .clone() + .map(transaction.changes()) + .transform_iter(|range| { + cursor_tabstop_offsets_iter + .next() + .unwrap() + .iter() + .map(move |(from, to)| { + Range::new( + (range.anchor as i128 + *from) as usize, + (range.anchor as i128 + *to) as usize, + ) + }) + }); + + transaction.with_selection(selection) + } + pub fn generate_transaction_from_edits( doc: &Rope, mut edits: Vec, diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index ab0f406d0..63054cdbb 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -1,9 +1,7 @@ use std::borrow::Cow; use anyhow::{anyhow, Result}; -use helix_core::SmallVec; - -use crate::{util::lsp_pos_to_pos, OffsetEncoding}; +use helix_core::{SmallVec, smallvec}; #[derive(Debug, PartialEq, Eq)] pub enum CaseChange { @@ -34,7 +32,7 @@ pub enum SnippetElement<'a> { }, Placeholder { tabstop: usize, - value: Box>, + value: Vec>, }, Choice { tabstop: usize, @@ -57,141 +55,108 @@ pub fn parse(s: &str) -> Result> { parser::parse(s).map_err(|rest| anyhow!("Failed to parse snippet. Remaining input: {}", rest)) } -pub fn into_transaction<'a>( - snippet: Snippet<'a>, - doc: &helix_core::Rope, - selection: &helix_core::Selection, - edit: &lsp_types::TextEdit, - line_ending: &str, - offset_encoding: OffsetEncoding, +fn render_elements( + snippet_elements: &[SnippetElement<'_>], + insert: &mut String, + offset: &mut usize, + tabstops: &mut Vec<(usize, (usize, usize))>, + newline_with_offset: &String, include_placeholer: bool, -) -> helix_core::Transaction { - use helix_core::{smallvec, Range, Transaction}; +) { use SnippetElement::*; - let text = doc.slice(..); - let primary_cursor = selection.primary().cursor(text); - - let start_offset = match lsp_pos_to_pos(doc, edit.range.start, offset_encoding) { - Some(start) => start as i128 - primary_cursor as i128, - None => return Transaction::new(doc), - }; - let end_offset = match lsp_pos_to_pos(doc, edit.range.end, offset_encoding) { - Some(end) => end as i128 - primary_cursor as i128, - None => return Transaction::new(doc), - }; - - let newline_with_offset = format!( - "{line_ending}{blank:width$}", - width = edit.range.start.character as usize, - blank = "" - ); - - let mut offset = 0; - let mut insert = String::new(); - let mut tabstops: Vec<(usize, usize, usize)> = Vec::new(); - - for element in snippet.elements { + for element in snippet_elements { match element { - Text(text) => { + &Text(text) => { // small optimization to avoid calling replace when it's unnecessary let text = if text.contains('\n') { - Cow::Owned(text.replace('\n', &newline_with_offset)) + Cow::Owned(text.replace('\n', newline_with_offset)) } else { Cow::Borrowed(text) }; - offset += text.chars().count(); + *offset += text.chars().count(); insert.push_str(&text); } - Variable { - name: _name, - regex: None, + &Variable { + name: _, + regex: _, r#default, } => { // TODO: variables. For now, fall back to the default, which defaults to "". let text = r#default.unwrap_or_default(); - offset += text.chars().count(); + *offset += text.chars().count(); insert.push_str(text); } - Tabstop { tabstop } => { - tabstops.push((tabstop, offset, offset)); + &Tabstop { tabstop } => { + tabstops.push((tabstop, (*offset, *offset))); } - Placeholder { tabstop, value } => match value.as_ref() { - // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html - // would make this a bit nicer - Text(text) => { - if include_placeholer { - let len_chars = text.chars().count(); - tabstops.push((tabstop, offset, offset + len_chars + 1)); - offset += len_chars; - insert.push_str(text); - } else { - tabstops.push((tabstop, offset, offset)); - } - } - other => { - log::error!( - "Discarding snippet: generating a transaction for placeholder contents {:?} is unimplemented.", - other + Placeholder { + tabstop, + value: inner_snippet_elements, + } => { + let start_offset = *offset; + if include_placeholer { + render_elements( + inner_snippet_elements, + insert, + offset, + tabstops, + newline_with_offset, + include_placeholer, ); - return Transaction::new(doc); } - }, - other => { - log::error!( - "Discarding snippet: generating a transaction for {:?} is unimplemented.", - other - ); - return Transaction::new(doc); + tabstops.push((*tabstop, (start_offset, *offset))); + } + &Choice { + tabstop, + choices: _, + } => { + // TODO: choices + tabstops.push((tabstop, (*offset, *offset))); } } } +} - let transaction = Transaction::change_by_selection(doc, selection, |range| { - let cursor = range.cursor(text); - ( - (cursor as i128 + start_offset) as usize, - (cursor as i128 + end_offset) as usize, - Some(insert.clone().into()), - ) - }); +#[allow(clippy::type_complexity)] // only used one time +pub fn render( + snippet: &Snippet<'_>, + newline_with_offset: String, + include_placeholer: bool, +) -> (String, Vec>) { + let mut insert = String::new(); + let mut tabstops = Vec::new(); + let mut offset = 0; + + render_elements( + &snippet.elements, + &mut insert, + &mut offset, + &mut tabstops, + &newline_with_offset, + include_placeholer, + ); // sort in ascending order (except for 0, which should always be the last one (per lsp doc)) - tabstops.sort_unstable_by_key(|(n, _o1, _o2)| if *n == 0 { usize::MAX } else { *n }); + tabstops.sort_unstable_by_key(|(n, _)| if *n == 0 { usize::MAX } else { *n }); // merge tabstops with the same index (we take advantage of the fact that we just sorted them // above to simply look backwards) let mut ntabstops = Vec::>::new(); { let mut prev = None; - for (tabstop, o1, o2) in tabstops { + for (tabstop, r) in tabstops { if prev == Some(tabstop) { let len_1 = ntabstops.len() - 1; - ntabstops[len_1].push((o1, o2)); + ntabstops[len_1].push(r); } else { prev = Some(tabstop); - ntabstops.push(smallvec![(o1, o2)]); + ntabstops.push(smallvec![r]); } } } - if let Some(first) = ntabstops.first() { - let cursor_offset = insert.chars().count() as i128 - (end_offset - start_offset); - let mut extra_offset = start_offset; - transaction.with_selection(selection.clone().transform_iter(|range| { - let cursor = range.cursor(text); - let iter = first.iter().map(move |first| { - Range::new( - (cursor as i128 + first.0 as i128 + extra_offset) as usize, - (cursor as i128 + first.1 as i128 + extra_offset) as usize, - ) - }); - extra_offset += cursor_offset; - iter - })) - } else { - transaction - } + (insert, ntabstops) } mod parser { @@ -343,14 +308,15 @@ mod parser { fn placeholder<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { // TODO: why doesn't parse_as work? // let value = reparse_as(take_until(|c| c == '}'), anything()); + // TODO: fix this to parse nested placeholders (take until terminates too early) let value = filter_map(take_until(|c| c == '}'), |s| { - anything().parse(s).map(|parse_result| parse_result.1).ok() + snippet().parse(s).map(|parse_result| parse_result.1).ok() }); map(seq!("${", digit(), ":", value, "}"), |seq| { SnippetElement::Placeholder { tabstop: seq.1, - value: Box::new(seq.3), + value: seq.3.elements, } }) } @@ -430,7 +396,7 @@ mod parser { Text("match("), Placeholder { tabstop: 1, - value: Box::new(Text("Arg1")), + value: vec!(Text("Arg1")), }, Text(")") ] @@ -447,12 +413,12 @@ mod parser { Text("local "), Placeholder { tabstop: 1, - value: Box::new(Text("var")), + value: vec!(Text("var")), }, Text(" = "), Placeholder { tabstop: 1, - value: Box::new(Text("value")), + value: vec!(Text("value")), }, ] }), @@ -460,6 +426,19 @@ mod parser { ) } + #[test] + fn parse_tabstop_nested_in_placeholder() { + assert_eq!( + Ok(Snippet { + elements: vec![Placeholder { + tabstop: 1, + value: vec!(Text("var, "), Tabstop { tabstop: 2 },), + },] + }), + parse("${1:var, $2}") + ) + } + #[test] fn parse_all() { assert_eq!( diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index c7955a3dd..e7815e12d 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -138,14 +138,14 @@ impl Completion { ) { match snippet::parse(&edit.new_text) { - Ok(snippet) => snippet::into_transaction( - snippet, + Ok(snippet) => util::generate_transaction_from_snippet( doc.text(), doc.selection(view_id), - &edit, + &edit.range, + snippet, doc.line_ending.as_str(), - offset_encoding, include_placeholder, + offset_encoding, ), Err(err) => { log::error!( From 0d924255e4ea3d5d5c4be9b11a337f4316550e32 Mon Sep 17 00:00:00 2001 From: Andrii Grynenko Date: Mon, 20 Feb 2023 22:04:24 -0800 Subject: [PATCH 0074/1169] Add nested placeholder parsing for LSP snippets And fix `text` over-parsing, inspired by https://github.com/neovim/neovim/blob/d18f8d5c2d82b209093b2feaa8921a4792b71d59/runtime/lua/vim/lsp/_snippet.lua --- helix-lsp/src/snippet.rs | 70 ++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index 63054cdbb..b27077e70 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use anyhow::{anyhow, Result}; -use helix_core::{SmallVec, smallvec}; +use helix_core::{smallvec, SmallVec}; #[derive(Debug, PartialEq, Eq)] pub enum CaseChange { @@ -210,8 +210,8 @@ mod parser { } } - fn text<'a>() -> impl Parser<'a, Output = &'a str> { - take_while(|c| c != '$') + fn text<'a, const SIZE: usize>(cs: [char; SIZE]) -> impl Parser<'a, Output = &'a str> { + take_while(move |c| cs.into_iter().all(|c1| c != c1)) } fn digit<'a>() -> impl Parser<'a, Output = usize> { @@ -270,13 +270,15 @@ mod parser { ), |seq| { Conditional(seq.1, None, Some(seq.4)) } ), - // Any text - map(text(), Text), ) } fn regex<'a>() -> impl Parser<'a, Output = Regex<'a>> { - let replacement = reparse_as(take_until(|c| c == '/'), one_or_more(format())); + let text = map(text(['$', '/']), FormatItem::Text); + let replacement = reparse_as( + take_until(|c| c == '/'), + one_or_more(choice!(format(), text)), + ); map( seq!( @@ -306,19 +308,20 @@ mod parser { } fn placeholder<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { - // TODO: why doesn't parse_as work? - // let value = reparse_as(take_until(|c| c == '}'), anything()); - // TODO: fix this to parse nested placeholders (take until terminates too early) - let value = filter_map(take_until(|c| c == '}'), |s| { - snippet().parse(s).map(|parse_result| parse_result.1).ok() - }); - - map(seq!("${", digit(), ":", value, "}"), |seq| { - SnippetElement::Placeholder { + let text = map(text(['$', '}']), SnippetElement::Text); + map( + seq!( + "${", + digit(), + ":", + one_or_more(choice!(anything(), text)), + "}" + ), + |seq| SnippetElement::Placeholder { tabstop: seq.1, - value: seq.3.elements, - } - }) + value: seq.3, + }, + ) } fn choice<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { @@ -366,12 +369,18 @@ mod parser { } fn anything<'a>() -> impl Parser<'a, Output = SnippetElement<'a>> { - let text = map(text(), SnippetElement::Text); - choice!(tabstop(), placeholder(), choice(), variable(), text) + // The parser has to be constructed lazily to avoid infinite opaque type recursion + |input: &'a str| { + let parser = choice!(tabstop(), placeholder(), choice(), variable()); + parser.parse(input) + } } fn snippet<'a>() -> impl Parser<'a, Output = Snippet<'a>> { - map(one_or_more(anything()), |parts| Snippet { elements: parts }) + let text = map(text(['$']), SnippetElement::Text); + map(one_or_more(choice!(anything(), text)), |parts| Snippet { + elements: parts, + }) } pub fn parse(s: &str) -> Result { @@ -439,6 +448,25 @@ mod parser { ) } + #[test] + fn parse_placeholder_nested_in_placeholder() { + assert_eq!( + Ok(Snippet { + elements: vec![Placeholder { + tabstop: 1, + value: vec!( + Text("foo "), + Placeholder { + tabstop: 2, + value: vec!(Text("bar")), + }, + ), + },] + }), + parse("${1:foo ${2:bar}}") + ) + } + #[test] fn parse_all() { assert_eq!( From 8c2e447b16e4d11db411b18f2fbe3ac2bc031d89 Mon Sep 17 00:00:00 2001 From: Andrii Grynenko Date: Thu, 2 Mar 2023 21:41:06 -0800 Subject: [PATCH 0075/1169] Handle snippets for LSPs not providing offsets for completion --- helix-lsp/src/lib.rs | 33 +++--------- helix-term/src/ui/completion.rs | 95 +++++++++++++++++++-------------- 2 files changed, 61 insertions(+), 67 deletions(-) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 5b4f7ee4e..147b381c2 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -252,26 +252,17 @@ pub mod util { pub fn generate_transaction_from_completion_edit( doc: &Rope, selection: &Selection, - edit: lsp::TextEdit, - offset_encoding: OffsetEncoding, + start_offset: i128, + end_offset: i128, + new_text: String, ) -> Transaction { - let replacement: Option = if edit.new_text.is_empty() { + let replacement: Option = if new_text.is_empty() { None } else { - Some(edit.new_text.into()) + Some(new_text.into()) }; let text = doc.slice(..); - let primary_cursor = selection.primary().cursor(text); - - let start_offset = match lsp_pos_to_pos(doc, edit.range.start, offset_encoding) { - Some(start) => start as i128 - primary_cursor as i128, - None => return Transaction::new(doc), - }; - let end_offset = match lsp_pos_to_pos(doc, edit.range.end, offset_encoding) { - Some(end) => end as i128 - primary_cursor as i128, - None => return Transaction::new(doc), - }; Transaction::change_by_selection(doc, selection, |range| { let cursor = range.cursor(text); @@ -288,23 +279,13 @@ pub mod util { pub fn generate_transaction_from_snippet( doc: &Rope, selection: &Selection, - edit_range: &lsp::Range, + start_offset: i128, + end_offset: i128, snippet: snippet::Snippet, line_ending: &str, include_placeholder: bool, - offset_encoding: OffsetEncoding, ) -> Transaction { let text = doc.slice(..); - let primary_cursor = selection.primary().cursor(text); - - let start_offset = match lsp_pos_to_pos(doc, edit_range.start, offset_encoding) { - Some(start) => start as i128 - primary_cursor as i128, - None => return Transaction::new(doc), - }; - let end_offset = match lsp_pos_to_pos(doc, edit_range.end, offset_encoding) { - Some(end) => end as i128 - primary_cursor as i128, - None => return Transaction::new(doc), - }; // For each cursor store offsets for the first tabstop let mut cursor_tabstop_offsets = Vec::>::new(); diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index e7815e12d..85931fe32 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -121,8 +121,9 @@ impl Completion { include_placeholder: bool, ) -> Transaction { use helix_lsp::snippet; + let selection = doc.selection(view_id); - if let Some(edit) = &item.text_edit { + let (start_offset, end_offset, new_text) = if let Some(edit) = &item.text_edit { let edit = match edit { lsp::CompletionTextEdit::Edit(edit) => edit.clone(), lsp::CompletionTextEdit::InsertAndReplace(item) => { @@ -130,46 +131,27 @@ impl Completion { lsp::TextEdit::new(item.replace, item.new_text.clone()) } }; - - if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET)) - || matches!( - item.insert_text_format, - Some(lsp::InsertTextFormat::SNIPPET) - ) - { - match snippet::parse(&edit.new_text) { - Ok(snippet) => util::generate_transaction_from_snippet( - doc.text(), - doc.selection(view_id), - &edit.range, - snippet, - doc.line_ending.as_str(), - include_placeholder, - offset_encoding, - ), - Err(err) => { - log::error!( - "Failed to parse snippet: {:?}, remaining output: {}", - &edit.new_text, - err - ); - Transaction::new(doc.text()) - } - } - } else { - util::generate_transaction_from_completion_edit( - doc.text(), - doc.selection(view_id), - edit, - offset_encoding, // TODO: should probably transcode in Client - ) - } + let text = doc.text().slice(..); + let primary_cursor = selection.primary().cursor(text); + + let start_offset = + match util::lsp_pos_to_pos(doc.text(), edit.range.start, offset_encoding) { + Some(start) => start as i128 - primary_cursor as i128, + None => return Transaction::new(doc.text()), + }; + let end_offset = + match util::lsp_pos_to_pos(doc.text(), edit.range.end, offset_encoding) { + Some(end) => end as i128 - primary_cursor as i128, + None => return Transaction::new(doc.text()), + }; + + (start_offset, end_offset, edit.new_text) } else { - let text = item.insert_text.as_ref().unwrap_or(&item.label); + let new_text = item.insert_text.as_ref().unwrap_or(&item.label); // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯ // in these cases we need to check for a common prefix and remove it let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset)); - let text = text.trim_start_matches::<&str>(&prefix); + let new_text = new_text.trim_start_matches::<&str>(&prefix); // TODO: this needs to be true for the numbers to work out correctly // in the closure below. It's passed in to a callback as this same @@ -182,11 +164,42 @@ impl Completion { == trigger_offset ); - Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| { - let cursor = range.cursor(doc.text().slice(..)); + (0, 0, new_text.into()) + }; - (cursor, cursor, Some(text.into())) - }) + if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET)) + || matches!( + item.insert_text_format, + Some(lsp::InsertTextFormat::SNIPPET) + ) + { + match snippet::parse(&new_text) { + Ok(snippet) => util::generate_transaction_from_snippet( + doc.text(), + selection, + start_offset, + end_offset, + snippet, + doc.line_ending.as_str(), + include_placeholder, + ), + Err(err) => { + log::error!( + "Failed to parse snippet: {:?}, remaining output: {}", + &new_text, + err + ); + Transaction::new(doc.text()) + } + } + } else { + util::generate_transaction_from_completion_edit( + doc.text(), + selection, + start_offset, + end_offset, + new_text, + ) } } From 48b6aa9a699df0680a6d31e9611ebd1ca9909de4 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Wed, 8 Mar 2023 02:49:14 +0100 Subject: [PATCH 0076/1169] Add command for resetting diff hunks (#5736) --- book/src/generated/typable-cmd.md | 1 + helix-term/src/commands.rs | 22 +++++------ helix-term/src/commands/typed.rs | 65 +++++++++++++++++++++++++++++++ helix-vcs/src/diff.rs | 58 +++++++++++++++++++-------- helix-vcs/src/diff/line_cache.rs | 8 ++++ helix-vcs/src/diff/worker.rs | 15 ++++--- helix-vcs/src/diff/worker/test.rs | 6 +-- helix-view/src/gutter.rs | 2 +- 8 files changed, 140 insertions(+), 37 deletions(-) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index badadc43d..8b367aad8 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -76,3 +76,4 @@ | `:pipe` | Pipe each selection to the shell command. | | `:pipe-to` | Pipe each selection to the shell command, ignoring output. | | `:run-shell-command`, `:sh` | Run a shell command | +| `:reset-diff-change`, `:diffget`, `:diffg` | Reset the diff change at the cursor position. | diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c76e9f2bd..ebdfdfde2 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3011,13 +3011,13 @@ fn goto_first_change_impl(cx: &mut Context, reverse: bool) { let (view, doc) = current!(editor); if let Some(handle) = doc.diff_handle() { let hunk = { - let hunks = handle.hunks(); + let diff = handle.load(); let idx = if reverse { - hunks.len().saturating_sub(1) + diff.len().saturating_sub(1) } else { 0 }; - hunks.nth_hunk(idx) + diff.nth_hunk(idx) }; if hunk != Hunk::NONE { let range = hunk_range(hunk, doc.text().slice(..)); @@ -3049,19 +3049,19 @@ fn goto_next_change_impl(cx: &mut Context, direction: Direction) { let selection = doc.selection(view.id).clone().transform(|range| { let cursor_line = range.cursor_line(doc_text) as u32; - let hunks = diff_handle.hunks(); + let diff = diff_handle.load(); let hunk_idx = match direction { - Direction::Forward => hunks + Direction::Forward => diff .next_hunk(cursor_line) - .map(|idx| (idx + count).min(hunks.len() - 1)), - Direction::Backward => hunks + .map(|idx| (idx + count).min(diff.len() - 1)), + Direction::Backward => diff .prev_hunk(cursor_line) .map(|idx| idx.saturating_sub(count)), }; let Some(hunk_idx) = hunk_idx else { return range; }; - let hunk = hunks.nth_hunk(hunk_idx); + let hunk = diff.nth_hunk(hunk_idx); let new_range = hunk_range(hunk, doc_text); if editor.mode == Mode::Select { let head = if new_range.head < range.anchor { @@ -4721,14 +4721,14 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) { let textobject_change = |range: Range| -> Range { let diff_handle = doc.diff_handle().unwrap(); - let hunks = diff_handle.hunks(); + let diff = diff_handle.load(); let line = range.cursor_line(text); - let hunk_idx = if let Some(hunk_idx) = hunks.hunk_at(line as u32, false) { + let hunk_idx = if let Some(hunk_idx) = diff.hunk_at(line as u32, false) { hunk_idx } else { return range; }; - let hunk = hunks.nth_hunk(hunk_idx).after; + let hunk = diff.nth_hunk(hunk_idx).after; let start = text.line_to_char(hunk.start as usize); let end = text.line_to_char(hunk.end as usize); diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 0ddca6df7..77c143212 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2033,6 +2033,64 @@ fn run_shell_command( Ok(()) } +fn reset_diff_change( + cx: &mut compositor::Context, + args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + ensure!(args.is_empty(), ":reset-diff-change takes no arguments"); + + let editor = &mut cx.editor; + let scrolloff = editor.config().scrolloff; + + let (view, doc) = current!(editor); + // TODO refactor to use let..else once MSRV is raised to 1.65 + let handle = match doc.diff_handle() { + Some(handle) => handle, + None => bail!("Diff is not available in the current buffer"), + }; + + let diff = handle.load(); + let doc_text = doc.text().slice(..); + let line = doc.selection(view.id).primary().cursor_line(doc_text); + + // TODO refactor to use let..else once MSRV is raised to 1.65 + let hunk_idx = match diff.hunk_at(line as u32, true) { + Some(hunk_idx) => hunk_idx, + None => bail!("There is no change at the cursor"), + }; + let hunk = diff.nth_hunk(hunk_idx); + let diff_base = diff.diff_base(); + let before_start = diff_base.line_to_char(hunk.before.start as usize); + let before_end = diff_base.line_to_char(hunk.before.end as usize); + let text: Tendril = diff + .diff_base() + .slice(before_start..before_end) + .chunks() + .collect(); + let anchor = doc_text.line_to_char(hunk.after.start as usize); + let transaction = Transaction::change( + doc.text(), + [( + anchor, + doc_text.line_to_char(hunk.after.end as usize), + (!text.is_empty()).then_some(text), + )] + .into_iter(), + ); + drop(diff); // make borrow check happy + doc.apply(&transaction, view.id); + // select inserted text + let text_len = before_end - before_start; + doc.set_selection(view.id, Selection::single(anchor, anchor + text_len)); + doc.append_changes_to_history(view); + view.ensure_cursor_in_view(doc, scrolloff); + Ok(()) +} + pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit", @@ -2569,6 +2627,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: run_shell_command, completer: Some(completers::filename), }, + TypableCommand { + name: "reset-diff-change", + aliases: &["diffget", "diffg"], + doc: "Reset the diff change at the cursor position.", + fun: reset_diff_change, + completer: None, + }, ]; pub static TYPABLE_COMMAND_MAP: Lazy> = diff --git a/helix-vcs/src/diff.rs b/helix-vcs/src/diff.rs index 9c6a362f7..ca33dda44 100644 --- a/helix-vcs/src/diff.rs +++ b/helix-vcs/src/diff.rs @@ -28,11 +28,18 @@ struct Event { render_lock: Option, } +#[derive(Clone, Debug, Default)] +struct DiffInner { + diff_base: Rope, + doc: Rope, + hunks: Vec, +} + #[derive(Clone, Debug)] pub struct DiffHandle { channel: UnboundedSender, render_lock: Arc>, - hunks: Arc>>, + diff: Arc>, inverted: bool, } @@ -47,10 +54,10 @@ impl DiffHandle { redraw_handle: RedrawHandle, ) -> (DiffHandle, JoinHandle<()>) { let (sender, receiver) = unbounded_channel(); - let hunks: Arc>> = Arc::default(); + let diff: Arc> = Arc::default(); let worker = DiffWorker { channel: receiver, - hunks: hunks.clone(), + diff: diff.clone(), new_hunks: Vec::default(), redraw_notify: redraw_handle.0, diff_finished_notify: Arc::default(), @@ -58,7 +65,7 @@ impl DiffHandle { let handle = tokio::spawn(worker.run(diff_base, doc)); let differ = DiffHandle { channel: sender, - hunks, + diff, inverted: false, render_lock: redraw_handle.1, }; @@ -69,9 +76,9 @@ impl DiffHandle { self.inverted = !self.inverted; } - pub fn hunks(&self) -> FileHunks { - FileHunks { - hunks: self.hunks.lock(), + pub fn load(&self) -> Diff { + Diff { + diff: self.diff.lock(), inverted: self.inverted, } } @@ -168,12 +175,28 @@ impl Hunk { /// A list of changes in a file sorted in ascending /// non-overlapping order #[derive(Debug)] -pub struct FileHunks<'a> { - hunks: MutexGuard<'a, Vec>, +pub struct Diff<'a> { + diff: MutexGuard<'a, DiffInner>, inverted: bool, } -impl FileHunks<'_> { +impl Diff<'_> { + pub fn diff_base(&self) -> &Rope { + if self.inverted { + &self.diff.doc + } else { + &self.diff.diff_base + } + } + + pub fn doc(&self) -> &Rope { + if self.inverted { + &self.diff.diff_base + } else { + &self.diff.doc + } + } + pub fn is_inverted(&self) -> bool { self.inverted } @@ -181,7 +204,7 @@ impl FileHunks<'_> { /// Returns the `Hunk` for the `n`th change in this file. /// if there is no `n`th change `Hunk::NONE` is returned instead. pub fn nth_hunk(&self, n: u32) -> Hunk { - match self.hunks.get(n as usize) { + match self.diff.hunks.get(n as usize) { Some(hunk) if self.inverted => hunk.invert(), Some(hunk) => hunk.clone(), None => Hunk::NONE, @@ -189,7 +212,7 @@ impl FileHunks<'_> { } pub fn len(&self) -> u32 { - self.hunks.len() as u32 + self.diff.hunks.len() as u32 } pub fn is_empty(&self) -> bool { @@ -204,19 +227,20 @@ impl FileHunks<'_> { }; let res = self + .diff .hunks .binary_search_by_key(&line, |hunk| hunk_range(hunk).start); match res { // Search found a hunk that starts exactly at this line, return the next hunk if it exists. - Ok(pos) if pos + 1 == self.hunks.len() => None, + Ok(pos) if pos + 1 == self.diff.hunks.len() => None, Ok(pos) => Some(pos as u32 + 1), // No hunk starts exactly at this line, so the search returns // the position where a hunk starting at this line should be inserted. // That position is exactly the position of the next hunk or the end // of the list if no such hunk exists - Err(pos) if pos == self.hunks.len() => None, + Err(pos) if pos == self.diff.hunks.len() => None, Err(pos) => Some(pos as u32), } } @@ -228,6 +252,7 @@ impl FileHunks<'_> { |hunk: &Hunk| hunk.after.clone() }; let res = self + .diff .hunks .binary_search_by_key(&line, |hunk| hunk_range(hunk).end); @@ -237,7 +262,7 @@ impl FileHunks<'_> { // which represents a pure removal. // Removals are technically empty but are still shown as single line hunks // and as such we must jump to the previous hunk (if it exists) if we are already inside the removal - Ok(pos) if !hunk_range(&self.hunks[pos]).is_empty() => Some(pos as u32), + Ok(pos) if !hunk_range(&self.diff.hunks[pos]).is_empty() => Some(pos as u32), // No hunk ends exactly at this line, so the search returns // the position where a hunk ending at this line should be inserted. @@ -255,6 +280,7 @@ impl FileHunks<'_> { }; let res = self + .diff .hunks .binary_search_by_key(&line, |hunk| hunk_range(hunk).start); @@ -267,7 +293,7 @@ impl FileHunks<'_> { // The previous hunk contains this hunk if it exists and doesn't end before this line Err(0) => None, Err(pos) => { - let hunk = hunk_range(&self.hunks[pos - 1]); + let hunk = hunk_range(&self.diff.hunks[pos - 1]); if hunk.end > line || include_removal && hunk.start == line && hunk.is_empty() { Some(pos as u32 - 1) } else { diff --git a/helix-vcs/src/diff/line_cache.rs b/helix-vcs/src/diff/line_cache.rs index c3ee5daa3..8e48250f1 100644 --- a/helix-vcs/src/diff/line_cache.rs +++ b/helix-vcs/src/diff/line_cache.rs @@ -43,6 +43,14 @@ impl InternedRopeLines { res } + pub fn doc(&self) -> Rope { + self.doc.clone() + } + + pub fn diff_base(&self) -> Rope { + self.diff_base.clone() + } + /// Updates the `diff_base` and optionally the document if `doc` is not None pub fn update_diff_base(&mut self, diff_base: Rope, doc: Option) { self.interned.clear(); diff --git a/helix-vcs/src/diff/worker.rs b/helix-vcs/src/diff/worker.rs index f4bb4dbfb..5406446fd 100644 --- a/helix-vcs/src/diff/worker.rs +++ b/helix-vcs/src/diff/worker.rs @@ -10,7 +10,7 @@ use tokio::sync::Notify; use tokio::time::{timeout, timeout_at, Duration}; use crate::diff::{ - Event, RenderLock, ALGORITHM, DIFF_DEBOUNCE_TIME_ASYNC, DIFF_DEBOUNCE_TIME_SYNC, + DiffInner, Event, RenderLock, ALGORITHM, DIFF_DEBOUNCE_TIME_ASYNC, DIFF_DEBOUNCE_TIME_SYNC, }; use super::line_cache::InternedRopeLines; @@ -21,7 +21,7 @@ mod test; pub(super) struct DiffWorker { pub channel: UnboundedReceiver, - pub hunks: Arc>>, + pub diff: Arc>, pub new_hunks: Vec, pub redraw_notify: Arc, pub diff_finished_notify: Arc, @@ -46,7 +46,7 @@ impl DiffWorker { if let Some(lines) = interner.interned_lines() { self.perform_diff(lines); } - self.apply_hunks(); + self.apply_hunks(interner.diff_base(), interner.doc()); while let Some(event) = self.channel.recv().await { let (doc, diff_base) = self.accumulate_events(event).await; @@ -70,15 +70,18 @@ impl DiffWorker { #[cfg(not(test))] tokio::task::block_in_place(process_accumulated_events); - self.apply_hunks(); + self.apply_hunks(interner.diff_base(), interner.doc()); } } /// update the hunks (used by the gutter) by replacing it with `self.new_hunks`. /// `self.new_hunks` is always empty after this function runs. /// To improve performance this function tries to reuse the allocation of the old diff previously stored in `self.line_diffs` - fn apply_hunks(&mut self) { - swap(&mut *self.hunks.lock(), &mut self.new_hunks); + fn apply_hunks(&mut self, diff_base: Rope, doc: Rope) { + let mut diff = self.diff.lock(); + diff.diff_base = diff_base; + diff.doc = doc; + swap(&mut diff.hunks, &mut self.new_hunks); self.diff_finished_notify.notify_waiters(); self.new_hunks.clear(); } diff --git a/helix-vcs/src/diff/worker/test.rs b/helix-vcs/src/diff/worker/test.rs index 144424265..6a68d987c 100644 --- a/helix-vcs/src/diff/worker/test.rs +++ b/helix-vcs/src/diff/worker/test.rs @@ -12,12 +12,12 @@ impl DiffHandle { ) } async fn into_diff(self, handle: JoinHandle<()>) -> Vec { - let hunks = self.hunks; + let diff = self.diff; // dropping the channel terminates the task drop(self.channel); handle.await.unwrap(); - let hunks = hunks.lock(); - Vec::clone(&*hunks) + let diff = diff.lock(); + Vec::clone(&diff.hunks) } } diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs index cb9e43336..36e8e16a4 100644 --- a/helix-view/src/gutter.rs +++ b/helix-view/src/gutter.rs @@ -100,7 +100,7 @@ pub fn diff<'doc>( let deleted = theme.get("diff.minus"); let modified = theme.get("diff.delta"); if let Some(diff_handle) = doc.diff_handle() { - let hunks = diff_handle.hunks(); + let hunks = diff_handle.load(); let mut hunk_i = 0; let mut hunk = hunks.nth_hunk(hunk_i); Box::new( From 3d850247177f61601296fe5b1cdada8819137783 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sun, 5 Mar 2023 12:06:12 -0600 Subject: [PATCH 0077/1169] Move terminal claim/restore code to helix-tui This moves the `Application::claim_term` and `helix-term::application::restore_term` functions into the helix-tui crate. How the terminal should be claimed and restored is a TUI concern and is implemented differently through different TUI backends. This cleans out a lot of crossterm and TUI code in Application and makes it easier to modify claim/restore based on information we query from the terminal host. The child commit will take advantage of this to cache the check for whether the host terminal supports the keyboard enhancement protocol. Without this change, caching that information takes much more code which is not easily reusable for anything else. The code to restore the terminal is somewhat duplicated by this patch: we want to restore the terminal in cases of panics. Panic handler hooks must live for `'static` and the Application's terminal does not. --- Cargo.lock | 1 + helix-term/src/application.rs | 98 +++++++----------------------- helix-tui/Cargo.toml | 1 + helix-tui/src/backend/crossterm.rs | 70 ++++++++++++++++++++- helix-tui/src/backend/mod.rs | 5 +- helix-tui/src/backend/test.rs | 13 ++++ helix-tui/src/terminal.rs | 22 +++++++ 7 files changed, 131 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc7265f33..231273956 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1201,6 +1201,7 @@ dependencies = [ "crossterm", "helix-core", "helix-view", + "log", "serde", "termini", "unicode-segmentation", diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index df6d9da6c..2487a02f8 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -30,22 +30,14 @@ use crate::{ use log::{debug, error, warn}; use std::{ - io::{stdin, stdout, Write}, + io::{stdin, stdout}, sync::Arc, time::{Duration, Instant}, }; use anyhow::{Context, Error}; -use crossterm::{ - event::{ - DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, - EnableFocusChange, EnableMouseCapture, Event as CrosstermEvent, KeyboardEnhancementFlags, - PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, - }, - execute, terminal, - tty::IsTty, -}; +use crossterm::{event::Event as CrosstermEvent, tty::IsTty}; #[cfg(not(windows))] use { signal_hook::{consts::signal, low_level}, @@ -63,10 +55,12 @@ use tui::backend::CrosstermBackend; use tui::backend::TestBackend; #[cfg(not(feature = "integration"))] -type Terminal = tui::terminal::Terminal>; +type TerminalBackend = CrosstermBackend; #[cfg(feature = "integration")] -type Terminal = tui::terminal::Terminal; +type TerminalBackend = TestBackend; + +type Terminal = tui::terminal::Terminal; pub struct Application { compositor: Compositor, @@ -108,26 +102,6 @@ fn setup_integration_logging() { .apply(); } -fn restore_term() -> Result<(), Error> { - let mut stdout = stdout(); - // reset cursor shape - write!(stdout, "\x1B[0 q")?; - if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { - execute!(stdout, PopKeyboardEnhancementFlags)?; - } - // Ignore errors on disabling, this might trigger on windows if we call - // disable without calling enable previously - let _ = execute!(stdout, DisableMouseCapture); - execute!( - stdout, - DisableBracketedPaste, - DisableFocusChange, - terminal::LeaveAlternateScreen - )?; - terminal::disable_raw_mode()?; - Ok(()) -} - impl Application { pub fn new( args: Args, @@ -472,13 +446,7 @@ impl Application { pub async fn handle_signals(&mut self, signal: i32) { match signal { signal::SIGTSTP => { - // restore cursor - use helix_view::graphics::CursorKind; - self.terminal - .backend_mut() - .show_cursor(CursorKind::Block) - .ok(); - restore_term().unwrap(); + self.restore_term().unwrap(); low_level::emulate_default_handler(signal::SIGTSTP).unwrap(); } signal::SIGCONT => { @@ -1054,37 +1022,19 @@ impl Application { } } - async fn claim_term(&mut self) -> Result<(), Error> { - use helix_view::graphics::CursorKind; - terminal::enable_raw_mode()?; - if self.terminal.cursor_kind() == CursorKind::Hidden { - self.terminal.backend_mut().hide_cursor().ok(); - } - let mut stdout = stdout(); - execute!( - stdout, - terminal::EnterAlternateScreen, - EnableBracketedPaste, - EnableFocusChange - )?; - execute!(stdout, terminal::Clear(terminal::ClearType::All))?; - if self.config.load().editor.mouse { - execute!(stdout, EnableMouseCapture)?; - } - if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { - log::debug!("The enhanced keyboard protocol is supported on this terminal"); - execute!( - stdout, - PushKeyboardEnhancementFlags( - KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES - | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS - ) - )?; - } else { - log::debug!("The enhanced keyboard protocol is not supported on this terminal"); - } + async fn claim_term(&mut self) -> std::io::Result<()> { + let terminal_config = self.config.load().editor.clone().into(); + self.terminal.claim(terminal_config) + } - Ok(()) + fn restore_term(&mut self) -> std::io::Result<()> { + let terminal_config = self.config.load().editor.clone().into(); + use helix_view::graphics::CursorKind; + self.terminal + .backend_mut() + .show_cursor(CursorKind::Block) + .ok(); + self.terminal.restore(terminal_config) } pub async fn run(&mut self, input_stream: &mut S) -> Result @@ -1099,7 +1049,7 @@ impl Application { // We can't handle errors properly inside this closure. And it's // probably not a good idea to `unwrap()` inside a panic handler. // So we just ignore the `Result`. - let _ = restore_term(); + let _ = TerminalBackend::force_restore(); hook(info); })); @@ -1107,13 +1057,7 @@ impl Application { let close_errs = self.close().await; - // restore cursor - use helix_view::graphics::CursorKind; - self.terminal - .backend_mut() - .show_cursor(CursorKind::Block) - .ok(); - restore_term()?; + self.restore_term()?; for err in close_errs { self.editor.exit_code = 1; diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index ccd016f50..3ca7e044e 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -22,5 +22,6 @@ unicode-segmentation = "1.10" crossterm = { version = "0.26", optional = true } termini = "0.1" serde = { version = "1", "optional" = true, features = ["derive"]} +log = "~0.4" helix-view = { version = "0.6", path = "../helix-view", features = ["term"] } helix-core = { version = "0.6", path = "../helix-core" } diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs index 5305640cb..e81c1e008 100644 --- a/helix-tui/src/backend/crossterm.rs +++ b/helix-tui/src/backend/crossterm.rs @@ -1,6 +1,11 @@ -use crate::{backend::Backend, buffer::Cell}; +use crate::{backend::Backend, buffer::Cell, terminal::Config}; use crossterm::{ cursor::{Hide, MoveTo, SetCursorStyle, Show}, + event::{ + DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, + EnableFocusChange, EnableMouseCapture, KeyboardEnhancementFlags, + PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, + }, execute, queue, style::{ Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor, @@ -83,6 +88,69 @@ impl Backend for CrosstermBackend where W: Write, { + fn claim(&mut self, config: Config) -> io::Result<()> { + terminal::enable_raw_mode()?; + execute!( + self.buffer, + terminal::EnterAlternateScreen, + EnableBracketedPaste, + EnableFocusChange + )?; + execute!(self.buffer, terminal::Clear(terminal::ClearType::All))?; + if config.enable_mouse_capture { + execute!(self.buffer, EnableMouseCapture)?; + } + if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { + log::debug!("The enhanced keyboard protocol is supported on this terminal"); + execute!( + self.buffer, + PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS + ) + )?; + } else { + log::debug!("The enhanced keyboard protocol is not supported on this terminal"); + } + Ok(()) + } + + fn restore(&mut self, config: Config) -> io::Result<()> { + // reset cursor shape + write!(self.buffer, "\x1B[0 q")?; + if config.enable_mouse_capture { + execute!(self.buffer, DisableMouseCapture)?; + } + if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { + execute!(self.buffer, PopKeyboardEnhancementFlags)?; + } + execute!( + self.buffer, + DisableBracketedPaste, + DisableFocusChange, + terminal::LeaveAlternateScreen + )?; + terminal::disable_raw_mode() + } + + fn force_restore() -> io::Result<()> { + let mut stdout = io::stdout(); + + // reset cursor shape + write!(stdout, "\x1B[0 q")?; + // Ignore errors on disabling, this might trigger on windows if we call + // disable without calling enable previously + let _ = execute!(stdout, DisableMouseCapture); + let _ = execute!(stdout, PopKeyboardEnhancementFlags); + execute!( + stdout, + DisableBracketedPaste, + DisableFocusChange, + terminal::LeaveAlternateScreen + )?; + terminal::disable_raw_mode() + } + fn draw<'a, I>(&mut self, content: I) -> io::Result<()> where I: Iterator, diff --git a/helix-tui/src/backend/mod.rs b/helix-tui/src/backend/mod.rs index c6c11019d..6d7c38942 100644 --- a/helix-tui/src/backend/mod.rs +++ b/helix-tui/src/backend/mod.rs @@ -1,6 +1,6 @@ use std::io; -use crate::buffer::Cell; +use crate::{buffer::Cell, terminal::Config}; use helix_view::graphics::{CursorKind, Rect}; @@ -13,6 +13,9 @@ mod test; pub use self::test::TestBackend; pub trait Backend { + fn claim(&mut self, config: Config) -> Result<(), io::Error>; + fn restore(&mut self, config: Config) -> Result<(), io::Error>; + fn force_restore() -> Result<(), io::Error>; fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error> where I: Iterator; diff --git a/helix-tui/src/backend/test.rs b/helix-tui/src/backend/test.rs index 52474148e..ff133ff3e 100644 --- a/helix-tui/src/backend/test.rs +++ b/helix-tui/src/backend/test.rs @@ -1,6 +1,7 @@ use crate::{ backend::Backend, buffer::{Buffer, Cell}, + terminal::Config, }; use helix_core::unicode::width::UnicodeWidthStr; use helix_view::graphics::{CursorKind, Rect}; @@ -106,6 +107,18 @@ impl TestBackend { } impl Backend for TestBackend { + fn claim(&mut self, _config: Config) -> Result<(), io::Error> { + Ok(()) + } + + fn restore(&mut self, _config: Config) -> Result<(), io::Error> { + Ok(()) + } + + fn force_restore() -> Result<(), io::Error> { + Ok(()) + } + fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error> where I: Iterator, diff --git a/helix-tui/src/terminal.rs b/helix-tui/src/terminal.rs index 22e9232f3..802a8c1d9 100644 --- a/helix-tui/src/terminal.rs +++ b/helix-tui/src/terminal.rs @@ -1,4 +1,5 @@ use crate::{backend::Backend, buffer::Buffer}; +use helix_view::editor::Config as EditorConfig; use helix_view::graphics::{CursorKind, Rect}; use std::io; @@ -16,6 +17,19 @@ pub struct Viewport { resize_behavior: ResizeBehavior, } +#[derive(Debug)] +pub struct Config { + pub enable_mouse_capture: bool, +} + +impl From for Config { + fn from(config: EditorConfig) -> Self { + Self { + enable_mouse_capture: config.mouse, + } + } +} + impl Viewport { /// UNSTABLE pub fn fixed(area: Rect) -> Viewport { @@ -98,6 +112,14 @@ where }) } + pub fn claim(&mut self, config: Config) -> io::Result<()> { + self.backend.claim(config) + } + + pub fn restore(&mut self, config: Config) -> io::Result<()> { + self.backend.restore(config) + } + // /// Get a Frame object which provides a consistent view into the terminal state for rendering. // pub fn get_frame(&mut self) -> Frame { // Frame { From 611701c36290b81c3c51ed30c49245f341a580e8 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sun, 5 Mar 2023 12:13:11 -0600 Subject: [PATCH 0078/1169] tui: Cache the keyboard enhancement check Wether the host terminal supports keyboard enhancement can be cached for the lifetime of a Helix session. Caching this lookup prevents a potential lockup within crossterm's event reading system where the query for the keyboard enhancement support waits on the next keyboard event, which can happen if the crossterm event stream is checked by `tokio::select!` in another thread. --- Cargo.lock | 1 + helix-tui/Cargo.toml | 1 + helix-tui/src/backend/crossterm.rs | 14 ++++++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 231273956..a03f9c921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1202,6 +1202,7 @@ dependencies = [ "helix-core", "helix-view", "log", + "once_cell", "serde", "termini", "unicode-segmentation", diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index 3ca7e044e..8a6d5367d 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -22,6 +22,7 @@ unicode-segmentation = "1.10" crossterm = { version = "0.26", optional = true } termini = "0.1" serde = { version = "1", "optional" = true, features = ["derive"]} +once_cell = "1.17" log = "~0.4" helix-view = { version = "0.6", path = "../helix-view", features = ["term"] } helix-core = { version = "0.6", path = "../helix-core" } diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs index e81c1e008..7c512ce3e 100644 --- a/helix-tui/src/backend/crossterm.rs +++ b/helix-tui/src/backend/crossterm.rs @@ -15,6 +15,7 @@ use crossterm::{ Command, }; use helix_view::graphics::{Color, CursorKind, Modifier, Rect, UnderlineStyle}; +use once_cell::sync::OnceCell; use std::{ fmt, io::{self, Write}, @@ -57,6 +58,7 @@ impl Capabilities { pub struct CrosstermBackend { buffer: W, capabilities: Capabilities, + supports_keyboard_enhancement_protocol: OnceCell, } impl CrosstermBackend @@ -67,8 +69,16 @@ where CrosstermBackend { buffer, capabilities: Capabilities::from_env_or_default(), + supports_keyboard_enhancement_protocol: OnceCell::new(), } } + + #[inline] + fn supports_keyboard_enhancement_protocol(&self) -> io::Result { + self.supports_keyboard_enhancement_protocol + .get_or_try_init(terminal::supports_keyboard_enhancement) + .copied() + } } impl Write for CrosstermBackend @@ -100,7 +110,7 @@ where if config.enable_mouse_capture { execute!(self.buffer, EnableMouseCapture)?; } - if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { + if self.supports_keyboard_enhancement_protocol()? { log::debug!("The enhanced keyboard protocol is supported on this terminal"); execute!( self.buffer, @@ -121,7 +131,7 @@ where if config.enable_mouse_capture { execute!(self.buffer, DisableMouseCapture)?; } - if matches!(terminal::supports_keyboard_enhancement(), Ok(true)) { + if self.supports_keyboard_enhancement_protocol()? { execute!(self.buffer, PopKeyboardEnhancementFlags)?; } execute!( From 563ac1a3cb7e909b345cf5c892c3bcf39b2e32a4 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 7 Mar 2023 10:33:46 -0600 Subject: [PATCH 0079/1169] tui: Log keyboard enhancement query time In my testing this takes around 3-4ms in terminals that support the enhanced keyboard protocol (Kitty, WezTerm) and a few hundred microseconds in terminals that don't (st, Alacritty). --- helix-tui/src/backend/crossterm.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs index 7c512ce3e..fba1f029c 100644 --- a/helix-tui/src/backend/crossterm.rs +++ b/helix-tui/src/backend/crossterm.rs @@ -76,7 +76,18 @@ where #[inline] fn supports_keyboard_enhancement_protocol(&self) -> io::Result { self.supports_keyboard_enhancement_protocol - .get_or_try_init(terminal::supports_keyboard_enhancement) + .get_or_try_init(|| { + use std::time::Instant; + + let now = Instant::now(); + let support = terminal::supports_keyboard_enhancement(); + log::debug!( + "The keyboard enhancement protocol is {}supported in this terminal (checked in {:?})", + if matches!(support, Ok(true)) { "" } else { "not " }, + Instant::now().duration_since(now) + ); + support + }) .copied() } } @@ -111,7 +122,6 @@ where execute!(self.buffer, EnableMouseCapture)?; } if self.supports_keyboard_enhancement_protocol()? { - log::debug!("The enhanced keyboard protocol is supported on this terminal"); execute!( self.buffer, PushKeyboardEnhancementFlags( @@ -119,8 +129,6 @@ where | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS ) )?; - } else { - log::debug!("The enhanced keyboard protocol is not supported on this terminal"); } Ok(()) } From 170593161cb23edc433d937eb62a9b8fd76bd339 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 7 Mar 2023 19:50:57 -0600 Subject: [PATCH 0080/1169] LSP: Send replies for malformed and unhandled RPC requests (#6058) Previously we did not respond to malformed or unhandled LSP requests. The JSONRPC spec says that all non-notification requests must have responses: > When a rpc call is made, the Server MUST reply with a Response, > except for in the case of Notifications (Note that Helix is the "Server" in this case. Also from the spec: "The Server is defined as the origin of Response objects and the handler of Request objects.") So this change sends error replies for requests which can't be parsed or handled. Request IDs are also now added to the log messages for unhandled requests. --- helix-term/src/application.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 2487a02f8..d56e7c884 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -928,24 +928,32 @@ impl Application { Call::MethodCall(helix_lsp::jsonrpc::MethodCall { method, params, id, .. }) => { - let call = match MethodCall::parse(&method, params) { - Ok(call) => call, + let reply = match MethodCall::parse(&method, params) { Err(helix_lsp::Error::Unhandled) => { - error!("Language Server: Method not found {}", method); - return; + error!( + "Language Server: Method {} not found in request {}", + method, id + ); + Err(helix_lsp::jsonrpc::Error { + code: helix_lsp::jsonrpc::ErrorCode::MethodNotFound, + message: format!("Method not found: {}", method), + data: None, + }) } Err(err) => { log::error!( - "received malformed method call from Language Server: {}: {}", + "Language Server: Received malformed method call {} in request {}: {}", method, + id, err ); - return; + Err(helix_lsp::jsonrpc::Error { + code: helix_lsp::jsonrpc::ErrorCode::ParseError, + message: format!("Malformed method call: {}", method), + data: None, + }) } - }; - - let reply = match call { - MethodCall::WorkDoneProgressCreate(params) => { + Ok(MethodCall::WorkDoneProgressCreate(params)) => { self.lsp_progress.create(server_id, params.token); let editor_view = self @@ -959,7 +967,7 @@ impl Application { Ok(serde_json::Value::Null) } - MethodCall::ApplyWorkspaceEdit(params) => { + Ok(MethodCall::ApplyWorkspaceEdit(params)) => { apply_workspace_edit( &mut self.editor, helix_lsp::OffsetEncoding::Utf8, @@ -972,13 +980,13 @@ impl Application { failed_change: None, })) } - MethodCall::WorkspaceFolders => { + Ok(MethodCall::WorkspaceFolders) => { let language_server = self.editor.language_servers.get_by_id(server_id).unwrap(); Ok(json!(language_server.workspace_folders())) } - MethodCall::WorkspaceConfiguration(params) => { + Ok(MethodCall::WorkspaceConfiguration(params)) => { let result: Vec<_> = params .items .iter() From 6dc017b9e23be4f9331bfac8d10f39cdfa54e7bd Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 7 Mar 2023 19:51:29 -0600 Subject: [PATCH 0081/1169] Jump to symbol ranges in LSP goto commands (#5986) This follows prior changes like 42ad1a9e: we select the range given by the language server rather than the starting point. --- helix-term/src/commands/lsp.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 3b94c9bd5..d59eebdbe 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -5,7 +5,7 @@ use helix_lsp::{ self, CodeAction, CodeActionOrCommand, CodeActionTriggerKind, DiagnosticSeverity, NumberOrString, }, - util::{diagnostic_to_lsp_diagnostic, lsp_pos_to_pos, lsp_range_to_range, range_to_lsp_range}, + util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range}, OffsetEncoding, }; use tui::{ @@ -196,15 +196,15 @@ fn jump_to_location( } } let (view, doc) = current!(editor); - let definition_pos = location.range.start; // TODO: convert inside server - let new_pos = if let Some(new_pos) = lsp_pos_to_pos(doc.text(), definition_pos, offset_encoding) - { - new_pos - } else { - return; - }; - doc.set_selection(view.id, Selection::point(new_pos)); + let new_range = + if let Some(new_range) = lsp_range_to_range(doc.text(), location.range, offset_encoding) { + new_range + } else { + log::warn!("lsp position out of bounds - {:?}", location.range); + return; + }; + doc.set_selection(view.id, Selection::single(new_range.anchor, new_range.head)); align_view(doc, view, Align::Center); } From 0c6d25acae86cf87356fd3b3b59a25904226e41b Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 7 Mar 2023 19:51:52 -0600 Subject: [PATCH 0082/1169] DynamicPicker: Recalculate column widths for new options (#6004) This fixes blank row text in a DynamicPicker which is initially given no options. This can happen for language servers which respond to the workspace symbol request for an empty query with an empty list of symbols, and that behavior is somewhat common since returning all symbols as the spec suggests is very expensive. For empty options, `Picker::new` calculated the widths of each column as 0. We can recalculate the column widths when the new options are set to fix this. This refactor is also a good opportunity to formalize setting new options on a picker: besides setting the new options and calculating column widths we also want to reset the cursor and rescore the options. --- helix-term/src/ui/picker.rs | 62 ++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 803e2d65b..ec8b1c7f4 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -435,26 +435,6 @@ impl Picker { |_editor: &mut Context, _pattern: &str, _event: PromptEvent| {}, ); - let n = options - .first() - .map(|option| option.format(&editor_data).cells.len()) - .unwrap_or_default(); - let max_lens = options.iter().fold(vec![0; n], |mut acc, option| { - let row = option.format(&editor_data); - // maintain max for each column - for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) { - let width = cell.content.width(); - if width > *acc { - *acc = width; - } - } - acc - }); - let widths = max_lens - .into_iter() - .map(|len| Constraint::Length(len as u16)) - .collect(); - let mut picker = Self { options, editor_data, @@ -467,10 +447,12 @@ impl Picker { show_preview: true, callback_fn: Box::new(callback_fn), completion_height: 0, - widths, + widths: Vec::new(), }; - // scoring on empty input: + picker.calculate_column_widths(); + + // scoring on empty input // TODO: just reuse score() picker .matches @@ -486,6 +468,38 @@ impl Picker { picker } + pub fn set_options(&mut self, new_options: Vec) { + self.options = new_options; + self.cursor = 0; + self.force_score(); + self.calculate_column_widths(); + } + + /// Calculate the width constraints using the maximum widths of each column + /// for the current options. + fn calculate_column_widths(&mut self) { + let n = self + .options + .first() + .map(|option| option.format(&self.editor_data).cells.len()) + .unwrap_or_default(); + let max_lens = self.options.iter().fold(vec![0; n], |mut acc, option| { + let row = option.format(&self.editor_data); + // maintain max for each column + for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) { + let width = cell.content.width(); + if width > *acc { + *acc = width; + } + } + acc + }); + self.widths = max_lens + .into_iter() + .map(|len| Constraint::Length(len as u16)) + .collect(); + } + pub fn score(&mut self) { let pattern = self.prompt.line(); @@ -931,9 +945,7 @@ impl Component for DynamicPicker { Some(overlay) => &mut overlay.content.file_picker.picker, None => return, }; - picker.options = new_options; - picker.cursor = 0; - picker.force_score(); + picker.set_options(new_options); editor.reset_idle_timer(); })); anyhow::Ok(callback) From f4bdbe4674e6d023a63d2e64aec48c8c8598be67 Mon Sep 17 00:00:00 2001 From: Kyle Smith Date: Tue, 7 Mar 2023 20:53:31 -0500 Subject: [PATCH 0083/1169] Do not add intermediate lines to jumplist with : command. (#5751) * Do not add intermediate lines to jumplist with : command. * Revert jumplist index changes. * Reduce calculations during update cycle. * Use jumplist for undo, set jumplist before preview. * remove some debug logging * Revert "remove some debug logging" This reverts commit 5772c4327e7121c53ea0726a4d7333ae1c413ffb. * Revert "Use jumplist for undo, set jumplist before preview." This reverts commit f73a1b29824feaf16477b9df547fb28d9db81923. * Add last_selection, update implementation. * @pascalkuthe initial feedback * Ensure ":goto 123" keybinding works as expected. * fix clippies, prefer expect() for expect last_selection state --- helix-term/src/commands.rs | 10 ++-- helix-term/src/commands/typed.rs | 80 ++++++++++++++++++++------------ helix-view/src/editor.rs | 11 +++-- 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index ebdfdfde2..e09a1c5bb 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2828,10 +2828,15 @@ fn push_jump(view: &mut View, doc: &Document) { } fn goto_line(cx: &mut Context) { - goto_line_impl(cx.editor, cx.count) + if cx.count.is_some() { + let (view, doc) = current!(cx.editor); + push_jump(view, doc); + + goto_line_without_jumplist(cx.editor, cx.count); + } } -fn goto_line_impl(editor: &mut Editor, count: Option) { +fn goto_line_without_jumplist(editor: &mut Editor, count: Option) { if let Some(count) = count { let (view, doc) = current!(editor); let text = doc.text().slice(..); @@ -2848,7 +2853,6 @@ fn goto_line_impl(editor: &mut Editor, count: Option) { .clone() .transform(|range| range.put_cursor(text, pos, editor.mode == Mode::Select)); - push_jump(view, doc); doc.set_selection(view.id, selection); } } diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 77c143212..7588b708c 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1572,47 +1572,67 @@ fn tutor( Ok(()) } +fn abort_goto_line_number_preview(cx: &mut compositor::Context) { + if let Some(last_selection) = cx.editor.last_selection.take() { + let scrolloff = cx.editor.config().scrolloff; + + let (view, doc) = current!(cx.editor); + doc.set_selection(view.id, last_selection); + view.ensure_cursor_in_view(doc, scrolloff); + } +} + +fn update_goto_line_number_preview( + cx: &mut compositor::Context, + args: &[Cow], +) -> anyhow::Result<()> { + cx.editor.last_selection.get_or_insert_with(|| { + let (view, doc) = current!(cx.editor); + doc.selection(view.id).clone() + }); + + let scrolloff = cx.editor.config().scrolloff; + let line = args[0].parse::()?; + goto_line_without_jumplist(cx.editor, NonZeroUsize::new(line)); + + let (view, doc) = current!(cx.editor); + view.ensure_cursor_in_view(doc, scrolloff); + + Ok(()) +} + pub(super) fn goto_line_number( cx: &mut compositor::Context, args: &[Cow], event: PromptEvent, ) -> anyhow::Result<()> { match event { - PromptEvent::Abort => { - if let Some(line_number) = cx.editor.last_line_number { - goto_line_impl(cx.editor, NonZeroUsize::new(line_number)); - let (view, doc) = current!(cx.editor); - view.ensure_cursor_in_view(doc, line_number); - cx.editor.last_line_number = None; - } - return Ok(()); - } + PromptEvent::Abort => abort_goto_line_number_preview(cx), PromptEvent::Validate => { ensure!(!args.is_empty(), "Line number required"); - cx.editor.last_line_number = None; - } - PromptEvent::Update => { - if args.is_empty() { - if let Some(line_number) = cx.editor.last_line_number { - // When a user hits backspace and there are no numbers left, - // we can bring them back to their original line - goto_line_impl(cx.editor, NonZeroUsize::new(line_number)); - let (view, doc) = current!(cx.editor); - view.ensure_cursor_in_view(doc, line_number); - cx.editor.last_line_number = None; - } - return Ok(()); - } + + // If we are invoked directly via a keybinding, Validate is + // sent without any prior Update events. Ensure the cursor + // is moved to the appropriate location. + update_goto_line_number_preview(cx, args)?; + + let last_selection = cx + .editor + .last_selection + .take() + .expect("update_goto_line_number_preview should always set last_selection"); + let (view, doc) = current!(cx.editor); - let text = doc.text().slice(..); - let line = doc.selection(view.id).primary().cursor_line(text); - cx.editor.last_line_number.get_or_insert(line + 1); + view.jumps.push((doc.id(), last_selection)); } + + // When a user hits backspace and there are no numbers left, + // we can bring them back to their original selection. If they + // begin typing numbers again, we'll start a new preview session. + PromptEvent::Update if args.is_empty() => abort_goto_line_number_preview(cx), + PromptEvent::Update => update_goto_line_number_preview(cx, args)?, } - let line = args[0].parse::()?; - goto_line_impl(cx.editor, NonZeroUsize::new(line)); - let (view, doc) = current!(cx.editor); - view.ensure_cursor_in_view(doc, line); + Ok(()) } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 50da3ddea..b96eec8de 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -40,12 +40,12 @@ use anyhow::{anyhow, bail, Error}; pub use helix_core::diagnostic::Severity; pub use helix_core::register::Registers; -use helix_core::Position; use helix_core::{ auto_pairs::AutoPairs, syntax::{self, AutoPairConfig}, Change, }; +use helix_core::{Position, Selection}; use helix_dap as dap; use helix_lsp::lsp; @@ -848,7 +848,12 @@ pub struct Editor { /// The currently applied editor theme. While previewing a theme, the previewed theme /// is set here. pub theme: Theme, - pub last_line_number: Option, + + /// The primary Selection prior to starting a goto_line_number preview. This is + /// restored when the preview is aborted, or added to the jumplist when it is + /// confirmed. + pub last_selection: Option, + pub status_msg: Option<(Cow<'static, str>, Severity)>, pub autoinfo: Option, @@ -964,7 +969,7 @@ impl Editor { syn_loader, theme_loader, last_theme: None, - last_line_number: None, + last_selection: None, registers: Registers::default(), clipboard_provider: get_clipboard_provider(), status_msg: None, From 8dd1ab48997fe774165b3aee5d366833c9660710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Delafargue?= Date: Wed, 8 Mar 2023 03:02:11 +0100 Subject: [PATCH 0084/1169] Softwrapping improvements (#5893) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use max_line_width + 1 during softwrap to account for newline char Helix softwrap implementation always wraps lines so that the newline character doesn't get cut off so he line wraps one chars earlier then in other editors. This is necessary, because newline chars are always selecatble in helix and must never be hidden. However That means that `max_line_width` currently wraps one char earlier than expected. The typical definition of line width does not include the newline character and other helix commands like `:reflow` also don't count the newline character here. This commit makes softwrap use `max_line_width + 1` instead of `max_line_width` to correct the impedance missmatch. * fix typos Co-authored-by: Jonathan Lebon * Add text-width to config.toml * text-width: update setting documentation * rename leftover config item * remove leftover max-line-length occurrences * Make `text-width` optional in editor config When it was only used for `:reflow` it made sense to have a default value set to `80`, but now that soft-wrapping uses this setting, keeping a default set to `80` would make soft-wrapping behave more aggressively. * Allow softwrapping to ignore `text-width` Softwrapping wraps by default to the viewport width or a configured `text-width` (whichever's smaller). In some cases we only want to set `text-width` to use for hard-wrapping and let longer lines flow if they have enough space. This setting allows that. * Revert "Make `text-width` optional in editor config" This reverts commit b247d526d69adf41434b6fd9c4983369c785aa22. * soft-wrap: allow per-language overrides * Update book/src/configuration.md Co-authored-by: Pascal Kuthe * Update book/src/languages.md Co-authored-by: Pascal Kuthe * Update book/src/configuration.md Co-authored-by: Pascal Kuthe --------- Co-authored-by: Pascal Kuthe Co-authored-by: Jonathan Lebon Co-authored-by: Alex Boehm Co-authored-by: Blaž Hrastnik --- book/src/configuration.md | 14 ++++---- book/src/languages.md | 2 +- helix-core/src/syntax.rs | 30 ++++++++++++++++- helix-core/src/wrap.rs | 4 +-- helix-term/src/commands/typed.rs | 20 +++++------- helix-view/src/document.rs | 55 ++++++++++++++++++++++++++------ helix-view/src/editor.rs | 42 +++--------------------- languages.toml | 2 +- 8 files changed, 99 insertions(+), 70 deletions(-) diff --git a/book/src/configuration.md b/book/src/configuration.md index 5410024ba..aebf5ff0f 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -55,6 +55,7 @@ signal to the Helix process on Unix operating systems, such as by using the comm | `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file | `[]` | | `bufferline` | Renders a line at the top of the editor displaying open buffers. Can be `always`, `never` or `multiple` (only shown if more than one buffer is in use) | `never` | | `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` | +| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap_at_text_width` is set | `80` | ### `[editor.statusline]` Section @@ -314,12 +315,13 @@ Currently unused Options for soft wrapping lines that exceed the view width: -| Key | Description | Default | -| --- | --- | --- | -| `enable` | Whether soft wrapping is enabled | `false` | -| `max-wrap` | Maximum free space left at the end of the line | `20` | -| `max-indent-retain` | Maximum indentation to carry over when soft wrapping a line | `40` | -| `wrap-indicator` | Text inserted before soft wrapped lines, highlighted with `ui.virtual.wrap` | `↪ ` | +| Key | Description | Default | +| --- | --- | --- | +| `enable` | Whether soft wrapping is enabled. | `false` | +| `max-wrap` | Maximum free space left at the end of the line. | `20` | +| `max-indent-retain` | Maximum indentation to carry over when soft wrapping a line. | `40` | +| `wrap-indicator` | Text inserted before soft wrapped lines, highlighted with `ui.virtual.wrap` | `↪ ` | +| `wrap-at-text-width` | Soft wrap at `text-width` instead of using the full viewport size. | `false` | Example: diff --git a/book/src/languages.md b/book/src/languages.md index 8a8f3bb61..5ed69505d 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -63,7 +63,7 @@ These configuration keys are available: | `config` | Language Server configuration | | `grammar` | The tree-sitter grammar to use (defaults to the value of `name`) | | `formatter` | The formatter for the language, it will take precedence over the lsp when defined. The formatter must be able to take the original file as input from stdin and write the formatted file to stdout | -| `max-line-length` | Maximum line length. Used for the `:reflow` command and soft-wrapping | +| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap_at_text_width` is set, defaults to `editor.text-width` | ### File-type detection and the `file-types` key diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 1b6c1b1da..941e3ba7b 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -82,7 +82,8 @@ pub struct LanguageConfiguration { pub shebangs: Vec, // interpreter(s) associated with language pub roots: Vec, // these indicate project roots <.git, Cargo.toml> pub comment_token: Option, - pub max_line_length: Option, + pub text_width: Option, + pub soft_wrap: Option, #[serde(default, skip_serializing, deserialize_with = "deserialize_lsp_config")] pub config: Option, @@ -546,6 +547,33 @@ impl LanguageConfiguration { .ok() } } +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] +pub struct SoftWrap { + /// Soft wrap lines that exceed viewport width. Default to off + pub enable: Option, + /// Maximum space left free at the end of the line. + /// This space is used to wrap text at word boundaries. If that is not possible within this limit + /// the word is simply split at the end of the line. + /// + /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. + /// + /// Default to 20 + pub max_wrap: Option, + /// Maximum number of indentation that can be carried over from the previous line when softwrapping. + /// If a line is indented further then this limit it is rendered at the start of the viewport instead. + /// + /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. + /// + /// Default to 40 + pub max_indent_retain: Option, + /// Indicator placed at the beginning of softwrapped lines + /// + /// Defaults to ↪ + pub wrap_indicator: Option, + /// Softwrap at `text_width` instead of viewport width if it is shorter + pub wrap_at_text_width: Option, +} // Expose loader as Lazy<> global since it's always static? diff --git a/helix-core/src/wrap.rs b/helix-core/src/wrap.rs index eabc47d47..2ba8d173e 100644 --- a/helix-core/src/wrap.rs +++ b/helix-core/src/wrap.rs @@ -2,6 +2,6 @@ use smartstring::{LazyCompact, SmartString}; /// Given a slice of text, return the text re-wrapped to fit it /// within the given width. -pub fn reflow_hard_wrap(text: &str, max_line_len: usize) -> SmartString { - textwrap::refill(text, max_line_len).into() +pub fn reflow_hard_wrap(text: &str, text_width: usize) -> SmartString { + textwrap::refill(text, text_width).into() } diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 7588b708c..5ea611086 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1840,30 +1840,26 @@ fn reflow( } let scrolloff = cx.editor.config().scrolloff; + let cfg_text_width: usize = cx.editor.config().text_width; let (view, doc) = current!(cx.editor); - const DEFAULT_MAX_LEN: usize = 79; - - // Find the max line length by checking the following sources in order: + // Find the text_width by checking the following sources in order: // - The passed argument in `args` - // - The configured max_line_len for this language in languages.toml - // - The const default we set above - let max_line_len: usize = args + // - The configured text-width for this language in languages.toml + // - The configured text-width in the config.toml + let text_width: usize = args .get(0) .map(|num| num.parse::()) .transpose()? - .or_else(|| { - doc.language_config() - .and_then(|config| config.max_line_length) - }) - .unwrap_or(DEFAULT_MAX_LEN); + .or_else(|| doc.language_config().and_then(|config| config.text_width)) + .unwrap_or(cfg_text_width); let rope = doc.text(); let selection = doc.selection(view.id); let transaction = Transaction::change_by_selection(rope, selection, |range| { let fragment = range.fragment(rope.slice(..)); - let reflowed_text = helix_core::wrap::reflow_hard_wrap(&fragment, max_line_len); + let reflowed_text = helix_core::wrap::reflow_hard_wrap(&fragment, text_width); (range.from(), range.to(), Some(reflowed_text)) }); diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 579c67250..4d3586f1a 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1238,24 +1238,61 @@ impl Document { } pub fn text_format(&self, mut viewport_width: u16, theme: Option<&Theme>) -> TextFormat { - if let Some(max_line_len) = self + let config = self.config.load(); + let text_width = self .language_config() - .and_then(|config| config.max_line_length) - { - viewport_width = viewport_width.min(max_line_len as u16) + .and_then(|config| config.text_width) + .unwrap_or(config.text_width); + let soft_wrap_at_text_width = self + .language_config() + .and_then(|config| { + config + .soft_wrap + .as_ref() + .and_then(|soft_wrap| soft_wrap.wrap_at_text_width) + }) + .or(config.soft_wrap.wrap_at_text_width) + .unwrap_or(false); + if soft_wrap_at_text_width { + // We increase max_line_len by 1 because softwrap considers the newline character + // as part of the line length while the "typical" expectation is that this is not the case. + // In particular other commands like :reflow do not count the line terminator. + // This is technically inconsistent for the last line as that line never has a line terminator + // but having the last visual line exceed the width by 1 seems like a rare edge case. + viewport_width = viewport_width.min(text_width as u16 + 1) } let config = self.config.load(); - let soft_wrap = &config.soft_wrap; + let editor_soft_wrap = &config.soft_wrap; + let language_soft_wrap = self + .language + .as_ref() + .and_then(|config| config.soft_wrap.as_ref()); + let enable_soft_wrap = language_soft_wrap + .and_then(|soft_wrap| soft_wrap.enable) + .or(editor_soft_wrap.enable) + .unwrap_or(false); + let max_wrap = language_soft_wrap + .and_then(|soft_wrap| soft_wrap.max_wrap) + .or(config.soft_wrap.max_wrap) + .unwrap_or(20); + let max_indent_retain = language_soft_wrap + .and_then(|soft_wrap| soft_wrap.max_indent_retain) + .or(editor_soft_wrap.max_indent_retain) + .unwrap_or(40); + let wrap_indicator = language_soft_wrap + .and_then(|soft_wrap| soft_wrap.wrap_indicator.clone()) + .or_else(|| config.soft_wrap.wrap_indicator.clone()) + .unwrap_or_else(|| "↪ ".into()); let tab_width = self.tab_width() as u16; TextFormat { - soft_wrap: soft_wrap.enable && viewport_width > 10, + soft_wrap: enable_soft_wrap && viewport_width > 10, tab_width, - max_wrap: soft_wrap.max_wrap.min(viewport_width / 4), - max_indent_retain: soft_wrap.max_indent_retain.min(viewport_width * 2 / 5), + max_wrap: max_wrap.min(viewport_width / 4), + max_indent_retain: max_indent_retain.min(viewport_width * 2 / 5), // avoid spinning forever when the window manager // sets the size to something tiny viewport_width, - wrap_indicator: soft_wrap.wrap_indicator.clone().into_boxed_str(), + wrap_indicator: wrap_indicator.into_boxed_str(), wrap_indicator_highlight: theme .and_then(|theme| theme.find_scope_index("ui.virtual.wrap")) .map(Highlight), diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index b96eec8de..5b819b333 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -42,7 +42,7 @@ pub use helix_core::diagnostic::Severity; pub use helix_core::register::Registers; use helix_core::{ auto_pairs::AutoPairs, - syntax::{self, AutoPairConfig}, + syntax::{self, AutoPairConfig, SoftWrap}, Change, }; use helix_core::{Position, Selection}; @@ -241,6 +241,8 @@ pub struct Config { pub auto_format: bool, /// Automatic save on focus lost. Defaults to false. pub auto_save: bool, + /// Set a global text_width + pub text_width: usize, /// Time in milliseconds since last keypress before idle timers trigger. /// Used for autocompletion, set to 0 for instant. Defaults to 400ms. #[serde( @@ -276,43 +278,6 @@ pub struct Config { pub soft_wrap: SoftWrap, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] -pub struct SoftWrap { - /// Soft wrap lines that exceed viewport width. Default to off - pub enable: bool, - /// Maximum space left free at the end of the line. - /// This space is used to wrap text at word boundaries. If that is not possible within this limit - /// the word is simply split at the end of the line. - /// - /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. - /// - /// Default to 20 - pub max_wrap: u16, - /// Maximum number of indentation that can be carried over from the previous line when softwrapping. - /// If a line is indented further then this limit it is rendered at the start of the viewport instead. - /// - /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. - /// - /// Default to 40 - pub max_indent_retain: u16, - /// Indicator placed at the beginning of softwrapped lines - /// - /// Defaults to ↪ - pub wrap_indicator: String, -} - -impl Default for SoftWrap { - fn default() -> Self { - SoftWrap { - enable: false, - max_wrap: 20, - max_indent_retain: 40, - wrap_indicator: "↪ ".into(), - } - } -} - #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(default, rename_all = "kebab-case", deny_unknown_fields)] pub struct TerminalConfig { @@ -772,6 +737,7 @@ impl Default for Config { indent_guides: IndentGuidesConfig::default(), color_modes: false, soft_wrap: SoftWrap::default(), + text_width: 80, } } } diff --git a/languages.toml b/languages.toml index 0fd37a006..8697f9fcc 100644 --- a/languages.toml +++ b/languages.toml @@ -1104,7 +1104,7 @@ file-types = ["COMMIT_EDITMSG"] comment-token = "#" indent = { tab-width = 2, unit = " " } rulers = [50, 72] -max-line-length = 72 +text-width = 72 [[grammar]] name = "git-commit" From 3849ca4c2abdb1c6076d391eab4c3f43dda30234 Mon Sep 17 00:00:00 2001 From: Kyle Smith Date: Tue, 7 Mar 2023 21:10:55 -0500 Subject: [PATCH 0085/1169] Add test cases for existing pair matching logic. (#6027) * Add test cases for existing pair matching logic. * fix clippy --- helix-core/src/surround.rs | 145 +++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 29 deletions(-) diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs index 64d48c13a..f430aee8a 100644 --- a/helix-core/src/surround.rs +++ b/helix-core/src/surround.rs @@ -275,51 +275,138 @@ mod test { #[test] fn test_get_surround_pos() { - let doc = Rope::from("(some) (chars)\n(newline)"); - let slice = doc.slice(..); - let selection = Selection::new( - SmallVec::from_slice(&[Range::point(2), Range::point(9), Range::point(20)]), - 0, - ); + #[rustfmt::skip] + let (doc, selection, expectations) = + rope_with_selections_and_expectations( + "(some) (chars)\n(newline)", + "_ ^ _ _ ^ _\n_ ^ _" + ); - // cursor on s[o]me, c[h]ars, newl[i]ne assert_eq!( - get_surround_pos(slice, &selection, Some('('), 1) - .unwrap() - .as_slice(), - &[0, 5, 7, 13, 15, 23] + get_surround_pos(doc.slice(..), &selection, Some('('), 1).unwrap(), + expectations ); } #[test] - fn test_get_surround_pos_bail() { - let doc = Rope::from("[some]\n(chars)xx\n(newline)"); - let slice = doc.slice(..); + fn test_get_surround_pos_bail_different_surround_chars() { + #[rustfmt::skip] + let (doc, selection, _) = + rope_with_selections_and_expectations( + "[some]\n(chars)xx\n(newline)", + " ^ \n ^ \n " + ); - let selection = - Selection::new(SmallVec::from_slice(&[Range::point(2), Range::point(9)]), 0); - // cursor on s[o]me, c[h]ars assert_eq!( - get_surround_pos(slice, &selection, Some('('), 1), - Err(Error::PairNotFound) // different surround chars + get_surround_pos(doc.slice(..), &selection, Some('('), 1), + Err(Error::PairNotFound) ); + } + + #[test] + fn test_get_surround_pos_bail_overlapping_surround_chars() { + #[rustfmt::skip] + let (doc, selection, _) = + rope_with_selections_and_expectations( + "[some]\n(chars)xx\n(newline)", + " \n ^ \n ^ " + ); - let selection = Selection::new( - SmallVec::from_slice(&[Range::point(14), Range::point(24)]), - 0, - ); - // cursor on [x]x, newli[n]e assert_eq!( - get_surround_pos(slice, &selection, Some('('), 1), + get_surround_pos(doc.slice(..), &selection, Some('('), 1), Err(Error::PairNotFound) // overlapping surround chars ); + } + + #[test] + fn test_get_surround_pos_bail_cursor_overlap() { + #[rustfmt::skip] + let (doc, selection, _) = + rope_with_selections_and_expectations( + "[some]\n(chars)xx\n(newline)", + " ^^ \n \n " + ); - let selection = - Selection::new(SmallVec::from_slice(&[Range::point(2), Range::point(3)]), 0); - // cursor on s[o][m]e assert_eq!( - get_surround_pos(slice, &selection, Some('['), 1), + get_surround_pos(doc.slice(..), &selection, Some('['), 1), Err(Error::CursorOverlap) ); } + + #[test] + fn test_find_nth_pairs_pos_quote_success() { + #[rustfmt::skip] + let (doc, selection, expectations) = + rope_with_selections_and_expectations( + "some 'quoted text' on this 'line'\n'and this one'", + " _ ^ _ \n " + ); + + assert_eq!(2, expectations.len()); + assert_eq!( + find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1) + .expect("find should succeed"), + (expectations[0], expectations[1]) + ) + } + + #[test] + fn test_find_nth_pairs_pos_nested_quote_success() { + #[rustfmt::skip] + let (doc, selection, expectations) = + rope_with_selections_and_expectations( + "some 'nested 'quoted' text' on this 'line'\n'and this one'", + " _ ^ _ \n " + ); + + assert_eq!(2, expectations.len()); + assert_eq!( + find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 2) + .expect("find should succeed"), + (expectations[0], expectations[1]) + ) + } + + #[test] + fn test_find_nth_pairs_pos_inside_quote_ambiguous() { + #[rustfmt::skip] + let (doc, selection, _) = + rope_with_selections_and_expectations( + "some 'nested 'quoted' text' on this 'line'\n'and this one'", + " ^ \n " + ); + + assert_eq!( + find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1), + Err(Error::CursorOnAmbiguousPair) + ) + } + + // Create a Rope and a matching Selection using a specification language. + // ^ is a single-point selection. + // _ is an expected index. These are returned as a Vec for use in assertions. + fn rope_with_selections_and_expectations( + text: &str, + spec: &str, + ) -> (Rope, Selection, Vec) { + if text.len() != spec.len() { + panic!("specification must match text length -- are newlines aligned?"); + } + + let rope = Rope::from(text); + + let selections: SmallVec<[Range; 1]> = spec + .match_indices('^') + .into_iter() + .map(|(i, _)| Range::point(i)) + .collect(); + + let expectations: Vec = spec + .match_indices('_') + .into_iter() + .map(|(i, _)| i) + .collect(); + + (rope, Selection::new(selections, 0), expectations) + } } From 44ff8a1df1f69733bc40ea866674fcfd7e0cdded Mon Sep 17 00:00:00 2001 From: Kyle Smith Date: Tue, 7 Mar 2023 21:11:43 -0500 Subject: [PATCH 0086/1169] LSP: Support textDocument/prepareRename (#6103) * LSP: Support textDocument/prepareRename 'textDocument/prepareRename' can be used by the client to ask the server the range of the symbol under the cursor which would be changed by a subsequent call to 'textDocument/rename' with that position. We can use this information to fill the prompt with an accurate prefill which can improve the UX for renaming symbols when the symbol doesn't align with the "word" textobject. (We currently use the "word" textobject as a default value for the prompt.) Co-authored-by: Michael Davis * clippy fixes * rustfmt * Update helix-term/src/commands/lsp.rs Co-authored-by: Michael Davis * Update helix-term/src/commands/lsp.rs Co-authored-by: Michael Davis * fix clippy from suggestions * Update helix-term/src/commands/lsp.rs Co-authored-by: Michael Davis --------- Co-authored-by: Michael Davis --- helix-lsp/src/client.rs | 25 +++++- helix-term/src/commands/lsp.rs | 136 ++++++++++++++++++++++++--------- 2 files changed, 125 insertions(+), 36 deletions(-) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 95f3ea348..9fa118fbd 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -359,7 +359,7 @@ impl Client { }), rename: Some(lsp::RenameClientCapabilities { dynamic_registration: Some(false), - prepare_support: Some(false), + prepare_support: Some(true), prepare_support_default_behavior: None, honors_change_annotations: Some(false), }), @@ -1034,6 +1034,29 @@ impl Client { Some(self.call::(params)) } + pub fn prepare_rename( + &self, + text_document: lsp::TextDocumentIdentifier, + position: lsp::Position, + ) -> Option>> { + let capabilities = self.capabilities.get().unwrap(); + + match capabilities.rename_provider { + Some(lsp::OneOf::Right(lsp::RenameOptions { + prepare_provider: Some(true), + .. + })) => (), + _ => return None, + } + + let params = lsp::TextDocumentPositionParams { + text_document, + position, + }; + + Some(self.call::(params)) + } + // empty string to get all symbols pub fn workspace_symbols(&self, query: String) -> Option>> { let capabilities = self.capabilities.get().unwrap(); diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index d59eebdbe..08519366b 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -1232,49 +1232,115 @@ pub fn hover(cx: &mut Context) { } pub fn rename_symbol(cx: &mut Context) { - let (view, doc) = current_ref!(cx.editor); - let text = doc.text().slice(..); - let primary_selection = doc.selection(view.id).primary(); - let prefill = if primary_selection.len() > 1 { - primary_selection - } else { - use helix_core::textobject::{textobject_word, TextObject}; - textobject_word(text, primary_selection, TextObject::Inside, 1, false) + fn get_prefill_from_word_boundary(editor: &Editor) -> String { + let (view, doc) = current_ref!(editor); + let text = doc.text().slice(..); + let primary_selection = doc.selection(view.id).primary(); + if primary_selection.len() > 1 { + primary_selection + } else { + use helix_core::textobject::{textobject_word, TextObject}; + textobject_word(text, primary_selection, TextObject::Inside, 1, false) + } + .fragment(text) + .into() } - .fragment(text) - .into(); - ui::prompt_with_input( - cx, - "rename-to:".into(), - prefill, - None, - ui::completers::none, - move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { - if event != PromptEvent::Validate { - return; + + fn get_prefill_from_lsp_response( + editor: &Editor, + offset_encoding: OffsetEncoding, + response: Option, + ) -> Result { + match response { + Some(lsp::PrepareRenameResponse::Range(range)) => { + let text = doc!(editor).text(); + + Ok(lsp_range_to_range(text, range, offset_encoding) + .ok_or("lsp sent invalid selection range for rename")? + .fragment(text.slice(..)) + .into()) + } + Some(lsp::PrepareRenameResponse::RangeWithPlaceholder { placeholder, .. }) => { + Ok(placeholder) + } + Some(lsp::PrepareRenameResponse::DefaultBehavior { .. }) => { + Ok(get_prefill_from_word_boundary(editor)) } + None => Err("lsp did not respond to prepare rename request"), + } + } - let (view, doc) = current!(cx.editor); - let language_server = language_server!(cx.editor, doc); - let offset_encoding = language_server.offset_encoding(); + fn create_rename_prompt(editor: &Editor, prefill: String) -> Box { + let prompt = ui::Prompt::new( + "rename-to:".into(), + None, + ui::completers::none, + move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { + if event != PromptEvent::Validate { + return; + } - let pos = doc.position(view.id, offset_encoding); + let (view, doc) = current!(cx.editor); + let language_server = language_server!(cx.editor, doc); + let offset_encoding = language_server.offset_encoding(); + + let pos = doc.position(view.id, offset_encoding); + + let future = + match language_server.rename_symbol(doc.identifier(), pos, input.to_string()) { + Some(future) => future, + None => { + cx.editor + .set_error("Language server does not support symbol renaming"); + return; + } + }; + match block_on(future) { + Ok(edits) => apply_workspace_edit(cx.editor, offset_encoding, &edits), + Err(err) => cx.editor.set_error(err.to_string()), + } + }, + ) + .with_line(prefill, editor); - let future = - match language_server.rename_symbol(doc.identifier(), pos, input.to_string()) { - Some(future) => future, - None => { - cx.editor - .set_error("Language server does not support symbol renaming"); + Box::new(prompt) + } + + let (view, doc) = current!(cx.editor); + let language_server = language_server!(cx.editor, doc); + let offset_encoding = language_server.offset_encoding(); + + let pos = doc.position(view.id, offset_encoding); + + match language_server.prepare_rename(doc.identifier(), pos) { + // Language server supports textDocument/prepareRename, use it. + Some(future) => cx.callback( + future, + move |editor, compositor, response: Option| { + let prefill = match get_prefill_from_lsp_response(editor, offset_encoding, response) + { + Ok(p) => p, + Err(e) => { + editor.set_error(e); return; } }; - match block_on(future) { - Ok(edits) => apply_workspace_edit(cx.editor, offset_encoding, &edits), - Err(err) => cx.editor.set_error(err.to_string()), - } - }, - ); + + let prompt = create_rename_prompt(editor, prefill); + + compositor.push(prompt); + }, + ), + // Language server does not support textDocument/prepareRename, fall back + // to word boundary selection. + None => { + let prefill = get_prefill_from_word_boundary(cx.editor); + + let prompt = create_rename_prompt(cx.editor, prefill); + + cx.push_layer(prompt); + } + }; } pub fn select_references_to_symbol_under_cursor(cx: &mut Context) { From 34be71fb50738a7e9d9e5ee5090680a0d84a321c Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Wed, 8 Mar 2023 15:06:34 +0100 Subject: [PATCH 0087/1169] Theme - auy_evolve: Up bufferline fg brightness (#6225) Currently a bit hard to discern inactive and active buffers in a brighter environment. --- runtime/themes/ayu_evolve.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/themes/ayu_evolve.toml b/runtime/themes/ayu_evolve.toml index 2c545beb1..b57235a6a 100644 --- a/runtime/themes/ayu_evolve.toml +++ b/runtime/themes/ayu_evolve.toml @@ -25,6 +25,8 @@ inherits = 'ayu_dark' "ui.cursor.primary.select" = { fg = "dark_gray", bg = "magenta" } "ui.cursor.primary.insert" = { fg = "dark_gray", bg = "green" } "ui.text.inactive" = "gray" +"ui.bufferline" = { fg = "light_gray", bg = "background" } +"ui.bufferline.active" = { fg = "light_gray", bg = "dark_gray" } [palette] background = '#020202' From bc23e548050570d297f263c5f0204af1d2210830 Mon Sep 17 00:00:00 2001 From: workingj <19818596+workingj@users.noreply.github.com> Date: Thu, 9 Mar 2023 03:17:45 +0100 Subject: [PATCH 0088/1169] feat(theme): Update pop-dark statusline (#6227) * update pop-theme for color-modes * fixed ui.statusline.select not worrking * adjustments for nicer statusline visuals * added status line color --- runtime/themes/pop-dark.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/themes/pop-dark.toml b/runtime/themes/pop-dark.toml index d2010ed62..17621d2af 100644 --- a/runtime/themes/pop-dark.toml +++ b/runtime/themes/pop-dark.toml @@ -28,6 +28,7 @@ error = { fg = 'brownD', bg = 'redE', modifiers = ['bold'] } 'ui.linenr' = { bg = 'brownU', fg = 'greyL' } 'ui.linenr.selected' = { fg = 'orangeH' } 'ui.cursorline' = { bg = 'brownH' } +'ui.statusline' = { fg = 'greyT', bg = 'brownU' } 'ui.statusline.inactive' = { fg = 'greyT', bg = 'brownN' } 'ui.statusline.normal' = { fg = 'greyT', bg = 'brownD', modifiers = ['bold'] } 'ui.statusline.select' = { bg = 'blueL', fg = 'brownD', modifiers = ['bold'] } From 4300a3ad058ea383a59a0a90f31a597eb264a7d4 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 2 Mar 2023 19:08:47 +0100 Subject: [PATCH 0089/1169] create savepoint before requesting completion --- helix-term/src/commands.rs | 1 + helix-term/src/ui/editor.rs | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e09a1c5bb..55ca875dc 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4181,6 +4181,7 @@ pub fn completion(cx: &mut Context) { iter.reverse(); let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count(); let start_offset = cursor.saturating_sub(offset); + doc.savepoint(); cx.callback( future, diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 59f371bda..2ea1b7147 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -957,9 +957,6 @@ impl EditorView { return; } - // Immediately initialize a savepoint - doc_mut!(editor).savepoint(); - editor.last_completion = None; self.last_insert.1.push(InsertEvent::TriggerCompletion); From 2588fa3710921683c16a84ffd91103a0823a033b Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 2 Mar 2023 19:10:47 +0100 Subject: [PATCH 0090/1169] save selection before completion savepoint Currently, the selection is not saved/restored when completion checkpoints are applied. This is usually fine because undoing changes usually restores maps selections back in insert mode. But this is not always the case and especially problematic in the presence of multi-cursor completions (since completions are applied relative to the selection/cursor) and snippets (which can change the selection) --- helix-term/src/commands.rs | 2 +- helix-term/src/ui/completion.rs | 2 +- helix-term/src/ui/editor.rs | 4 ++-- helix-view/src/document.rs | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 55ca875dc..bc0e8ebea 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4181,7 +4181,7 @@ pub fn completion(cx: &mut Context) { iter.reverse(); let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count(); let start_offset = cursor.saturating_sub(offset); - doc.savepoint(); + doc.savepoint(&view); cx.callback( future, diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 85931fe32..179a8cf8c 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -235,7 +235,7 @@ impl Completion { ); // initialize a savepoint - doc.savepoint(); + doc.savepoint(&view); doc.apply(&transaction, view.id); editor.last_completion = Some(CompleteAction { diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 2ea1b7147..62f04cc9d 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -914,8 +914,8 @@ impl EditorView { doc.apply(&tx, view.id); } InsertEvent::TriggerCompletion => { - let (_, doc) = current!(cxt.editor); - doc.savepoint(); + let (view, doc) = current!(cxt.editor); + doc.savepoint(view); } } } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 4d3586f1a..13ffe7948 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -941,7 +941,8 @@ impl Document { } pub fn savepoint(&mut self) { - self.savepoint = Some(Transaction::new(self.text())); + self.savepoint = + Some(Transaction::new(self.text()).with_selection(self.selection(view.id).clone())); } pub fn restore(&mut self, view: &mut View) { From e8898fd9a8ac8120827fb2d6f4752b3cb2431a62 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 2 Mar 2023 21:56:55 +0100 Subject: [PATCH 0091/1169] store multiple snapshots on the document at once Fixing autocomplete required moving the document savepoint before the asynchronous completion request. However, this in turn causes new bugs: If the completion popup is open, the savepoint is restored when the popup closes (or another entry is selected). However, at that point a new completion request might already have been created which would have replaced the new savepoint (therefore leading to incorrectly applied complies). This commit fixes that bug by allowing in arbitrary number of savepoints to be tracked on the document. The savepoints are reference counted and therefore remain valid as long as any reference to them remains. Weak reference are stored on the document and any reference that can not be upgraded anymore (hence no strong reference remain) are automatically discarded. --- Cargo.lock | 1 + helix-term/src/commands.rs | 3 +- helix-term/src/ui/completion.rs | 8 ++-- helix-term/src/ui/editor.rs | 14 +++++-- helix-view/Cargo.toml | 1 + helix-view/src/document.rs | 69 ++++++++++++++++++++++++++------- 6 files changed, 72 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a03f9c921..a1a9eae4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1241,6 +1241,7 @@ dependencies = [ "libc", "log", "once_cell", + "parking_lot", "serde", "serde_json", "slotmap", diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index bc0e8ebea..01673c895 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4181,7 +4181,7 @@ pub fn completion(cx: &mut Context) { iter.reverse(); let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count(); let start_offset = cursor.saturating_sub(offset); - doc.savepoint(&view); + let savepoint = doc.savepoint(view); cx.callback( future, @@ -4209,6 +4209,7 @@ pub fn completion(cx: &mut Context) { let ui = compositor.find::().unwrap(); ui.set_completion( editor, + savepoint, items, offset_encoding, start_offset, diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 179a8cf8c..ef88938fe 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -1,12 +1,13 @@ use crate::compositor::{Component, Context, Event, EventResult}; use helix_view::{ + document::SavePoint, editor::CompleteAction, theme::{Modifier, Style}, ViewId, }; use tui::{buffer::Buffer as Surface, text::Span}; -use std::borrow::Cow; +use std::{borrow::Cow, sync::Arc}; use helix_core::{Change, Transaction}; use helix_view::{graphics::Rect, Document, Editor}; @@ -101,6 +102,7 @@ impl Completion { pub fn new( editor: &Editor, + savepoint: Arc, mut items: Vec, offset_encoding: helix_lsp::OffsetEncoding, start_offset: usize, @@ -213,11 +215,10 @@ impl Completion { let (view, doc) = current!(editor); // if more text was entered, remove it - doc.restore(view); + doc.restore(view, &savepoint); match event { PromptEvent::Abort => { - doc.restore(view); editor.last_completion = None; } PromptEvent::Update => { @@ -235,7 +236,6 @@ impl Completion { ); // initialize a savepoint - doc.savepoint(&view); doc.apply(&transaction, view.id); editor.last_completion = Some(CompleteAction { diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 62f04cc9d..c81ae635a 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -940,17 +940,25 @@ impl EditorView { } } + #[allow(clippy::too_many_arguments)] pub fn set_completion( &mut self, editor: &mut Editor, + savepoint: Arc, items: Vec, offset_encoding: helix_lsp::OffsetEncoding, start_offset: usize, trigger_offset: usize, size: Rect, ) { - let mut completion = - Completion::new(editor, items, offset_encoding, start_offset, trigger_offset); + let mut completion = Completion::new( + editor, + savepoint, + items, + offset_encoding, + start_offset, + trigger_offset, + ); if completion.is_empty() { // skip if we got no completion results @@ -969,8 +977,6 @@ impl EditorView { self.completion = None; // Clear any savepoints - let doc = doc_mut!(editor); - doc.savepoint = None; editor.clear_idle_timer(); // don't retrigger } diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 54b679ade..e3f98a8d3 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -43,6 +43,7 @@ toml = "0.7" log = "~0.4" which = "4.4" +parking_lot = "0.12.1" [target.'cfg(windows)'.dependencies] diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 13ffe7948..db12fb92b 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -9,6 +9,7 @@ use helix_core::text_annotations::TextAnnotations; use helix_core::Range; use helix_vcs::{DiffHandle, DiffProviderRegistry}; +use ::parking_lot::Mutex; use serde::de::{self, Deserialize, Deserializer}; use serde::Serialize; use std::borrow::Cow; @@ -18,7 +19,7 @@ use std::fmt::Display; use std::future::Future; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use std::time::SystemTime; use helix_core::{ @@ -105,6 +106,13 @@ pub struct DocumentSavedEvent { pub type DocumentSavedEventResult = Result; pub type DocumentSavedEventFuture = BoxFuture<'static, DocumentSavedEventResult>; +#[derive(Debug)] +pub struct SavePoint { + /// The view this savepoint is associated with + pub view: ViewId, + revert: Mutex, +} + pub struct Document { pub(crate) id: DocumentId, text: Rope, @@ -136,7 +144,7 @@ pub struct Document { pub history: Cell, pub config: Arc>, - pub savepoint: Option, + savepoints: Vec>, // Last time we wrote to the file. This will carry the time the file was last opened if there // were no saves. @@ -389,7 +397,7 @@ impl Document { diagnostics: Vec::new(), version: 0, history: Cell::new(History::default()), - savepoint: None, + savepoints: Vec::new(), last_saved_time: SystemTime::now(), last_saved_revision: 0, modified_since_accessed: false, @@ -846,11 +854,18 @@ impl Document { } // generate revert to savepoint - if self.savepoint.is_some() { - take_with(&mut self.savepoint, |prev_revert| { - let revert = transaction.invert(&old_doc); - Some(revert.compose(prev_revert.unwrap())) - }); + if !self.savepoints.is_empty() { + let revert = transaction.invert(&old_doc); + self.savepoints + .retain_mut(|save_point| match save_point.upgrade() { + Some(savepoint) => { + let mut revert_to_savepoint = savepoint.revert.lock(); + *revert_to_savepoint = + revert.clone().compose(mem::take(&mut revert_to_savepoint)); + true + } + None => false, + }) } // update tree-sitter syntax tree @@ -940,15 +955,39 @@ impl Document { self.undo_redo_impl(view, false) } - pub fn savepoint(&mut self) { - self.savepoint = - Some(Transaction::new(self.text()).with_selection(self.selection(view.id).clone())); + /// Creates a reference counted snapshot (called savpepoint) of the document. + /// + /// The snapshot will remain valid (and updated) idenfinitly as long as ereferences to it exist. + /// Restoring the snapshot will restore the selection and the contents of the document to + /// the state it had when this function was called. + pub fn savepoint(&mut self, view: &View) -> Arc { + let revert = Transaction::new(self.text()).with_selection(self.selection(view.id).clone()); + let savepoint = Arc::new(SavePoint { + view: view.id, + revert: Mutex::new(revert), + }); + self.savepoints.push(Arc::downgrade(&savepoint)); + savepoint } - pub fn restore(&mut self, view: &mut View) { - if let Some(revert) = self.savepoint.take() { - self.apply(&revert, view.id); - } + pub fn restore(&mut self, view: &mut View, savepoint: &SavePoint) { + assert_eq!( + savepoint.view, view.id, + "Savepoint must not be used with a different view!" + ); + // search and remove savepoint using a ptr comparison + // this avoids a deadlock as we need to lock the mutex + let savepoint_idx = self + .savepoints + .iter() + .position(|savepoint_ref| savepoint_ref.as_ptr() == savepoint as *const _) + .expect("Savepoint must belong to this document"); + + let savepoint_ref = self.savepoints.remove(savepoint_idx); + let mut revert = savepoint.revert.lock(); + self.apply(&revert, view.id); + *revert = Transaction::new(self.text()).with_selection(self.selection(view.id).clone()); + self.savepoints.push(savepoint_ref) } fn earlier_later_impl(&mut self, view: &mut View, uk: UndoKind, earlier: bool) -> bool { From 8cb7cdfd7a01b9cb50b9142e9a5d133bd1e23256 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 2 Mar 2023 23:12:50 +0100 Subject: [PATCH 0092/1169] discard stale completion requests Completion requests are computed asynchronously to avoid common micro freezes while editing. This means that once a completion request completes, the state of the editor might have changed. Currently, there is a check to ensure we are still in insert mode. However, we also need to ensure that the view and document hasn't changed to avoid accidentally using a savepoint with the wrong view/document. Furthermore, the editor might request a new completion while the previous completion request hasn't complemented yet. This can lead to weird flickering or an outdated completion request replacing a newer completion that has already completed (the LSP server is not required to process completion requests in order). This change also needed to ensure determinism/linear ordering so that completion popup always correspond to the last completion request. --- helix-term/src/commands.rs | 31 +++++++++++++++++++++++++++++-- helix-term/src/ui/editor.rs | 1 + helix-view/src/editor.rs | 11 ++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 01673c895..574e1edf6 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -5,6 +5,7 @@ pub(crate) mod typed; pub use dap::*; use helix_vcs::Hunk; pub use lsp::*; +use tokio::sync::oneshot; use tui::widgets::Row; pub use typed::*; @@ -4171,6 +4172,24 @@ pub fn completion(cx: &mut Context) { None => return, }; + // setup a chanel that allows the request to be canceled + let (tx, rx) = oneshot::channel(); + // set completion_request so that this request can be canceled + // by setting completion_request, the old channel stored there is dropped + // and the associated request is automatically dropped + cx.editor.completion_request_handle = Some(tx); + let future = async move { + tokio::select! { + biased; + _ = rx => { + Ok(serde_json::Value::Null) + } + res = future => { + res + } + } + }; + let trigger_offset = cursor; // TODO: trigger_offset should be the cursor offset but we also need a starting offset from where we want to apply @@ -4183,11 +4202,19 @@ pub fn completion(cx: &mut Context) { let start_offset = cursor.saturating_sub(offset); let savepoint = doc.savepoint(view); + let trigger_doc = doc.id(); + let trigger_view = view.id; + cx.callback( future, move |editor, compositor, response: Option| { - if editor.mode != Mode::Insert { - // we're not in insert mode anymore + let (view, doc) = current_ref!(editor); + // check if the completion request is stale. + // + // Completions are completed asynchrounsly and therefore the user could + //switch document/view or leave insert mode. In all of thoise cases the + // completion should be discarded + if editor.mode != Mode::Insert || view.id != trigger_view || doc.id() != trigger_doc { return; } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index c81ae635a..859176fb4 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -820,6 +820,7 @@ impl EditorView { (Mode::Insert, Mode::Normal) => { // if exiting insert mode, remove completion self.completion = None; + cxt.editor.completion_request_handle = None; // TODO: Use an on_mode_change hook to remove signature help cxt.jobs.callback(async { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 5b819b333..c6541105a 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -31,7 +31,7 @@ use std::{ use tokio::{ sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - Notify, RwLock, + oneshot, Notify, RwLock, }, time::{sleep, Duration, Instant, Sleep}, }; @@ -852,6 +852,14 @@ pub struct Editor { /// avoid calculating the cursor position multiple /// times during rendering and should not be set by other functions. pub cursor_cache: Cell>>, + /// When a new completion request is sent to the server old + /// unifinished request must be dropped. Each completion + /// request is associated with a channel that cancels + /// when the channel is dropped. That channel is stored + /// here. When a new completion request is sent this + /// field is set and any old requests are automatically + /// canceled as a result + pub completion_request_handle: Option>, } pub type RedrawHandle = (Arc, Arc>); @@ -950,6 +958,7 @@ impl Editor { redraw_handle: Default::default(), needs_redraw: false, cursor_cache: Cell::new(None), + completion_request_handle: None, } } From aabc8af95dd1c093da4b67151f6a7026a85e9c0e Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Fri, 3 Mar 2023 00:34:05 +0100 Subject: [PATCH 0093/1169] correctly store snapshots when repeating insert-mode actions Repeating completions currently crates a savepoint when a completion popup was triggered (so after the request completed). Just like for normal completions the savepoint must be created at the request. The occurrence of the completion request was previously not saved in `last_insert`. To that end a new `InsertEvent::RequestCompletion` variant has been added. When replayed this event creates a snapshot that is "actived" by the `TriggerCompletion` event and subsequently used during any `InsertEvent::CompletiuonApply` events. --- helix-term/src/commands.rs | 19 ++++++++++++++++++- helix-term/src/ui/editor.rs | 18 +++++++++++++----- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 574e1edf6..6817bc5c7 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -53,7 +53,10 @@ use crate::{ filter_picker_entry, job::Callback, keymap::ReverseKeymap, - ui::{self, overlay::overlayed, FilePicker, Picker, Popup, Prompt, PromptEvent}, + ui::{ + self, editor::InsertEvent, overlay::overlayed, FilePicker, Picker, Popup, Prompt, + PromptEvent, + }, }; use crate::job::{self, Jobs}; @@ -4205,6 +4208,20 @@ pub fn completion(cx: &mut Context) { let trigger_doc = doc.id(); let trigger_view = view.id; + // FIXME: The commands Context can only have a single callback + // which means it gets overwritten when executing keybindings + // with multiple commands or macros. This would mean that completion + // might be incorrectly applied when repeating the insertmode action + // + // TODO: to solve this either make cx.callback a Vec of callbacks or + // alternatively move `last_insert` to `helix_view::Editor` + cx.callback = Some(Box::new( + move |compositor: &mut Compositor, _cx: &mut compositor::Context| { + let ui = compositor.find::().unwrap(); + ui.last_insert.1.push(InsertEvent::RequestCompletion); + }, + )); + cx.callback( future, move |editor, compositor, response: Option| { diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 859176fb4..4abbe01e7 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -21,14 +21,14 @@ use helix_core::{ visual_offset_from_block, Position, Range, Selection, Transaction, }; use helix_view::{ - document::{Mode, SCRATCH_BUFFER_NAME}, + document::{Mode, SavePoint, SCRATCH_BUFFER_NAME}, editor::{CompleteAction, CursorShapeConfig}, graphics::{Color, CursorKind, Modifier, Rect, Style}, input::{KeyEvent, MouseButton, MouseEvent, MouseEventKind}, keyboard::{KeyCode, KeyModifiers}, Document, Editor, Theme, View, }; -use std::{num::NonZeroUsize, path::PathBuf, rc::Rc}; +use std::{mem::take, num::NonZeroUsize, path::PathBuf, rc::Rc, sync::Arc}; use tui::buffer::Buffer as Surface; @@ -39,7 +39,7 @@ pub struct EditorView { pub keymaps: Keymaps, on_next_key: Option, pseudo_pending: Vec, - last_insert: (commands::MappableCommand, Vec), + pub(crate) last_insert: (commands::MappableCommand, Vec), pub(crate) completion: Option, spinners: ProgressSpinners, } @@ -49,6 +49,7 @@ pub enum InsertEvent { Key(KeyEvent), CompletionApply(CompleteAction), TriggerCompletion, + RequestCompletion, } impl Default for EditorView { @@ -891,6 +892,8 @@ impl EditorView { 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); + let mut last_savepoint = None; + let mut last_request_savepoint = None; // then replay the inputs for key in self.last_insert.1.clone() { match key { @@ -898,7 +901,9 @@ impl EditorView { InsertEvent::CompletionApply(compl) => { let (view, doc) = current!(cxt.editor); - doc.restore(view); + if let Some(last_savepoint) = last_savepoint.as_deref() { + doc.restore(view, last_savepoint); + } let text = doc.text().slice(..); let cursor = doc.selection(view.id).primary().cursor(text); @@ -915,8 +920,11 @@ impl EditorView { doc.apply(&tx, view.id); } InsertEvent::TriggerCompletion => { + last_savepoint = take(&mut last_request_savepoint); + } + InsertEvent::RequestCompletion => { let (view, doc) = current!(cxt.editor); - doc.savepoint(view); + last_request_savepoint = Some(doc.savepoint(view)); } } } From 2cf4ce235662fcb272c684751b844b2ebc1b757f Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Thu, 9 Mar 2023 05:08:28 +0100 Subject: [PATCH 0094/1169] Fix `shrink_selection` with multiple cursors. (#6093) * Fix #6092 Cause were some incorrect assumptions that missed an edge case in the `Selection.contains()` calculation. Tests were added accordingly. * Fix Selection.contains() edge-case handling. Removing the len check short-circuit was the only thing needed as pointed out by @dead10ck. --- helix-core/src/selection.rs | 11 ++++++----- helix-term/src/commands.rs | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 0db7634c9..0eb2b755e 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -625,11 +625,6 @@ impl Selection { // returns true if self ⊇ other pub fn contains(&self, other: &Selection) -> bool { - // can't contain other if it is larger - if other.len() > self.len() { - return false; - } - let (mut iter_self, mut iter_other) = (self.iter(), other.iter()); let (mut ele_self, mut ele_other) = (iter_self.next(), iter_other.next()); @@ -1240,5 +1235,11 @@ mod test { vec!((3, 4), (7, 9)) )); assert!(!contains(vec!((1, 1), (5, 6)), vec!((1, 6)))); + + // multiple ranges of other are all contained in some ranges of self, + assert!(contains( + vec!((1, 4), (7, 10)), + vec!((1, 2), (3, 4), (7, 9)) + )); } } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 6817bc5c7..803f4051d 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4371,7 +4371,6 @@ fn shrink_selection(cx: &mut Context) { // try to restore previous selection if let Some(prev_selection) = view.object_selections.pop() { if current_selection.contains(&prev_selection) { - // allow shrinking the selection only if current selection contains the previous object selection doc.set_selection(view.id, prev_selection); return; } else { From ce1fb9e64c189fb7476b4c72c6774a5bf6cbfd0f Mon Sep 17 00:00:00 2001 From: paul-scott Date: Fri, 10 Mar 2023 01:50:43 +1100 Subject: [PATCH 0095/1169] Generalised to multiple runtime directories with priorities (#5411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Generalised to multiple runtime directories with priorities This is an implementation for #3346. Previously, one of the following runtime directories were used: 1. `$HELIX_RUNTIME` 2. sibling directory to `$CARGO_MANIFEST_DIR` 3. subdirectory of user config directory 4. subdirectory of path to helix executable The first directory provided / found to exist in this order was used as a root for all runtime file searches (grammars, themes, queries). This change lowers the priority of `$HELIX_RUNTIME` so that the user config runtime has higher priority. More significantly, all of these directories are now searched for runtime files, enabling a user to override default or system-level runtime files. If the same file name appears in multiple runtime directories, the following priority is now used: 1. sibling directory to `$CARGO_MANIFEST_DIR` 2. subdirectory of user config directory 3. `$HELIX_RUNTIME` 4. subdirectory of path to helix executable One exception to this rule is that a user can have a `themes` directory directly in the user config directory that has higher piority to `themes` directories in runtime directories. That behaviour has been preserved. As part of implementing this feature `theme::Loader` was simplified and the cycle detection logic of the theme inheritance was improved to cover more cases and to be more explicit. * Removed AsRef usage to avoid binary growth * Health displaying ;-separated runtime dirs * Changed HELIX_RUNTIME build from src instructions * Updated doc for more detail on runtime directories * Improved health symlink printing and theme cycle errors The health display of runtime symlinks now prints both ends of the link. Separate errors are given when theme file is not found and when the only theme file found would form an inheritence cycle. * Satisfied clippy on passing Path * Clarified highest priority runtime directory purpose * Further clarified multiple runtime details in book Also gave markdown headings to subsections. Fixed a error with table indentation not building table that also appears present on master. --------- Co-authored-by: Paul Scott Co-authored-by: Blaž Hrastnik --- book/src/install.md | 38 +++++++++---- helix-loader/src/grammar.rs | 23 +++++--- helix-loader/src/lib.rs | 81 ++++++++++++++++++++++----- helix-term/src/application.rs | 10 ++-- helix-term/src/commands/typed.rs | 2 +- helix-term/src/health.rs | 38 +++++++++---- helix-term/src/ui/mod.rs | 8 +-- helix-view/src/theme.rs | 95 ++++++++++++++++++-------------- 8 files changed, 197 insertions(+), 98 deletions(-) diff --git a/book/src/install.md b/book/src/install.md index f9cf9a3ba..bd3f502b6 100644 --- a/book/src/install.md +++ b/book/src/install.md @@ -137,8 +137,8 @@ cargo install --path helix-term --locked ``` This command will create the `hx` executable and construct the tree-sitter -grammars either in the `runtime` folder, or in the folder specified in `HELIX_RUNTIME` -(as described below). To build the tree-sitter grammars requires a c++ compiler to be installed, for example `gcc-c++`. +grammars in the local `runtime` folder. To build the tree-sitter grammars requires +a c++ compiler to be installed, for example `gcc-c++`. > 💡 If you are using the musl-libc instead of glibc the following environment variable must be set during the build > to ensure tree-sitter grammars can be loaded correctly: @@ -149,11 +149,13 @@ grammars either in the `runtime` folder, or in the folder specified in `HELIX_RU > 💡 Tree-sitter grammars can be fetched and compiled if not pre-packaged. Fetch > grammars with `hx --grammar fetch` (requires `git`) and compile them with -> `hx --grammar build` (requires a C++ compiler). +> `hx --grammar build` (requires a C++ compiler). This will install them in +> the `runtime` directory within the user's helix config directory (more +> [details below](#multiple-runtime-directories)). ### Configuring Helix's runtime files -- **Linux and macOS** +#### Linux and macOS Either set the `HELIX_RUNTIME` environment variable to point to the runtime files and add it to your `~/.bashrc` or equivalent: @@ -167,7 +169,7 @@ Or, create a symlink in `~/.config/helix` that links to the source code director ln -s $PWD/runtime ~/.config/helix/runtime ``` -- **Windows** +#### Windows Either set the `HELIX_RUNTIME` environment variable to point to the runtime files using the Windows setting (search for `Edit environment variables for your account`) or use the `setx` command in @@ -182,13 +184,27 @@ setx HELIX_RUNTIME "%userprofile%\source\repos\helix\runtime" Or, create a symlink in `%appdata%\helix\` that links to the source code directory: - | Method | Command | - | ---------- | -------------------------------------------------------------------------------------- | - | PowerShell | `New-Item -ItemType Junction -Target "runtime" -Path "$Env:AppData\helix\runtime"` | - | Cmd | `cd %appdata%\helix`
`mklink /D runtime "%userprofile%\src\helix\runtime"` | +| Method | Command | +| ---------- | -------------------------------------------------------------------------------------- | +| PowerShell | `New-Item -ItemType Junction -Target "runtime" -Path "$Env:AppData\helix\runtime"` | +| Cmd | `cd %appdata%\helix`
`mklink /D runtime "%userprofile%\src\helix\runtime"` | - > 💡 On Windows, creating a symbolic link may require running PowerShell or - > Cmd as an administrator. +> 💡 On Windows, creating a symbolic link may require running PowerShell or +> Cmd as an administrator. + +#### Multiple runtime directories + +When Helix finds multiple runtime directories it will search through them for files in the +following order: + +1. `runtime/` sibling directory to `$CARGO_MANIFEST_DIR` directory (this is intended for + developing and testing helix only). +2. `runtime/` subdirectory of OS-dependent helix user config directory. +3. `$HELIX_RUNTIME`. +4. `runtime/` subdirectory of path to Helix executable. + +This order also sets the priority for selecting which file will be used if multiple runtime +directories have files with the same name. ### Validating the installation diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs index 01c966c8c..a85cb274c 100644 --- a/helix-loader/src/grammar.rs +++ b/helix-loader/src/grammar.rs @@ -67,8 +67,9 @@ pub fn get_language(name: &str) -> Result { #[cfg(not(target_arch = "wasm32"))] pub fn get_language(name: &str) -> Result { use libloading::{Library, Symbol}; - let mut library_path = crate::runtime_dir().join("grammars").join(name); - library_path.set_extension(DYLIB_EXTENSION); + let mut rel_library_path = PathBuf::new().join("grammars").join(name); + rel_library_path.set_extension(DYLIB_EXTENSION); + let library_path = crate::runtime_file(&rel_library_path); let library = unsafe { Library::new(&library_path) } .with_context(|| format!("Error opening dynamic library {:?}", library_path))?; @@ -252,7 +253,9 @@ fn fetch_grammar(grammar: GrammarConfiguration) -> Result { remote, revision, .. } = grammar.source { - let grammar_dir = crate::runtime_dir() + let grammar_dir = crate::runtime_dirs() + .first() + .expect("No runtime directories provided") // guaranteed by post-condition .join("grammars") .join("sources") .join(&grammar.grammar_id); @@ -350,7 +353,9 @@ fn build_grammar(grammar: GrammarConfiguration, target: Option<&str>) -> Result< let grammar_dir = if let GrammarSource::Local { path } = &grammar.source { PathBuf::from(&path) } else { - crate::runtime_dir() + crate::runtime_dirs() + .first() + .expect("No runtime directories provided") // guaranteed by post-condition .join("grammars") .join("sources") .join(&grammar.grammar_id) @@ -401,7 +406,10 @@ fn build_tree_sitter_library( None } }; - let parser_lib_path = crate::runtime_dir().join("grammars"); + let parser_lib_path = crate::runtime_dirs() + .first() + .expect("No runtime directories provided") // guaranteed by post-condition + .join("grammars"); let mut library_path = parser_lib_path.join(&grammar.grammar_id); library_path.set_extension(DYLIB_EXTENSION); @@ -511,9 +519,6 @@ fn mtime(path: &Path) -> Result { /// Gives the contents of a file from a language's `runtime/queries/` /// directory pub fn load_runtime_file(language: &str, filename: &str) -> Result { - let path = crate::RUNTIME_DIR - .join("queries") - .join(language) - .join(filename); + let path = crate::runtime_file(&PathBuf::new().join("queries").join(language).join(filename)); std::fs::read_to_string(path) } diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index 8dc2928ad..04b44b5aa 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -2,11 +2,12 @@ pub mod config; pub mod grammar; use etcetera::base_strategy::{choose_base_strategy, BaseStrategy}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub const VERSION_AND_GIT_HASH: &str = env!("VERSION_AND_GIT_HASH"); -pub static RUNTIME_DIR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(runtime_dir); +static RUNTIME_DIRS: once_cell::sync::Lazy> = + once_cell::sync::Lazy::new(prioritize_runtime_dirs); static CONFIG_FILE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); @@ -25,31 +26,83 @@ pub fn initialize_config_file(specified_file: Option) { CONFIG_FILE.set(config_file).ok(); } -pub fn runtime_dir() -> PathBuf { - if let Ok(dir) = std::env::var("HELIX_RUNTIME") { - return dir.into(); - } - +/// A list of runtime directories from highest to lowest priority +/// +/// The priority is: +/// +/// 1. sibling directory to `CARGO_MANIFEST_DIR` (if environment variable is set) +/// 2. subdirectory of user config directory (always included) +/// 3. `HELIX_RUNTIME` (if environment variable is set) +/// 4. subdirectory of path to helix executable (always included) +/// +/// Postcondition: returns at least two paths (they might not exist). +fn prioritize_runtime_dirs() -> Vec { + const RT_DIR: &str = "runtime"; + // Adding higher priority first + let mut rt_dirs = Vec::new(); if let Ok(dir) = std::env::var("CARGO_MANIFEST_DIR") { // this is the directory of the crate being run by cargo, we need the workspace path so we take the parent let path = std::path::PathBuf::from(dir).parent().unwrap().join(RT_DIR); log::debug!("runtime dir: {}", path.to_string_lossy()); - return path; + rt_dirs.push(path); } - const RT_DIR: &str = "runtime"; - let conf_dir = config_dir().join(RT_DIR); - if conf_dir.exists() { - return conf_dir; + let conf_rt_dir = config_dir().join(RT_DIR); + rt_dirs.push(conf_rt_dir); + + if let Ok(dir) = std::env::var("HELIX_RUNTIME") { + rt_dirs.push(dir.into()); } // fallback to location of the executable being run // canonicalize the path in case the executable is symlinked - std::env::current_exe() + let exe_rt_dir = std::env::current_exe() .ok() .and_then(|path| std::fs::canonicalize(path).ok()) .and_then(|path| path.parent().map(|path| path.to_path_buf().join(RT_DIR))) - .unwrap() + .unwrap(); + rt_dirs.push(exe_rt_dir); + rt_dirs +} + +/// Runtime directories ordered from highest to lowest priority +/// +/// All directories should be checked when looking for files. +/// +/// Postcondition: returns at least one path (it might not exist). +pub fn runtime_dirs() -> &'static [PathBuf] { + &RUNTIME_DIRS +} + +/// Find file with path relative to runtime directory +/// +/// `rel_path` should be the relative path from within the `runtime/` directory. +/// The valid runtime directories are searched in priority order and the first +/// file found to exist is returned, otherwise None. +fn find_runtime_file(rel_path: &Path) -> Option { + RUNTIME_DIRS.iter().find_map(|rt_dir| { + let path = rt_dir.join(rel_path); + if path.exists() { + Some(path) + } else { + None + } + }) +} + +/// Find file with path relative to runtime directory +/// +/// `rel_path` should be the relative path from within the `runtime/` directory. +/// The valid runtime directories are searched in priority order and the first +/// file found to exist is returned, otherwise the path to the final attempt +/// that failed. +pub fn runtime_file(rel_path: &Path) -> PathBuf { + find_runtime_file(rel_path).unwrap_or_else(|| { + RUNTIME_DIRS + .last() + .map(|dir| dir.join(rel_path)) + .unwrap_or_default() + }) } pub fn config_dir() -> PathBuf { diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index d56e7c884..c7e939959 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -31,6 +31,7 @@ use crate::{ use log::{debug, error, warn}; use std::{ io::{stdin, stdout}, + path::Path, sync::Arc, time::{Duration, Instant}, }; @@ -113,10 +114,9 @@ impl Application { use helix_view::editor::Action; - let theme_loader = std::sync::Arc::new(theme::Loader::new( - &helix_loader::config_dir(), - &helix_loader::runtime_dir(), - )); + let mut theme_parent_dirs = vec![helix_loader::config_dir()]; + theme_parent_dirs.extend(helix_loader::runtime_dirs().iter().cloned()); + let theme_loader = std::sync::Arc::new(theme::Loader::new(&theme_parent_dirs)); let true_color = config.editor.true_color || crate::true_color(); let theme = config @@ -162,7 +162,7 @@ impl Application { compositor.push(editor_view); if args.load_tutor { - let path = helix_loader::runtime_dir().join("tutor"); + let path = helix_loader::runtime_file(Path::new("tutor")); editor.open(&path, Action::VerticalSplit)?; // Unset path to prevent accidentally saving to the original tutor file. doc_mut!(editor).set_path(None)?; diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 5ea611086..e9a722258 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1565,7 +1565,7 @@ fn tutor( return Ok(()); } - let path = helix_loader::runtime_dir().join("tutor"); + let path = helix_loader::runtime_file(Path::new("tutor")); cx.editor.open(&path, Action::Replace)?; // Unset path to prevent accidentally saving to the original tutor file. doc_mut!(cx.editor).set_path(None)?; diff --git a/helix-term/src/health.rs b/helix-term/src/health.rs index 6558fe19f..480c2c675 100644 --- a/helix-term/src/health.rs +++ b/helix-term/src/health.rs @@ -52,7 +52,7 @@ pub fn general() -> std::io::Result<()> { let config_file = helix_loader::config_file(); let lang_file = helix_loader::lang_config_file(); let log_file = helix_loader::log_file(); - let rt_dir = helix_loader::runtime_dir(); + let rt_dirs = helix_loader::runtime_dirs(); let clipboard_provider = get_clipboard_provider(); if config_file.exists() { @@ -66,17 +66,31 @@ pub fn general() -> std::io::Result<()> { writeln!(stdout, "Language file: default")?; } writeln!(stdout, "Log file: {}", log_file.display())?; - writeln!(stdout, "Runtime directory: {}", rt_dir.display())?; - - if let Ok(path) = std::fs::read_link(&rt_dir) { - let msg = format!("Runtime directory is symlinked to {}", path.display()); - writeln!(stdout, "{}", msg.yellow())?; - } - if !rt_dir.exists() { - writeln!(stdout, "{}", "Runtime directory does not exist.".red())?; - } - if rt_dir.read_dir().ok().map(|it| it.count()) == Some(0) { - writeln!(stdout, "{}", "Runtime directory is empty.".red())?; + writeln!( + stdout, + "Runtime directories: {}", + rt_dirs + .iter() + .map(|d| d.to_string_lossy()) + .collect::>() + .join(";") + )?; + for rt_dir in rt_dirs.iter() { + if let Ok(path) = std::fs::read_link(rt_dir) { + let msg = format!( + "Runtime directory {} is symlinked to: {}", + rt_dir.display(), + path.display() + ); + writeln!(stdout, "{}", msg.yellow())?; + } + if !rt_dir.exists() { + let msg = format!("Runtime directory does not exist: {}", rt_dir.display()); + writeln!(stdout, "{}", msg.yellow())?; + } else if rt_dir.read_dir().ok().map(|it| it.count()) == Some(0) { + let msg = format!("Runtime directory is empty: {}", rt_dir.display()); + writeln!(stdout, "{}", msg.yellow())?; + } } writeln!(stdout, "Clipboard provider: {}", clipboard_provider.name())?; diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index d7717f8cf..3e9a14b06 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -280,10 +280,10 @@ pub mod completers { } pub fn theme(_editor: &Editor, input: &str) -> Vec { - let mut names = theme::Loader::read_names(&helix_loader::runtime_dir().join("themes")); - names.extend(theme::Loader::read_names( - &helix_loader::config_dir().join("themes"), - )); + let mut names = theme::Loader::read_names(&helix_loader::config_dir().join("themes")); + for rt_dir in helix_loader::runtime_dirs() { + names.extend(theme::Loader::read_names(&rt_dir.join("themes"))); + } names.push("default".into()); names.push("base16_default".into()); names.sort(); diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs index ce061babe..5d79ff26b 100644 --- a/helix-view/src/theme.rs +++ b/helix-view/src/theme.rs @@ -1,5 +1,5 @@ use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, path::{Path, PathBuf}, str, }; @@ -37,19 +37,21 @@ pub static BASE16_DEFAULT_THEME: Lazy = Lazy::new(|| Theme { #[derive(Clone, Debug)] pub struct Loader { - user_dir: PathBuf, - default_dir: PathBuf, + /// Theme directories to search from highest to lowest priority + theme_dirs: Vec, } impl Loader { - /// Creates a new loader that can load themes from two directories. - pub fn new>(user_dir: P, default_dir: P) -> Self { + /// Creates a new loader that can load themes from multiple directories. + /// + /// The provided directories should be ordered from highest to lowest priority. + /// The directories will have their "themes" subdirectory searched. + pub fn new(dirs: &[PathBuf]) -> Self { Self { - user_dir: user_dir.as_ref().join("themes"), - default_dir: default_dir.as_ref().join("themes"), + theme_dirs: dirs.iter().map(|p| p.join("themes")).collect(), } } - /// Loads a theme first looking in the `user_dir` then in `default_dir` + /// Loads a theme searching directories in priority order. pub fn load(&self, name: &str) -> Result { if name == "default" { return Ok(self.default()); @@ -58,7 +60,8 @@ impl Loader { return Ok(self.base16_default()); } - let theme = self.load_theme(name, name, false).map(Theme::from)?; + let mut visited_paths = HashSet::new(); + let theme = self.load_theme(name, &mut visited_paths).map(Theme::from)?; Ok(Theme { name: name.into(), @@ -66,16 +69,18 @@ impl Loader { }) } - // load the theme and its parent recursively and merge them - // `base_theme_name` is the theme from the config.toml, - // used to prevent some circular loading scenarios - fn load_theme( - &self, - name: &str, - base_theme_name: &str, - only_default_dir: bool, - ) -> Result { - let path = self.path(name, only_default_dir); + /// Recursively load a theme, merging with any inherited parent themes. + /// + /// The paths that have been visited in the inheritance hierarchy are tracked + /// to detect and avoid cycling. + /// + /// It is possible for one file to inherit from another file with the same name + /// so long as the second file is in a themes directory with lower priority. + /// However, it is not recommended that users do this as it will make tracing + /// errors more difficult. + fn load_theme(&self, name: &str, visited_paths: &mut HashSet) -> Result { + let path = self.path(name, visited_paths)?; + let theme_toml = self.load_toml(path)?; let inherits = theme_toml.get("inherits"); @@ -92,11 +97,7 @@ impl Loader { // load default themes's toml from const. "default" => DEFAULT_THEME_DATA.clone(), "base16_default" => BASE16_DEFAULT_THEME_DATA.clone(), - _ => self.load_theme( - parent_theme_name, - base_theme_name, - base_theme_name == parent_theme_name, - )?, + _ => self.load_theme(parent_theme_name, visited_paths)?, }; self.merge_themes(parent_theme_toml, theme_toml) @@ -148,7 +149,7 @@ impl Loader { merge_toml_values(theme, palette.into(), 1) } - // Loads the theme data as `toml::Value` first from the user_dir then in default_dir + // Loads the theme data as `toml::Value` fn load_toml(&self, path: PathBuf) -> Result { let data = std::fs::read_to_string(path)?; let value = toml::from_str(&data)?; @@ -156,25 +157,35 @@ impl Loader { Ok(value) } - // Returns the path to the theme with the name - // With `only_default_dir` as false the path will first search for the user path - // disabled it ignores the user path and returns only the default path - fn path(&self, name: &str, only_default_dir: bool) -> PathBuf { + /// Returns the path to the theme with the given name + /// + /// Ignores paths already visited and follows directory priority order. + fn path(&self, name: &str, visited_paths: &mut HashSet) -> Result { let filename = format!("{}.toml", name); - let user_path = self.user_dir.join(&filename); - if !only_default_dir && user_path.exists() { - user_path - } else { - self.default_dir.join(filename) - } - } - - /// Lists all theme names available in default and user directory - pub fn names(&self) -> Vec { - let mut names = Self::read_names(&self.user_dir); - names.extend(Self::read_names(&self.default_dir)); - names + let mut cycle_found = false; // track if there was a path, but it was in a cycle + self.theme_dirs + .iter() + .find_map(|dir| { + let path = dir.join(&filename); + if !path.exists() { + None + } else if visited_paths.contains(&path) { + // Avoiding cycle, continuing to look in lower priority directories + cycle_found = true; + None + } else { + visited_paths.insert(path.clone()); + Some(path) + } + }) + .ok_or_else(|| { + if cycle_found { + anyhow!("Theme: cycle found in inheriting: {}", name) + } else { + anyhow!("Theme: file not found for: {}", name) + } + }) } pub fn default_theme(&self, true_color: bool) -> Theme { From 9b4326b18b24fa8ec8ffd0cb261a8c82ecad32d6 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Tue, 10 Jan 2023 12:21:44 -0700 Subject: [PATCH 0096/1169] allow LSP insert text to replace non-matching prefixes (#5469) Most LSPs will complete case-insensitive matches, particularly from lowercase to uppercase. In some cases, notably Pyright, this is given as a simple insert text instead of TextEdit. When this happens, the prefix text was left unedited. --- helix-term/src/ui/completion.rs | 42 +++++++++++++++++---------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index ef88938fe..336b75cb4 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -124,6 +124,8 @@ impl Completion { ) -> Transaction { use helix_lsp::snippet; let selection = doc.selection(view_id); + let text = doc.text().slice(..); + let primary_cursor = selection.primary().cursor(text); let (start_offset, end_offset, new_text) = if let Some(edit) = &item.text_edit { let edit = match edit { @@ -133,8 +135,6 @@ impl Completion { lsp::TextEdit::new(item.replace, item.new_text.clone()) } }; - let text = doc.text().slice(..); - let primary_cursor = selection.primary().cursor(text); let start_offset = match util::lsp_pos_to_pos(doc.text(), edit.range.start, offset_encoding) { @@ -149,24 +149,26 @@ impl Completion { (start_offset, end_offset, edit.new_text) } else { - let new_text = item.insert_text.as_ref().unwrap_or(&item.label); - // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯ - // in these cases we need to check for a common prefix and remove it - let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset)); - let new_text = new_text.trim_start_matches::<&str>(&prefix); - - // TODO: this needs to be true for the numbers to work out correctly - // in the closure below. It's passed in to a callback as this same - // formula, but can the value change between the LSP request and - // response? If it does, can we recover? - debug_assert!( - doc.selection(view_id) - .primary() - .cursor(doc.text().slice(..)) - == trigger_offset - ); - - (0, 0, new_text.into()) + let new_text = item + .insert_text + .clone() + .unwrap_or_else(|| item.label.clone()); + + // check that we are still at the correct savepoint + // we can still generate a transaction regardless but if the + // document changed (and not just the selection) then we will + // likely delete the wrong text (same if we applied an edit sent by the LS) + debug_assert!(primary_cursor == trigger_offset); + + // TODO: Respect editor.completion_replace? + // Would require detecting the end of the word boundary for every cursor individually. + // We don't do the same for normal `edits, to be consistent we would have to do it for those too + + ( + start_offset as i128 - primary_cursor as i128, + trigger_offset as i128 - primary_cursor as i128, + new_text, + ) }; if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET)) From e91289fda1c81bc3f3cc32735a1b6841e6df55a5 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 9 Mar 2023 22:09:12 +0100 Subject: [PATCH 0097/1169] Add IntoIterator implementation for Selection --- helix-core/src/selection.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 0eb2b755e..8e93c633e 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -661,6 +661,15 @@ impl<'a> IntoIterator for &'a Selection { } } +impl IntoIterator for Selection { + type Item = Range; + type IntoIter = smallvec::IntoIter<[Range; 1]>; + + fn into_iter(self) -> smallvec::IntoIter<[Range; 1]> { + self.ranges.into_iter() + } +} + // TODO: checkSelection -> check if valid for doc length && sorted pub fn keep_or_remove_matches( From cdec933523560f71c665469adc409d7ac0e06171 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 9 Mar 2023 22:17:12 +0100 Subject: [PATCH 0098/1169] avoid allocations during snippet rendering --- helix-lsp/src/snippet.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/helix-lsp/src/snippet.rs b/helix-lsp/src/snippet.rs index b27077e70..4713ad8bb 100644 --- a/helix-lsp/src/snippet.rs +++ b/helix-lsp/src/snippet.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use anyhow::{anyhow, Result}; -use helix_core::{smallvec, SmallVec}; +use helix_core::{smallvec, SmallVec, Tendril}; #[derive(Debug, PartialEq, Eq)] pub enum CaseChange { @@ -57,10 +57,10 @@ pub fn parse(s: &str) -> Result> { fn render_elements( snippet_elements: &[SnippetElement<'_>], - insert: &mut String, + insert: &mut Tendril, offset: &mut usize, tabstops: &mut Vec<(usize, (usize, usize))>, - newline_with_offset: &String, + newline_with_offset: &str, include_placeholer: bool, ) { use SnippetElement::*; @@ -121,10 +121,10 @@ fn render_elements( #[allow(clippy::type_complexity)] // only used one time pub fn render( snippet: &Snippet<'_>, - newline_with_offset: String, + newline_with_offset: &str, include_placeholer: bool, -) -> (String, Vec>) { - let mut insert = String::new(); +) -> (Tendril, Vec>) { + let mut insert = Tendril::new(); let mut tabstops = Vec::new(); let mut offset = 0; @@ -133,7 +133,7 @@ pub fn render( &mut insert, &mut offset, &mut tabstops, - &newline_with_offset, + newline_with_offset, include_placeholer, ); From 2b64a64d7ea43e22ad82f97f2c118891b74c3199 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 9 Mar 2023 22:19:14 +0100 Subject: [PATCH 0099/1169] Add API to create a Transaction from potentially overlapping changes This commit adds new functions to `Transaction` that allow creating edits that might potentially overlap. Any change that overlaps previous changes is ignored. Furthermore, a utility method is added that also drops selections associated with dropped changes (for transactions that are created from a selection). This is needed to avoid crashes when applying multicursor autocompletions, as the edit from a previous cursor may overlap with the next cursor/edit. --- helix-core/src/transaction.rs | 67 +++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index d2f4de07d..d8e581aae 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -1,3 +1,5 @@ +use smallvec::SmallVec; + use crate::{Range, Rope, Selection, Tendril}; use std::borrow::Cow; @@ -466,6 +468,33 @@ impl Transaction { self } + /// Generate a transaction from a set of potentially overlapping changes. The `change_ranges` + /// iterator yield the range (of removed text) in the old document for each edit. If any change + /// overlaps with a range overlaps with a previous range then that range is ignored. + /// + /// The `process_change` callback is called for each edit that is not ignored (in the order + /// yielded by `changes`) and should return the new text that the associated range will be + /// replaced with. + /// + /// To make this function more flexible the iterator can yield additional data for each change + /// that is passed to `process_change` + pub fn change_ignore_overlapping( + doc: &Rope, + change_ranges: impl Iterator, + mut process_change: impl FnMut(usize, usize, T) -> Option, + ) -> Self { + let mut last = 0; + let changes = change_ranges.filter_map(|(from, to, data)| { + if from < last { + return None; + } + let tendril = process_change(from, to, data); + last = to; + Some((from, to, tendril)) + }); + Self::change(doc, changes) + } + /// Generate a transaction from a set of changes. pub fn change(doc: &Rope, changes: I) -> Self where @@ -513,6 +542,44 @@ impl Transaction { Self::change(doc, selection.iter().map(f)) } + pub fn change_by_selection_ignore_overlapping( + doc: &Rope, + selection: &Selection, + mut change_range: impl FnMut(&Range) -> (usize, usize), + mut create_tendril: impl FnMut(usize, usize) -> Option, + ) -> (Transaction, Selection) { + let mut last_selection_idx = None; + let mut new_primary_idx = None; + let mut ranges: SmallVec<[Range; 1]> = SmallVec::new(); + let process_change = |change_start, change_end, (idx, range): (usize, &Range)| { + // update the primary idx + if idx == selection.primary_index() { + new_primary_idx = Some(idx); + } else if new_primary_idx.is_none() { + if idx > selection.primary_index() { + new_primary_idx = last_selection_idx; + } else { + last_selection_idx = Some(idx); + } + } + ranges.push(*range); + create_tendril(change_start, change_end) + }; + let transaction = Self::change_ignore_overlapping( + doc, + selection.iter().enumerate().map(|range| { + let (change_start, change_end) = change_range(range.1); + (change_start, change_end, range) + }), + process_change, + ); + + ( + transaction, + Selection::new(ranges, new_primary_idx.unwrap_or(0)), + ) + } + /// Insert text at each selection head. pub fn insert(doc: &Rope, selection: &Selection, text: Tendril) -> Self { Self::change_by_selection(doc, selection, |range| { From b1f75280909884c9621b553b43030ac39cfa47ce Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 9 Mar 2023 23:08:55 +0100 Subject: [PATCH 0100/1169] fix snippet bugs and multicursor completion edgecases Multicursor completions may overlap and therefore overlapping completions must be dropped to avoid crashes. Furthermore, multicursor edits might simply be out of range if the word before/after the cursor is shorter. This currently leads to crashes, instead these selections are now also removed for completions. This commit also significantly refactors snippet transaction generation so that tabstops behave correctly with the above rules. Furthermore, snippet tabstops need to be carefully mapped to ensure their position is correct and consistent with our selection semantics. Finally, we now keep a partially updated Rope while creating snippet transactions so that we can fill information into snippets that depends on the position in the document. --- helix-lsp/src/lib.rs | 241 +++++++++++++++++++++++--------- helix-term/src/ui/completion.rs | 18 +-- 2 files changed, 180 insertions(+), 79 deletions(-) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 147b381c2..58e8d83dc 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -59,8 +59,8 @@ pub enum OffsetEncoding { pub mod util { use super::*; use helix_core::line_ending::{line_end_byte_index, line_end_char_index}; + use helix_core::{chars, RopeSlice, SmallVec}; use helix_core::{diagnostic::NumberOrString, Range, Rope, Selection, Tendril, Transaction}; - use helix_core::{smallvec, SmallVec}; /// Converts a diagnostic in the document to [`lsp::Diagnostic`]. /// @@ -247,13 +247,46 @@ pub mod util { Some(Range::new(start, end)) } + /// If the LS did not provide a range for the completion or the range of the + /// primary cursor can not be used for the secondary cursor, this function + /// can be used to find the completion range for a cursor + fn find_completion_range(text: RopeSlice, cursor: usize) -> (usize, usize) { + let start = cursor + - text + .chars_at(cursor) + .reversed() + .take_while(|ch| chars::char_is_word(*ch)) + .count(); + (start, cursor) + } + fn completion_range( + text: RopeSlice, + edit_offset: Option<(i128, i128)>, + cursor: usize, + ) -> Option<(usize, usize)> { + let res = match edit_offset { + Some((start_offset, end_offset)) => { + let start_offset = cursor as i128 + start_offset; + if start_offset < 0 { + return None; + } + let end_offset = cursor as i128 + end_offset; + if end_offset > text.len_chars() as i128 { + return None; + } + (start_offset as usize, end_offset as usize) + } + None => find_completion_range(text, cursor), + }; + Some(res) + } + /// Creates a [Transaction] from the [lsp::TextEdit] in a completion response. /// The transaction applies the edit to all cursors. pub fn generate_transaction_from_completion_edit( doc: &Rope, selection: &Selection, - start_offset: i128, - end_offset: i128, + edit_offset: Option<(i128, i128)>, new_text: String, ) -> Transaction { let replacement: Option = if new_text.is_empty() { @@ -263,83 +296,163 @@ pub mod util { }; let text = doc.slice(..); + let (removed_start, removed_end) = + completion_range(text, edit_offset, selection.primary().cursor(text)) + .expect("transaction must be valid for primary selection"); + let removed_text = text.slice(removed_start..removed_end); - Transaction::change_by_selection(doc, selection, |range| { - let cursor = range.cursor(text); - ( - (cursor as i128 + start_offset) as usize, - (cursor as i128 + end_offset) as usize, - replacement.clone(), - ) - }) + let (transaction, mut selection) = Transaction::change_by_selection_ignore_overlapping( + doc, + selection, + |range| { + let cursor = range.cursor(text); + completion_range(text, edit_offset, cursor) + .filter(|(start, end)| text.slice(start..end) == removed_text) + .unwrap_or_else(|| find_completion_range(text, cursor)) + }, + |_, _| replacement.clone(), + ); + if transaction.changes().is_empty() { + return transaction; + } + selection = selection.map(transaction.changes()); + transaction.with_selection(selection) } /// Creates a [Transaction] from the [snippet::Snippet] in a completion response. /// The transaction applies the edit to all cursors. + #[allow(clippy::too_many_arguments)] pub fn generate_transaction_from_snippet( doc: &Rope, selection: &Selection, - start_offset: i128, - end_offset: i128, + edit_offset: Option<(i128, i128)>, snippet: snippet::Snippet, line_ending: &str, include_placeholder: bool, + tab_width: usize, ) -> Transaction { let text = doc.slice(..); - // For each cursor store offsets for the first tabstop - let mut cursor_tabstop_offsets = Vec::>::new(); - let transaction = Transaction::change_by_selection(doc, selection, |range| { - let cursor = range.cursor(text); - let replacement_start = (cursor as i128 + start_offset) as usize; - let replacement_end = (cursor as i128 + end_offset) as usize; - let newline_with_offset = format!( - "{line_ending}{blank:width$}", - line_ending = line_ending, - width = replacement_start - doc.line_to_char(doc.char_to_line(replacement_start)), - blank = "" - ); - - let (replacement, tabstops) = - snippet::render(&snippet, newline_with_offset, include_placeholder); - - let replacement_len = replacement.chars().count(); - cursor_tabstop_offsets.push( - tabstops - .first() - .unwrap_or(&smallvec![(replacement_len, replacement_len)]) - .iter() - .map(|(from, to)| -> (i128, i128) { - ( - *from as i128 - replacement_len as i128, - *to as i128 - replacement_len as i128, - ) - }) - .collect(), - ); - - (replacement_start, replacement_end, Some(replacement.into())) - }); + let mut off = 0i128; + let mut mapped_doc = doc.clone(); + let mut selection_tabstops: SmallVec<[_; 1]> = SmallVec::new(); + let (removed_start, removed_end) = + completion_range(text, edit_offset, selection.primary().cursor(text)) + .expect("transaction must be valid for primary selection"); + let removed_text = text.slice(removed_start..removed_end); - // Create new selection based on the cursor tabstop from above - let mut cursor_tabstop_offsets_iter = cursor_tabstop_offsets.iter(); - let selection = selection - .clone() - .map(transaction.changes()) - .transform_iter(|range| { - cursor_tabstop_offsets_iter - .next() - .unwrap() - .iter() - .map(move |(from, to)| { - Range::new( - (range.anchor as i128 + *from) as usize, - (range.anchor as i128 + *to) as usize, - ) - }) - }); + let (transaction, selection) = Transaction::change_by_selection_ignore_overlapping( + doc, + selection, + |range| { + let cursor = range.cursor(text); + completion_range(text, edit_offset, cursor) + .filter(|(start, end)| text.slice(start..end) == removed_text) + .unwrap_or_else(|| find_completion_range(text, cursor)) + }, + |replacement_start, replacement_end| { + let mapped_replacement_start = (replacement_start as i128 + off) as usize; + let mapped_replacement_end = (replacement_end as i128 + off) as usize; + + let line_idx = mapped_doc.char_to_line(mapped_replacement_start); + let pos_on_line = mapped_replacement_start - mapped_doc.line_to_char(line_idx); + + // we only care about the actual offset here (not virtual text/softwrap) + // so it's ok to use the deprecated function here + #[allow(deprecated)] + let width = helix_core::visual_coords_at_pos( + mapped_doc.line(line_idx), + pos_on_line, + tab_width, + ) + .col; + let newline_with_offset = format!( + "{line_ending}{blank:width$}", + line_ending = line_ending, + blank = "" + ); + + let (replacement, tabstops) = + snippet::render(&snippet, &newline_with_offset, include_placeholder); + selection_tabstops.push((mapped_replacement_start, tabstops)); + mapped_doc.remove(mapped_replacement_start..mapped_replacement_end); + mapped_doc.insert(mapped_replacement_start, &replacement); + off += + replacement_start as i128 - replacement_end as i128 + replacement.len() as i128; + + Some(replacement) + }, + ); - transaction.with_selection(selection) + let changes = transaction.changes(); + if changes.is_empty() { + return transaction; + } + + let mut mapped_selection = SmallVec::with_capacity(selection.len()); + let mut mapped_primary_idx = 0; + let primary_range = selection.primary(); + for (range, (tabstop_anchor, tabstops)) in selection.into_iter().zip(selection_tabstops) { + if range == primary_range { + mapped_primary_idx = mapped_selection.len() + } + + let range = range.map(changes); + let tabstops = tabstops.first().filter(|tabstops| !tabstops.is_empty()); + let Some(tabstops) = tabstops else{ + // no tabstop normal mapping + mapped_selection.push(range); + continue; + }; + + // expand the selection to cover the tabstop to retain the helix selection semantic + // the tabstop closest to the range simply replaces `head` while anchor remains in place + // the remaining tabstops receive their own single-width cursor + if range.head < range.anchor { + let first_tabstop = tabstop_anchor + tabstops[0].1; + + // if selection is forward but was moved to the right it is + // contained entirely in the replacement text, just do a point + // selection (fallback below) + if range.anchor >= first_tabstop { + let range = Range::new(range.anchor, first_tabstop); + mapped_selection.push(range); + let rem_tabstops = tabstops[1..] + .iter() + .map(|tabstop| Range::point(tabstop_anchor + tabstop.1)); + mapped_selection.extend(rem_tabstops); + continue; + } + } else { + let last_idx = tabstops.len() - 1; + let last_tabstop = tabstop_anchor + tabstops[last_idx].1; + + // if selection is forward but was moved to the right it is + // contained entirely in the replacement text, just do a point + // selection (fallback below) + if range.anchor <= last_tabstop { + // we can't properly compute the the next grapheme + // here because the transaction hasn't been applied yet + // that is not a problem because the range gets grapheme aligned anyway + // tough so just adding one will always cause head to be grapheme + // aligned correctly when applied to the document + let range = Range::new(range.anchor, last_tabstop + 1); + mapped_selection.push(range); + let rem_tabstops = tabstops[..last_idx] + .iter() + .map(|tabstop| Range::point(tabstop_anchor + tabstop.0)); + mapped_selection.extend(rem_tabstops); + continue; + } + }; + + let tabstops = tabstops + .iter() + .map(|tabstop| Range::point(tabstop_anchor + tabstop.0)); + mapped_selection.extend(tabstops); + } + + transaction.with_selection(Selection::new(mapped_selection, mapped_primary_idx)) } pub fn generate_transaction_from_edits( diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 336b75cb4..99c337811 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -118,7 +118,6 @@ impl Completion { view_id: ViewId, item: &CompletionItem, offset_encoding: helix_lsp::OffsetEncoding, - start_offset: usize, trigger_offset: usize, include_placeholder: bool, ) -> Transaction { @@ -147,28 +146,18 @@ impl Completion { None => return Transaction::new(doc.text()), }; - (start_offset, end_offset, edit.new_text) + (Some((start_offset, end_offset)), edit.new_text) } else { let new_text = item .insert_text .clone() .unwrap_or_else(|| item.label.clone()); - // check that we are still at the correct savepoint // we can still generate a transaction regardless but if the // document changed (and not just the selection) then we will // likely delete the wrong text (same if we applied an edit sent by the LS) debug_assert!(primary_cursor == trigger_offset); - - // TODO: Respect editor.completion_replace? - // Would require detecting the end of the word boundary for every cursor individually. - // We don't do the same for normal `edits, to be consistent we would have to do it for those too - - ( - start_offset as i128 - primary_cursor as i128, - trigger_offset as i128 - primary_cursor as i128, - new_text, - ) + (None, Some(0), new_text) }; if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET)) @@ -186,6 +175,7 @@ impl Completion { snippet, doc.line_ending.as_str(), include_placeholder, + doc.tab_width(), ), Err(err) => { log::error!( @@ -232,7 +222,6 @@ impl Completion { view.id, item, offset_encoding, - start_offset, trigger_offset, true, ); @@ -254,7 +243,6 @@ impl Completion { view.id, item, offset_encoding, - start_offset, trigger_offset, false, ); From d63e570e0a4013f5ad703a9b1ce2d19a06630a82 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 9 Mar 2023 23:21:02 +0100 Subject: [PATCH 0101/1169] treat replace/insertmode consistently, default to insert --- book/src/configuration.md | 1 + helix-lsp/src/lib.rs | 45 +++++++++++++++++++++++---------- helix-term/src/ui/completion.rs | 24 ++++++++++++------ helix-view/src/editor.rs | 4 +++ 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/book/src/configuration.md b/book/src/configuration.md index aebf5ff0f..4b62ca521 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -50,6 +50,7 @@ signal to the Helix process on Unix operating systems, such as by using the comm | `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant | `400` | | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | +| `completion-replace` | Set to `true` to make completions always replace the entire word and not just the part before the cursor | `false` | | `auto-info` | Whether to display info boxes | `true` | | `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative | `false` | | `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file | `[]` | diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 58e8d83dc..1463ccb3c 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -250,18 +250,27 @@ pub mod util { /// If the LS did not provide a range for the completion or the range of the /// primary cursor can not be used for the secondary cursor, this function /// can be used to find the completion range for a cursor - fn find_completion_range(text: RopeSlice, cursor: usize) -> (usize, usize) { + fn find_completion_range(text: RopeSlice, replace_mode: bool, cursor: usize) -> (usize, usize) { let start = cursor - text .chars_at(cursor) .reversed() .take_while(|ch| chars::char_is_word(*ch)) .count(); - (start, cursor) + let mut end = cursor; + if replace_mode { + end += text + .chars_at(cursor) + .skip(1) + .take_while(|ch| chars::char_is_word(*ch)) + .count(); + } + (start, end) } fn completion_range( text: RopeSlice, edit_offset: Option<(i128, i128)>, + replace_mode: bool, cursor: usize, ) -> Option<(usize, usize)> { let res = match edit_offset { @@ -276,7 +285,7 @@ pub mod util { } (start_offset as usize, end_offset as usize) } - None => find_completion_range(text, cursor), + None => find_completion_range(text, replace_mode, cursor), }; Some(res) } @@ -287,6 +296,7 @@ pub mod util { doc: &Rope, selection: &Selection, edit_offset: Option<(i128, i128)>, + replace_mode: bool, new_text: String, ) -> Transaction { let replacement: Option = if new_text.is_empty() { @@ -296,9 +306,13 @@ pub mod util { }; let text = doc.slice(..); - let (removed_start, removed_end) = - completion_range(text, edit_offset, selection.primary().cursor(text)) - .expect("transaction must be valid for primary selection"); + let (removed_start, removed_end) = completion_range( + text, + edit_offset, + replace_mode, + selection.primary().cursor(text), + ) + .expect("transaction must be valid for primary selection"); let removed_text = text.slice(removed_start..removed_end); let (transaction, mut selection) = Transaction::change_by_selection_ignore_overlapping( @@ -306,9 +320,9 @@ pub mod util { selection, |range| { let cursor = range.cursor(text); - completion_range(text, edit_offset, cursor) + completion_range(text, edit_offset, replace_mode, cursor) .filter(|(start, end)| text.slice(start..end) == removed_text) - .unwrap_or_else(|| find_completion_range(text, cursor)) + .unwrap_or_else(|| find_completion_range(text, replace_mode, cursor)) }, |_, _| replacement.clone(), ); @@ -326,6 +340,7 @@ pub mod util { doc: &Rope, selection: &Selection, edit_offset: Option<(i128, i128)>, + replace_mode: bool, snippet: snippet::Snippet, line_ending: &str, include_placeholder: bool, @@ -336,9 +351,13 @@ pub mod util { let mut off = 0i128; let mut mapped_doc = doc.clone(); let mut selection_tabstops: SmallVec<[_; 1]> = SmallVec::new(); - let (removed_start, removed_end) = - completion_range(text, edit_offset, selection.primary().cursor(text)) - .expect("transaction must be valid for primary selection"); + let (removed_start, removed_end) = completion_range( + text, + edit_offset, + replace_mode, + selection.primary().cursor(text), + ) + .expect("transaction must be valid for primary selection"); let removed_text = text.slice(removed_start..removed_end); let (transaction, selection) = Transaction::change_by_selection_ignore_overlapping( @@ -346,9 +365,9 @@ pub mod util { selection, |range| { let cursor = range.cursor(text); - completion_range(text, edit_offset, cursor) + completion_range(text, edit_offset, replace_mode, cursor) .filter(|(start, end)| text.slice(start..end) == removed_text) - .unwrap_or_else(|| find_completion_range(text, cursor)) + .unwrap_or_else(|| find_completion_range(text, replace_mode, cursor)) }, |replacement_start, replacement_end| { let mapped_replacement_start = (replacement_start as i128 + off) as usize; diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 99c337811..6303793b4 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -108,6 +108,7 @@ impl Completion { start_offset: usize, trigger_offset: usize, ) -> Self { + let replace_mode = editor.config().completion_replace; // Sort completion items according to their preselect status (given by the LSP server) items.sort_by_key(|item| !item.preselect.unwrap_or(false)); @@ -120,18 +121,23 @@ impl Completion { offset_encoding: helix_lsp::OffsetEncoding, trigger_offset: usize, include_placeholder: bool, + replace_mode: bool, ) -> Transaction { use helix_lsp::snippet; let selection = doc.selection(view_id); let text = doc.text().slice(..); let primary_cursor = selection.primary().cursor(text); - let (start_offset, end_offset, new_text) = if let Some(edit) = &item.text_edit { + let (edit_offset, new_text) = if let Some(edit) = &item.text_edit { let edit = match edit { lsp::CompletionTextEdit::Edit(edit) => edit.clone(), lsp::CompletionTextEdit::InsertAndReplace(item) => { - // TODO: support using "insert" instead of "replace" via user config - lsp::TextEdit::new(item.replace, item.new_text.clone()) + let range = if replace_mode { + item.replace + } else { + item.insert + }; + lsp::TextEdit::new(range, item.new_text.clone()) } }; @@ -157,7 +163,7 @@ impl Completion { // document changed (and not just the selection) then we will // likely delete the wrong text (same if we applied an edit sent by the LS) debug_assert!(primary_cursor == trigger_offset); - (None, Some(0), new_text) + (None, new_text) }; if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET)) @@ -170,8 +176,8 @@ impl Completion { Ok(snippet) => util::generate_transaction_from_snippet( doc.text(), selection, - start_offset, - end_offset, + edit_offset, + replace_mode, snippet, doc.line_ending.as_str(), include_placeholder, @@ -190,8 +196,8 @@ impl Completion { util::generate_transaction_from_completion_edit( doc.text(), selection, - start_offset, - end_offset, + edit_offset, + replace_mode, new_text, ) } @@ -224,6 +230,7 @@ impl Completion { offset_encoding, trigger_offset, true, + replace_mode, ); // initialize a savepoint @@ -245,6 +252,7 @@ impl Completion { offset_encoding, trigger_offset, false, + replace_mode, ); doc.apply(&transaction, view.id); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index c6541105a..1b4664ffb 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -251,6 +251,9 @@ pub struct Config { )] pub idle_timeout: Duration, pub completion_trigger_len: u8, + /// Whether to instruct the LSP to replace the entire word when applying a completion + /// or to only insert new text + pub completion_replace: bool, /// Whether to display infoboxes. Defaults to true. pub auto_info: bool, pub file_picker: FilePickerConfig, @@ -738,6 +741,7 @@ impl Default for Config { color_modes: false, soft_wrap: SoftWrap::default(), text_width: 80, + completion_replace: false, } } } From 98415f288ffa043520b0c85bc4464dc44b85f948 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Fri, 10 Mar 2023 17:32:45 +0100 Subject: [PATCH 0102/1169] Improved yuck highlighting (and parser), and introduced a tag.builtin scope (#6242) --- book/src/themes.md | 1 + languages.toml | 2 +- runtime/queries/yuck/highlights.scm | 135 ++++++++++++++++++---------- runtime/queries/yuck/injections.scm | 2 +- 4 files changed, 91 insertions(+), 49 deletions(-) diff --git a/book/src/themes.md b/book/src/themes.md index af238e949..5ddd4f2c1 100644 --- a/book/src/themes.md +++ b/book/src/themes.md @@ -215,6 +215,7 @@ We use a similar set of scopes as - `special` (preprocessor in C) - `tag` - Tags (e.g. `` in HTML) + - `builtin` - `namespace` diff --git a/languages.toml b/languages.toml index 8697f9fcc..86f4a64d2 100644 --- a/languages.toml +++ b/languages.toml @@ -2206,7 +2206,7 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "yuck" -source = { git = "https://github.com/Philipp-M/tree-sitter-yuck", rev = "9e97da5773f82123a8c8cccf8f7e795d140ed7d1" } +source = { git = "https://github.com/Philipp-M/tree-sitter-yuck", rev = "e3d91a3c65decdea467adebe4127b8366fa47919" } [[language]] name = "prql" diff --git a/runtime/queries/yuck/highlights.scm b/runtime/queries/yuck/highlights.scm index 483348a8c..9f116f153 100644 --- a/runtime/queries/yuck/highlights.scm +++ b/runtime/queries/yuck/highlights.scm @@ -1,66 +1,107 @@ +; Errors + (ERROR) @error -(line_comment) @comment +; Comments -; keywords and symbols +(comment) @comment -(keyword) @keyword -(symbol) @tag +; Operators -; literals +[ + "+" + "-" + "*" + "/" + "%" + "||" + "&&" + "==" + "!=" + "=~" + ">" + "<" + ">=" + "<=" + "!" + "?." + "?:" +] @operator -(bool_literal) @constant.builtin.boolean -(num_literal) @constant.numeric +(ternary_expression + ["?" ":"] @operator) -; strings -(string_interpolation - (string_interpolation_start) @punctuation.special - (string_interpolation_end) @punctuation.special) +; Punctuation + +[ ":" "." "," ] @punctuation.delimiter + +[ "{" "}" "[" "]" "(" ")" ] @punctuation.bracket + +; Literals + +(number (float)) @constant.numeric.float + +(number (integer)) @constant.numeric.integer + +(boolean) @constant.builtin.boolean + +; Strings (escape_sequence) @constant.character.escape -(string - [ - (unescaped_single_quote_string_fragment) - (unescaped_double_quote_string_fragment) - (unescaped_backtick_string_fragment) - "\"" - "'" - "`" - ]) @string +(string_interpolation + "${" @punctuation.special + "}" @punctuation.special) -; operators and general punctuation +[ (string_fragment) "\"" "'" "`" ] @string -(unary_expression - operator: _ @operator) +; Attributes & Fields -(binary_expression - operator: _ @operator) +(keyword) @attribute -(ternary_expression - operator: _ @operator) +; Functions -[ - ":" - "." - "," -] @punctuation.delimiter +(function_call + name: (ident) @function) -[ - "(" - ")" - "[" - "]" - "{" - "}" -] @punctuation.bracket -[ - ":" - "." - "," -] @punctuation.delimiter +; Variables -; Rest (general identifiers that are not yet catched) +(ident) @variable + +(array + (symbol) @variable) + +; Builtin widgets + +(list . + ((symbol) @tag.builtin + (#match? @tag.builtin "^(box|button|calendar|centerbox|checkbox|circular-progress|color-button|color-chooser|combo-box-text|eventbox|expander|graph|image|input|label|literal|overlay|progress|revealer|scale|scroll|transform)$"))) + +; Keywords + +; I think there's a bug in tree-sitter the anchor doesn't seem to be working, see +; https://github.com/tree-sitter/tree-sitter/pull/2107 +(list . + ((symbol) @keyword + (#match? @keyword "^(defwindow|defwidget|defvar|defpoll|deflisten|geometry|children|struts)$"))) + +(list . + ((symbol) @keyword.control.import + (#eq? @keyword.control.import "include"))) + +; Loop + +(loop_widget . "for" @keyword.control.repeat . (symbol) @variable . "in" @keyword.operator . (symbol) @variable) + +(loop_widget . "for" @keyword.control.repeat . (symbol) @variable . "in" @keyword.operator) + +; Tags + +; TODO apply to every symbol in list? I think it should probably only be applied to the first child of the list +(list + (symbol) @tag) + +; Other stuff that has not been catched by the previous queries yet -(index) @variable (ident) @variable +(index) @variable diff --git a/runtime/queries/yuck/injections.scm b/runtime/queries/yuck/injections.scm index d3fdb0ca7..321c90add 100644 --- a/runtime/queries/yuck/injections.scm +++ b/runtime/queries/yuck/injections.scm @@ -1,2 +1,2 @@ -((line_comment) @injection.content +((comment) @injection.content (#set! injection.language "comment")) From 1661e4b5e1d8ebfef28f798fcb86ba2656373ba0 Mon Sep 17 00:00:00 2001 From: Dimitar Gyurov Date: Fri, 10 Mar 2023 23:42:42 +0100 Subject: [PATCH 0103/1169] Add a version-control statusline element (#5682) --- Cargo.lock | 1 + book/src/configuration.md | 1 + helix-term/src/ui/statusline.rs | 14 ++++++++++++++ helix-vcs/Cargo.toml | 1 + helix-vcs/src/git.rs | 17 +++++++++++++++++ helix-vcs/src/lib.rs | 14 +++++++++++++- helix-view/src/document.rs | 16 ++++++++++++++++ helix-view/src/editor.rs | 4 ++++ 8 files changed, 67 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a1a9eae4d..de985bca1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1212,6 +1212,7 @@ dependencies = [ name = "helix-vcs" version = "0.6.0" dependencies = [ + "arc-swap", "gix", "helix-core", "imara-diff", diff --git a/book/src/configuration.md b/book/src/configuration.md index 4b62ca521..e698646bc 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -111,6 +111,7 @@ The following statusline elements can be configured: | `position-percentage` | The cursor position as a percentage of the total number of lines | | `separator` | The string defined in `editor.statusline.separator` (defaults to `"│"`) | | `spacer` | Inserts a space between elements (multiple/contiguous spacers may be specified) | +| `version-control` | The current branch name or detached commit hash of the opened workspace | ### `[editor.lsp]` Section diff --git a/helix-term/src/ui/statusline.rs b/helix-term/src/ui/statusline.rs index 3e7065b82..887863519 100644 --- a/helix-term/src/ui/statusline.rs +++ b/helix-term/src/ui/statusline.rs @@ -159,6 +159,7 @@ where helix_view::editor::StatusLineElement::TotalLineNumbers => render_total_line_numbers, helix_view::editor::StatusLineElement::Separator => render_separator, helix_view::editor::StatusLineElement::Spacer => render_spacer, + helix_view::editor::StatusLineElement::VersionControl => render_version_control, } } @@ -476,3 +477,16 @@ where { write(context, String::from(" "), None); } + +fn render_version_control(context: &mut RenderContext, write: F) +where + F: Fn(&mut RenderContext, String, Option",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}());hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}());hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}());hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}());hljs.registerLanguage("d",function(){"use strict";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},d="((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))",n="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",t={className:"number",begin:"\\b"+d+"(L|u|U|Lu|LU|uL|UL)?",relevance:0},_={className:"number",begin:"\\b(((0[xX](([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)\\.([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)|\\.?([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))|((0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(\\.\\d*|([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)))|\\d+\\.(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)|\\.(0|[1-9][\\d_]*)([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))?))([fF]|L|i|[fF]i|Li)?|"+d+"(i|[fF]i|Li))",relevance:0},r={className:"string",begin:"'("+n+"|.)",end:"'",illegal:"."},i={className:"string",begin:'"',contains:[{begin:n,relevance:0}],end:'"[cwd]?'},s=e.COMMENT("\\/\\+","\\+\\/",{contains:["self"],relevance:10});return{name:"D",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:"string",begin:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',relevance:10},i,{className:"string",begin:'[rq]"',end:'"[cwd]?',relevance:5},{className:"string",begin:"`",end:"`[cwd]?"},{className:"string",begin:'q"\\{',end:'\\}"'},_,t,r,{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}}}());hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("http",function(){"use strict";return function(e){var n="HTTP/[0-9\\.]+";return{name:"HTTP",aliases:["https"],illegal:"\\S",contains:[{begin:"^"+n,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+n+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}}());hljs.registerLanguage("haskell",function(){"use strict";return function(e){var n={variants:[e.COMMENT("--","$"),e.COMMENT("{-","-}",{contains:["self"]})]},i={className:"meta",begin:"{-#",end:"#-}"},a={className:"meta",begin:"^#",end:"$"},s={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},l={begin:"\\(",end:"\\)",illegal:'"',contains:[i,a,{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{begin:"[_a-z][\\w']*"}),n]};return{name:"Haskell",aliases:["hs"],keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",contains:[{beginKeywords:"module",end:"where",keywords:"module where",contains:[l,n],illegal:"\\W\\.|;"},{begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding",contains:[l,n],illegal:"\\W\\.|;"},{className:"class",begin:"^(\\s*)?(class|instance)\\b",end:"where",keywords:"class family instance where",contains:[s,l,n]},{className:"class",begin:"\\b(data|(new)?type)\\b",end:"$",keywords:"data family type newtype deriving",contains:[i,s,l,{begin:"{",end:"}",contains:l.contains},n]},{beginKeywords:"default",end:"$",contains:[s,l,n]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,n]},{begin:"\\bforeign\\b",end:"$",keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",contains:[s,e.QUOTE_STRING_MODE,n]},{className:"meta",begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}]}}}());hljs.registerLanguage("handlebars",function(){"use strict";function e(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(n){const a={"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"},t=/\[.*?\]/,s=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,i=e("(",/'.*?'/,"|",/".*?"/,"|",t,"|",s,"|",/\.|\//,")+"),r=e("(",t,"|",s,")(?==)"),l={begin:i,lexemes:/[\w.\/]+/},c=n.inherit(l,{keywords:{literal:"true false undefined null"}}),o={begin:/\(/,end:/\)/},m={className:"attr",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:"name",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:"name"}),h=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:"Handlebars",aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,skip:!0},n.COMMENT(/\{\{!--/,/--\}\}/),n.COMMENT(/\{\{!/,/\}\}/),{className:"template-tag",begin:/\{\{\{\{(?!\/)/,end:/\}\}\}\}/,contains:[u],starts:{end:/\{\{\{\{\//,returnEnd:!0,subLanguage:"xml"}},{className:"template-tag",begin:/\{\{\{\{\//,end:/\}\}\}\}/,contains:[b]},{className:"template-tag",begin:/\{\{#/,end:/\}\}/,contains:[u]},{className:"template-tag",begin:/\{\{(?=else\}\})/,end:/\}\}/,keywords:"else"},{className:"template-tag",begin:/\{\{\//,end:/\}\}/,contains:[b]},{className:"template-variable",begin:/\{\{\{/,end:/\}\}\}/,contains:[h]},{className:"template-variable",begin:/\{\{/,end:/\}\}/,contains:[h]}]}}}());hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}());hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}());hljs.registerLanguage("ini",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(...n){return n.map(n=>e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}());hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}());hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}());hljs.registerLanguage("x86asm",function(){"use strict";return function(s){return{name:"Intel x86 Assembly",case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE,keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'"},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst",begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{className:"meta",begin:/^\s*\.[\w_-]+/}]}}}());hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}());hljs.registerLanguage("armasm",function(){"use strict";return function(s){const e={variants:[s.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),s.COMMENT("[;@]","$",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+s.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},e,s.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}}());hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:">>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}());hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}());hljs.registerLanguage("scala",function(){"use strict";return function(e){var n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:"\\${",end:"}"}]},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,relevance:0},i={className:"class",beginKeywords:"class object trait type",end:/[:={\[\n;]/,excludeEnd:!0,contains:[{beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,excludeEnd:!0,contains:[t]};return{name:"Scala",keywords:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:"symbol",begin:"'\\w[\\w\\d_]*(?!')"},s,l,i,e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"}]}}}());hljs.registerLanguage("julia",function(){"use strict";return function(e){var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},a={keywords:t,illegal:/<\//},n={className:"subst",begin:/\$\(/,end:/\)/,keywords:t},o={className:"variable",begin:"\\$"+r},i={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\w*"""/,end:/"""\w*/,relevance:10},{begin:/\w*"/,end:/"\w*/}]},l={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],begin:"`",end:"`"},s={className:"meta",begin:"@"+r};return a.name="Julia",a.contains=[{className:"number",begin:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,relevance:0},{className:"string",begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:"comment",variants:[{begin:"#=",end:"=#",relevance:10},{begin:"#",end:"$"}]},e.HASH_COMMENT_MODE,{className:"keyword",begin:"\\b(((abstract|primitive)\\s+)type|(mutable\\s+)?struct)\\b"},{begin:/<:/}],n.contains=a.contains,a}}());hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());hljs.registerLanguage("r",function(){"use strict";return function(e){var n="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{name:"R",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},relevance:0},{className:"number",begin:"0[xX][0-9a-fA-F]+[Li]?\\b",relevance:0},{className:"number",begin:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",relevance:0},{className:"number",begin:"\\d+\\.(?!\\d)(?:i\\b)?",relevance:0},{className:"number",begin:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{className:"number",begin:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{begin:"`",end:"`",relevance:0},{className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'"',end:'"'},{begin:"'",end:"'"}]}]}}}());hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}());hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}());hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}());hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}());hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}());hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}());hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); diff --git a/book/theme/index.hbs b/book/theme/index.hbs index 6e0cce0aa..ed27410f6 100644 --- a/book/theme/index.hbs +++ b/book/theme/index.hbs @@ -15,7 +15,6 @@ {{> head}} - @@ -53,18 +52,19 @@ {{#if mathjax_support}} - + {{/if}} +
- - - - +
{{> header}} -