Merge branch 'helix-editor:master' into command-expansion

pull/11164/head
Théo Daron 5 months ago
commit 0d97406257

71
Cargo.lock generated

@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.100" version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -171,9 +171,9 @@ dependencies = [
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
version = "5.3.1" version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
dependencies = [ dependencies = [
"error-code", "error-code",
] ]
@ -351,6 +351,15 @@ dependencies = [
"parking_lot_core", "parking_lot_core",
] ]
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]] [[package]]
name = "dunce" name = "dunce"
version = "1.0.4" version = "1.0.4"
@ -1601,12 +1610,12 @@ dependencies = [
[[package]] [[package]]
name = "imara-diff" name = "imara-diff"
version = "0.1.5" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8" checksum = "af13c8ceb376860ff0c6a66d83a8cdd4ecd9e464da24621bbffcd02b49619434"
dependencies = [ dependencies = [
"ahash", "ahash",
"hashbrown 0.12.3", "hashbrown 0.14.5",
] ]
[[package]] [[package]]
@ -1711,9 +1720,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "lsp-types" name = "lsp-types"
@ -1795,6 +1804,12 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.15"
@ -1840,9 +1855,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "open" name = "open"
version = "5.1.4" version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5ca541f22b1c46d4bb9801014f234758ab4297e7870b904b6a8415b980a7388" checksum = "9d2c909a3fce3bd80efef4cd1c6c056bd9376a8fe06fcfdbebaf32cb485a7e37"
dependencies = [ dependencies = [
"is-wsl", "is-wsl",
"libc", "libc",
@ -1896,6 +1911,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.76" version = "1.0.76"
@ -2098,18 +2119,18 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2118,9 +2139,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.117" version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -2358,13 +2379,16 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.23" version = "0.3.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [ dependencies = [
"deranged",
"itoa", "itoa",
"libc", "libc",
"num-conv",
"num_threads", "num_threads",
"powerfmt",
"serde", "serde",
"time-core", "time-core",
"time-macros", "time-macros",
@ -2372,16 +2396,17 @@ dependencies = [
[[package]] [[package]]
name = "time-core" name = "time-core"
version = "0.1.1" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.10" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
dependencies = [ dependencies = [
"num-conv",
"time-core", "time-core",
] ]

@ -47,7 +47,7 @@ Note: Only certain languages have indentation definitions at the moment. Check
[Installation documentation](https://docs.helix-editor.com/install.html). [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) [![Packaging status](https://repology.org/badge/vertical-allrepos/helix.svg?exclude_unsupported=1)](https://repology.org/project/helix/versions)
# Contributing # Contributing

@ -148,6 +148,12 @@ provided `.desktop` and icon files to their correct folders:
cp contrib/Helix.desktop ~/.local/share/applications cp contrib/Helix.desktop ~/.local/share/applications
cp contrib/helix.png ~/.icons # or ~/.local/share/icons cp contrib/helix.png ~/.icons # or ~/.local/share/icons
``` ```
It is recommended to convert the links in the `.desktop` file to absolute paths to avoid potential problems:
```sh
sed -i -e "s|Exec=hx %F|Exec=$(readlink -f ~/.cargo/bin/hx) %F|g" \
-e "s|Icon=helix|Icon=$(readlink -f ~/.icons/helix.png)|g" ~/.local/share/applications/Helix.desktop
```
To use another terminal than the system default, you can modify the `.desktop` To use another terminal than the system default, you can modify the `.desktop`
file. For example, to use `kitty`: file. For example, to use `kitty`:

@ -1,15 +1,18 @@
#!/usr/bin/env fish #!/usr/bin/env fish
# Fish completion script for Helix editor # Fish completion script for Helix editor
set -l langs (hx --health |tail -n '+7' |awk '{print $1}' |sed 's/\x1b\[[0-9;]*m//g')
complete -c hx -s h -l help -d "Prints help information" complete -c hx -s h -l help -d "Prints help information"
complete -c hx -l tutor -d "Loads the tutorial" complete -c hx -l tutor -d "Loads the tutorial"
complete -c hx -l health -x -a "$langs" -d "Checks for errors in editor setup" complete -c hx -l health -xa "(__hx_langs_ops)" -d "Checks for errors"
complete -c hx -s g -l grammar -x -a "fetch build" -d "Fetches or builds tree-sitter grammars" complete -c hx -s g -l grammar -x -a "fetch build" -d "Fetch or build tree-sitter grammars"
complete -c hx -s v -o vv -o vvv -d "Increases logging verbosity" complete -c hx -s v -o vv -o vvv -d "Increases logging verbosity"
complete -c hx -s V -l version -d "Prints version information" complete -c hx -s V -l version -d "Prints version information"
complete -c hx -l vsplit -d "Splits all given files vertically into different windows" complete -c hx -l vsplit -d "Splits all given files vertically"
complete -c hx -l hsplit -d "Splits all given files horizontally into different windows" complete -c hx -l hsplit -d "Splits all given files horizontally"
complete -c hx -s c -l config -r -d "Specifies a file to use for completion" complete -c hx -s c -l config -r -d "Specifies a file to use for config"
complete -c hx -l log -r -d "Specifies a file to write log data into" complete -c hx -l log -r -d "Specifies a file to use for logging"
complete -c hx -s w -l working-dir -d "Specify initial working directory" -xa "(__fish_complete_directories)"
function __hx_langs_ops
hx --health languages | tail -n '+2' | string replace -fr '^(\S+) .*' '$1'
end

@ -14,16 +14,18 @@ _hx() {
"--health[Checks for errors in editor setup]:language:->health" \ "--health[Checks for errors in editor setup]:language:->health" \
"-g[Fetches or builds tree-sitter grammars]:action:->grammar" \ "-g[Fetches or builds tree-sitter grammars]:action:->grammar" \
"--grammar[Fetches or builds tree-sitter grammars]:action:->grammar" \ "--grammar[Fetches or builds tree-sitter grammars]:action:->grammar" \
"--vsplit[Splits all given files vertically into different windows]" \ "--vsplit[Splits all given files vertically]" \
"--hsplit[Splits all given files horizontally into different windows]" \ "--hsplit[Splits all given files horizontally]" \
"-c[Specifies a file to use for configuration]" \ "-c[Specifies a file to use for configuration]" \
"--config[Specifies a file to use for configuration]" \ "--config[Specifies a file to use for configuration]" \
"--log[Specifies a file to write log data into]" \ "-w[Specify initial working directory]" \
"--working-dir[Specify initial working directory]" \
"--log[Specifies a file to use for logging]" \
"*:file:_files" "*:file:_files"
case "$state" in case "$state" in
health) health)
local languages=($(hx --health |tail -n '+7' |awk '{print $1}' |sed 's/\x1b\[[0-9;]*m//g')) local languages=($(hx --health | tail -n '+11' | awk '{print $1}' | sed 's/\x1b\[[0-9;]*m//g;s/[✘✓]//g'))
_values 'language' $languages _values 'language' $languages
;; ;;
grammar) grammar)
@ -31,4 +33,3 @@ _hx() {
;; ;;
esac esac
} }

@ -40,7 +40,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
toml = "0.8" toml = "0.8"
imara-diff = "0.1.0" imara-diff = "0.1.6"
encoding_rs = "0.8" encoding_rs = "0.8"

@ -175,7 +175,7 @@ impl Range {
/// function runs in O(N) (N is number of changes) and can therefore /// function runs in O(N) (N is number of changes) and can therefore
/// cause performance problems if run for a large number of ranges as the /// cause performance problems if run for a large number of ranges as the
/// complexity is then O(MN) (for multicuror M=N usually). Instead use /// complexity is then O(MN) (for multicuror M=N usually). Instead use
/// [Selection::map] or [ChangeSet::update_positions] instead /// [Selection::map] or [ChangeSet::update_positions].
pub fn map(mut self, changes: &ChangeSet) -> Self { pub fn map(mut self, changes: &ChangeSet) -> Self {
use std::cmp::Ordering; use std::cmp::Ordering;
if changes.is_empty() { if changes.is_empty() {
@ -541,6 +541,8 @@ impl Selection {
} }
/// Normalizes a `Selection`. /// Normalizes a `Selection`.
///
/// Ranges are sorted by [Range::from], with overlapping ranges merged.
fn normalize(mut self) -> Self { fn normalize(mut self) -> Self {
if self.len() < 2 { if self.len() < 2 {
return self; return self;

@ -157,8 +157,8 @@ impl Client {
) )
} }
pub fn starting_request_args(&self) -> &Option<Value> { pub fn starting_request_args(&self) -> Option<&Value> {
&self.starting_request_args self.starting_request_args.as_ref()
} }
pub async fn tcp_process( pub async fn tcp_process(

@ -422,7 +422,7 @@ fn build_tree_sitter_library(
} }
} }
let recompile = needs_recompile(&library_path, &parser_path, &scanner_path) let recompile = needs_recompile(&library_path, &parser_path, scanner_path.as_ref())
.context("Failed to compare source and binary timestamps")?; .context("Failed to compare source and binary timestamps")?;
if !recompile { if !recompile {
@ -568,7 +568,7 @@ fn build_tree_sitter_library(
fn needs_recompile( fn needs_recompile(
lib_path: &Path, lib_path: &Path,
parser_c_path: &Path, parser_c_path: &Path,
scanner_path: &Option<PathBuf>, scanner_path: Option<&PathBuf>,
) -> Result<bool> { ) -> Result<bool> {
if !lib_path.exists() { if !lib_path.exists() {
return Ok(true); return Ok(true);

@ -123,7 +123,7 @@ impl Client {
{ {
client.add_workspace_folder( client.add_workspace_folder(
root_uri, root_uri,
&workspace_folders_caps.change_notifications, workspace_folders_caps.change_notifications.as_ref(),
); );
} }
}); });
@ -136,7 +136,10 @@ impl Client {
.and_then(|cap| cap.workspace_folders.as_ref()) .and_then(|cap| cap.workspace_folders.as_ref())
.filter(|cap| cap.supported.unwrap_or(false)) .filter(|cap| cap.supported.unwrap_or(false))
{ {
self.add_workspace_folder(root_uri, &workspace_folders_caps.change_notifications); self.add_workspace_folder(
root_uri,
workspace_folders_caps.change_notifications.as_ref(),
);
true true
} else { } else {
// the server doesn't support multi workspaces, we need a new client // the server doesn't support multi workspaces, we need a new client
@ -147,7 +150,7 @@ impl Client {
fn add_workspace_folder( fn add_workspace_folder(
&self, &self,
root_uri: Option<lsp::Url>, root_uri: Option<lsp::Url>,
change_notifications: &Option<OneOf<bool, String>>, change_notifications: Option<&OneOf<bool, String>>,
) { ) {
// root_uri is None just means that there isn't really any LSP workspace // root_uri is None just means that there isn't really any LSP workspace
// associated with this file. For servers that support multiple workspaces // associated with this file. For servers that support multiple workspaces
@ -162,7 +165,7 @@ impl Client {
self.workspace_folders self.workspace_folders
.lock() .lock()
.push(workspace_for_uri(root_uri.clone())); .push(workspace_for_uri(root_uri.clone()));
if &Some(OneOf::Left(false)) == change_notifications { if Some(&OneOf::Left(false)) == change_notifications {
// server specifically opted out of DidWorkspaceChange notifications // server specifically opted out of DidWorkspaceChange notifications
// let's assume the server will request the workspace folders itself // let's assume the server will request the workspace folders itself
// and that we can therefore reuse the client (but are done now) // and that we can therefore reuse the client (but are done now)
@ -616,6 +619,9 @@ impl Client {
prepare_support_default_behavior: None, prepare_support_default_behavior: None,
honors_change_annotations: Some(false), honors_change_annotations: Some(false),
}), }),
formatting: Some(lsp::DocumentFormattingClientCapabilities {
dynamic_registration: Some(false),
}),
code_action: Some(lsp::CodeActionClientCapabilities { code_action: Some(lsp::CodeActionClientCapabilities {
code_action_literal_support: Some(lsp::CodeActionLiteralSupport { code_action_literal_support: Some(lsp::CodeActionLiteralSupport {
code_action_kind: lsp::CodeActionKindLiteralSupport { code_action_kind: lsp::CodeActionKindLiteralSupport {

@ -58,7 +58,7 @@ pulldown-cmark = { version = "0.11", default-features = false }
content_inspector = "0.2.4" content_inspector = "0.2.4"
# opening URLs # opening URLs
open = "5.1.4" open = "5.2.0"
url = "2.5.2" url = "2.5.2"
# config # config

@ -1210,19 +1210,15 @@ fn goto_file_impl(cx: &mut Context, action: Action) {
let (view, doc) = current_ref!(cx.editor); let (view, doc) = current_ref!(cx.editor);
let text = doc.text(); let text = doc.text();
let selections = doc.selection(view.id); let selections = doc.selection(view.id);
let primary = selections.primary();
let rel_path = doc let rel_path = doc
.relative_path() .relative_path()
.map(|path| path.parent().unwrap().to_path_buf()) .map(|path| path.parent().unwrap().to_path_buf())
.unwrap_or_default(); .unwrap_or_default();
let mut paths: Vec<_> = selections
.iter()
.map(|r| text.slice(r.from()..r.to()).to_string())
.collect();
let primary = selections.primary();
// Checks whether there is only one selection with a width of 1
if selections.len() == 1 && primary.len() == 1 {
paths.clear();
let paths: Vec<_> = if selections.len() == 1 && primary.len() == 1 {
// Secial case: if there is only one one-width selection, try to detect the
// path under the cursor.
let is_valid_path_char = |c: &char| { let is_valid_path_char = |c: &char| {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let valid_chars = &[ let valid_chars = &[
@ -1257,29 +1253,29 @@ fn goto_file_impl(cx: &mut Context, action: Action) {
.take_while(is_valid_path_char) .take_while(is_valid_path_char)
.count(); .count();
let path: Cow<str> = text let path: String = text
.slice((start_pos - prefix_len)..(start_pos + postfix_len)) .slice((start_pos - prefix_len)..(start_pos + postfix_len))
.into(); .into();
log::debug!("Goto file path: {}", path); log::debug!("goto_file auto-detected path: {}", path);
match expand_tilde(PathBuf::from(path.as_ref())).to_str() { vec![path]
Some(path) => paths.push(path.to_string()), } else {
None => cx.editor.set_error("Couldn't get string out of path."), // Otherwise use each selection, trimmed.
selections
.fragments(text.slice(..))
.map(|sel| sel.trim().to_string())
.filter(|sel| !sel.is_empty())
.collect()
}; };
}
for sel in paths { for sel in paths {
let p = sel.trim(); if let Ok(url) = Url::parse(&sel) {
if p.is_empty() {
continue;
}
if let Ok(url) = Url::parse(p) {
open_url(cx, url, action); open_url(cx, url, action);
continue; continue;
} }
let path = &rel_path.join(p); let path = expand_tilde(Cow::from(PathBuf::from(sel)));
let path = &rel_path.join(path);
if path.is_dir() { if path.is_dir() {
let picker = ui::file_picker(path.into(), &cx.editor.config()); let picker = ui::file_picker(path.into(), &cx.editor.config());
cx.push_layer(Box::new(overlaid(picker))); cx.push_layer(Box::new(overlaid(picker)));
@ -5768,6 +5764,7 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
let fragment = range.slice(text); let fragment = range.slice(text);
match shell_impl(shell, cmd, pipe.then(|| fragment.into())) { match shell_impl(shell, cmd, pipe.then(|| fragment.into())) {
Ok(result) => { Ok(result) => {
let result = Tendril::from(result.trim_end());
if !pipe { if !pipe {
shell_output = Some(result.clone()); shell_output = Some(result.clone());
} }

@ -9,6 +9,7 @@ use super::*;
use helix_core::fuzzy::fuzzy_match; use helix_core::fuzzy::fuzzy_match;
use helix_core::indent::MAX_INDENT; use helix_core::indent::MAX_INDENT;
use helix_core::{line_ending, shellwords::Shellwords}; use helix_core::{line_ending, shellwords::Shellwords};
use helix_lsp::LanguageServerId;
use helix_view::document::{read_to_string, DEFAULT_LANGUAGE_NAME}; use helix_view::document::{read_to_string, DEFAULT_LANGUAGE_NAME};
use helix_view::editor::{CloseError, ConfigEvent}; use helix_view::editor::{CloseError, ConfigEvent};
use serde_json::Value; use serde_json::Value;
@ -339,9 +340,12 @@ fn write_impl(
let path = path.map(AsRef::as_ref); let path = path.map(AsRef::as_ref);
if config.insert_final_newline { if config.insert_final_newline {
insert_final_newline(doc, view); insert_final_newline(doc, view.id);
} }
// Save an undo checkpoint for any outstanding changes.
doc.append_changes_to_history(view);
let fmt = if config.auto_format { let fmt = if config.auto_format {
doc.auto_format().map(|fmt| { doc.auto_format().map(|fmt| {
let callback = make_format_callback( let callback = make_format_callback(
@ -366,13 +370,12 @@ fn write_impl(
Ok(()) Ok(())
} }
fn insert_final_newline(doc: &mut Document, view: &mut View) { fn insert_final_newline(doc: &mut Document, view_id: ViewId) {
let text = doc.text(); let text = doc.text();
if line_ending::get_line_ending(&text.slice(..)).is_none() { if line_ending::get_line_ending(&text.slice(..)).is_none() {
let eof = Selection::point(text.len_chars()); let eof = Selection::point(text.len_chars());
let insert = Transaction::insert(text, &eof, doc.line_ending.as_str().into()); let insert = Transaction::insert(text, &eof, doc.line_ending.as_str().into());
doc.apply(&insert, view.id); doc.apply(&insert, view_id);
doc.append_changes_to_history(view);
} }
} }
@ -703,11 +706,15 @@ pub fn write_all_impl(
for (doc_id, target_view) in saves { for (doc_id, target_view) in saves {
let doc = doc_mut!(cx.editor, &doc_id); let doc = doc_mut!(cx.editor, &doc_id);
let view = view_mut!(cx.editor, target_view);
if config.insert_final_newline { if config.insert_final_newline {
insert_final_newline(doc, view_mut!(cx.editor, target_view)); insert_final_newline(doc, target_view);
} }
// Save an undo checkpoint for any outstanding changes.
doc.append_changes_to_history(view);
let fmt = if config.auto_format { let fmt = if config.auto_format {
doc.auto_format().map(|fmt| { doc.auto_format().map(|fmt| {
let callback = make_format_callback( let callback = make_format_callback(
@ -1370,37 +1377,51 @@ fn lsp_workspace_command(
if event != PromptEvent::Validate { if event != PromptEvent::Validate {
return Ok(()); return Ok(());
} }
struct LsIdCommand(LanguageServerId, helix_lsp::lsp::Command);
impl ui::menu::Item for LsIdCommand {
type Data = ();
fn format(&self, _data: &Self::Data) -> Row {
self.1.title.as_str().into()
}
}
let doc = doc!(cx.editor); let doc = doc!(cx.editor);
let Some((language_server_id, options)) = doc let ls_id_commands = doc
.language_servers_with_feature(LanguageServerFeature::WorkspaceCommand) .language_servers_with_feature(LanguageServerFeature::WorkspaceCommand)
.find_map(|ls| { .flat_map(|ls| {
ls.capabilities() ls.capabilities()
.execute_command_provider .execute_command_provider
.as_ref() .iter()
.map(|options| (ls.id(), options)) .flat_map(|options| options.commands.iter())
}) .map(|command| (ls.id(), command))
else { });
cx.editor
.set_status("No active language servers for this document support workspace commands");
return Ok(());
};
if args.is_empty() { if args.is_empty() {
let commands = options let commands = ls_id_commands
.commands .map(|(ls_id, command)| {
.iter() LsIdCommand(
.map(|command| helix_lsp::lsp::Command { ls_id,
helix_lsp::lsp::Command {
title: command.clone(), title: command.clone(),
command: command.clone(), command: command.clone(),
arguments: None, arguments: None,
},
)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let callback = async move { let callback = async move {
let call: job::Callback = Callback::EditorCompositor(Box::new( let call: job::Callback = Callback::EditorCompositor(Box::new(
move |_editor: &mut Editor, compositor: &mut Compositor| { move |_editor: &mut Editor, compositor: &mut Compositor| {
let picker = ui::Picker::new(commands, (), move |cx, command, _action| { let picker = ui::Picker::new(
execute_lsp_command(cx.editor, language_server_id, command.clone()); commands,
}); (),
move |cx, LsIdCommand(ls_id, command), _action| {
execute_lsp_command(cx.editor, *ls_id, command.clone());
},
);
compositor.push(Box::new(overlaid(picker))) compositor.push(Box::new(overlaid(picker)))
}, },
)); ));
@ -1409,21 +1430,32 @@ fn lsp_workspace_command(
cx.jobs.callback(callback); cx.jobs.callback(callback);
} else { } else {
let command = args.join(" "); let command = args.join(" ");
if options.commands.iter().any(|c| c == &command) { let matches: Vec<_> = ls_id_commands
.filter(|(_ls_id, c)| *c == &command)
.collect();
match matches.as_slice() {
[(ls_id, _command)] => {
execute_lsp_command( execute_lsp_command(
cx.editor, cx.editor,
language_server_id, *ls_id,
helix_lsp::lsp::Command { helix_lsp::lsp::Command {
title: command.clone(), title: command.clone(),
arguments: None, arguments: None,
command, command,
}, },
); );
} else { }
[] => {
cx.editor.set_status(format!( cx.editor.set_status(format!(
"`{command}` is not supported for this language server" "`{command}` is not supported for any language server"
)); ));
return Ok(()); }
_ => {
cx.editor.set_status(format!(
"`{command}` supported by multiple language servers"
));
}
} }
} }
Ok(()) Ok(())

@ -364,14 +364,16 @@ pub mod completers {
} }
pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec<Completion> { pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec<Completion> {
let Some(options) = doc!(editor) let commands = doc!(editor)
.language_servers_with_feature(LanguageServerFeature::WorkspaceCommand) .language_servers_with_feature(LanguageServerFeature::WorkspaceCommand)
.find_map(|ls| ls.capabilities().execute_command_provider.as_ref()) .flat_map(|ls| {
else { ls.capabilities()
return vec![]; .execute_command_provider
}; .iter()
.flat_map(|options| options.commands.iter())
});
fuzzy_match(input, &options.commands, false) fuzzy_match(input, commands, false)
.into_iter() .into_iter()
.map(|(name, _)| ((0..), name.to_owned().into())) .map(|(name, _)| ((0..), name.to_owned().into()))
.collect() .collect()

@ -210,13 +210,10 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
"}, "},
"|echo foo<ret>", "|echo foo<ret>",
indoc! {"\ indoc! {"\
#[|foo\n]# #[|foo]#
#(|foo)#
#(|foo\n)# #(|foo)#"
},
#(|foo\n)#
"},
)) ))
.await?; .await?;
@ -229,12 +226,9 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
"}, "},
"!echo foo<ret>", "!echo foo<ret>",
indoc! {"\ indoc! {"\
#[|foo\n]# #[|foo]#lorem
lorem #(|foo)#ipsum
#(|foo\n)# #(|foo)#dolor
ipsum
#(|foo\n)#
dolor
"}, "},
)) ))
.await?; .await?;
@ -248,12 +242,9 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
"}, "},
"<A-!>echo foo<ret>", "<A-!>echo foo<ret>",
indoc! {"\ indoc! {"\
lorem#[|foo\n]# lorem#[|foo]#
ipsum#(|foo)#
ipsum#(|foo\n)# dolor#(|foo)#
dolor#(|foo\n)#
"}, "},
)) ))
.await?; .await?;

@ -20,7 +20,7 @@ parking_lot = "0.12"
arc-swap = { version = "1.7.1" } arc-swap = { version = "1.7.1" }
gix = { version = "0.63.0", features = ["attributes", "status"], default-features = false, optional = true } gix = { version = "0.63.0", features = ["attributes", "status"], default-features = false, optional = true }
imara-diff = "0.1.5" imara-diff = "0.1.6"
anyhow = "1" anyhow = "1"
log = "0.4" log = "0.4"

@ -53,7 +53,7 @@ parking_lot = "0.12.3"
thiserror.workspace = true thiserror.workspace = true
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
clipboard-win = { version = "5.3", features = ["std"] } clipboard-win = { version = "5.4", features = ["std"] }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
libc = "0.2" libc = "0.2"

@ -1410,6 +1410,11 @@ impl Document {
} }
fn undo_redo_impl(&mut self, view: &mut View, undo: bool) -> bool { fn undo_redo_impl(&mut self, view: &mut View, undo: bool) -> bool {
if undo {
self.append_changes_to_history(view);
} else if !self.changes.is_empty() {
return false;
}
let mut history = self.history.take(); let mut history = self.history.take();
let txn = if undo { history.undo() } else { history.redo() }; let txn = if undo { history.undo() } else { history.redo() };
let success = if let Some(txn) = txn { let success = if let Some(txn) = txn {
@ -1490,6 +1495,11 @@ impl Document {
} }
fn earlier_later_impl(&mut self, view: &mut View, uk: UndoKind, earlier: bool) -> bool { fn earlier_later_impl(&mut self, view: &mut View, uk: UndoKind, earlier: bool) -> bool {
if earlier {
self.append_changes_to_history(view);
} else if !self.changes.is_empty() {
return false;
}
let txns = if earlier { let txns = if earlier {
self.history.get_mut().earlier(uk) self.history.get_mut().earlier(uk)
} else { } else {

@ -73,6 +73,7 @@ prisma-language-server = { command = "prisma-language-server", args = ["--stdio"
purescript-language-server = { command = "purescript-language-server", args = ["--stdio"] } purescript-language-server = { command = "purescript-language-server", args = ["--stdio"] }
pylsp = { command = "pylsp" } pylsp = { command = "pylsp" }
pyright = { command = "pyright-langserver", args = ["--stdio"], config = {} } pyright = { command = "pyright-langserver", args = ["--stdio"], config = {} }
basedpyright = { command = "basedpyright-langserver", args = ["--stdio"], config = {} }
pylyzer = { command = "pylyzer", args = ["--server"] } pylyzer = { command = "pylyzer", args = ["--server"] }
qmlls = { command = "qmlls" } qmlls = { command = "qmlls" }
r = { command = "R", args = ["--no-echo", "-e", "languageserver::run()"] } r = { command = "R", args = ["--no-echo", "-e", "languageserver::run()"] }
@ -296,7 +297,7 @@ source = { git = "https://github.com/FuelLabs/tree-sitter-sway", rev = "e491a005
name = "toml" name = "toml"
scope = "source.toml" scope = "source.toml"
injection-regex = "toml" injection-regex = "toml"
file-types = ["toml", { glob = "poetry.lock" }, { glob = "Cargo.lock" }] file-types = ["toml", { glob = "pdm.lock" }, { glob = "poetry.lock" }, { glob = "Cargo.lock" }, { glob = "uv.lock" }]
comment-token = "#" comment-token = "#"
language-servers = [ "taplo" ] language-servers = [ "taplo" ]
indent = { tab-width = 2, unit = " " } indent = { tab-width = 2, unit = " " }
@ -1269,7 +1270,7 @@ source = { git = "https://github.com/ikatyang/tree-sitter-yaml", rev = "0e36bed1
name = "haskell" name = "haskell"
scope = "source.haskell" scope = "source.haskell"
injection-regex = "hs|haskell" injection-regex = "hs|haskell"
file-types = ["hs", "hs-boot"] file-types = ["hs", "hs-boot", "hsc"]
roots = ["Setup.hs", "stack.yaml", "cabal.project"] roots = ["Setup.hs", "stack.yaml", "cabal.project"]
comment-token = "--" comment-token = "--"
block-comment-tokens = { start = "{-", end = "-}" } block-comment-tokens = { start = "{-", end = "-}" }
@ -1693,7 +1694,7 @@ source = { git = "https://github.com/mtoohey31/tree-sitter-gitattributes", rev =
[[language]] [[language]]
name = "git-ignore" name = "git-ignore"
scope = "source.gitignore" scope = "source.gitignore"
file-types = [{ glob = ".gitignore_global" }, { glob = ".ignore" }, { glob = "CODEOWNERS" }, { glob = ".config/helix/ignore" }, { glob = ".helix/ignore" }, { glob = ".*ignore" }] file-types = [{ glob = ".gitignore_global" }, { glob = "git/ignore" }, { glob = ".ignore" }, { glob = "CODEOWNERS" }, { glob = ".config/helix/ignore" }, { glob = ".helix/ignore" }, { glob = ".*ignore" }]
injection-regex = "git-ignore" injection-regex = "git-ignore"
comment-token = "#" comment-token = "#"
grammar = "gitignore" grammar = "gitignore"
@ -2054,7 +2055,7 @@ indent = { tab-width = 8, unit = "\t" }
[[grammar]] [[grammar]]
name = "hare" name = "hare"
source = { git = "https://git.sr.ht/~ecmma/tree-sitter-hare", rev = "2495958aaf3f93581c87ec020164255e80655331" } source = { git = "https://git.sr.ht/~ecs/tree-sitter-hare", rev = "07035a248943575444aa0b893ffe306e1444c0ab" }
[[language]] [[language]]
name = "devicetree" name = "devicetree"
@ -2081,7 +2082,7 @@ language-servers = [ "cairo-language-server" ]
[[grammar]] [[grammar]]
name = "cairo" name = "cairo"
source = { git = "https://github.com/starkware-libs/tree-sitter-cairo", rev = "0596baab741ffacdc65c761d5d5ffbbeae97f033" } source = { git = "https://github.com/starkware-libs/tree-sitter-cairo", rev = "e3a0212261c125cb38248458cd856c0ffee2b398" }
[[language]] [[language]]
name = "cpon" name = "cpon"
@ -2702,6 +2703,8 @@ file-types = [
"kube", "kube",
"network", "network",
{ glob = ".editorconfig" }, { glob = ".editorconfig" },
{ glob = ".npmrc" },
{ glob = "npmrc" },
{ glob = "rclone.conf" }, { glob = "rclone.conf" },
"properties", "properties",
"cfg", "cfg",
@ -3242,7 +3245,7 @@ auto-format = true
[[grammar]] [[grammar]]
name = "todotxt" name = "todotxt"
source = { git = "https://github.com/arnarg/tree-sitter-todotxt", rev = "0207f6a4ab6aeafc4b091914d31d8235049a2578" } source = { git = "https://github.com/arnarg/tree-sitter-todotxt", rev = "3937c5cd105ec4127448651a21aef45f52d19609" }
[[language]] [[language]]
name = "strace" name = "strace"
@ -3513,7 +3516,7 @@ scope = "source.helm"
roots = ["Chart.yaml"] roots = ["Chart.yaml"]
comment-token = "#" comment-token = "#"
language-servers = ["helm_ls"] language-servers = ["helm_ls"]
file-types = [ { glob = "templates/*.yaml" }, { glob = "templates/_*.tpl"}, { glob = "templates/NOTES.txt" } ] file-types = [ { glob = "templates/*.yaml" }, { glob = "templates/*.yml" }, { glob = "templates/_*.tpl"}, { glob = "templates/NOTES.txt" } ]
[[language]] [[language]]
name = "glimmer" name = "glimmer"

@ -1,10 +1,15 @@
[ [
(string) (string)
(raw_string) (raw_string)
(ansi_c_string)
(heredoc_body) (heredoc_body)
(heredoc_start)
] @string ] @string
[
(heredoc_start)
(heredoc_end)
] @label
(command_name) @function (command_name) @function
(variable_name) @variable.other.member (variable_name) @variable.other.member

@ -6,3 +6,6 @@
argument: (raw_string) @injection.content argument: (raw_string) @injection.content
(#match? @_command "^[gnm]?awk$") (#match? @_command "^[gnm]?awk$")
(#set! injection.language "awk")) (#set! injection.language "awk"))
((regex) @injection.content
(#set! injection.language "regex"))

@ -95,6 +95,12 @@
; ------- ; -------
; Keywords ; Keywords
; ------- ; -------
(for_expression
"for" @keyword.control.repeat)
"in" @keyword.control
[ [
"match" "match"
"if" "if"

@ -115,4 +115,10 @@
(#not-same-line? @expr-start @pattern-guard) (#not-same-line? @expr-start @pattern-guard)
) @indent ) @indent
(for_expression
"in" @in
.
(_) @indent
(#not-same-line? @in @indent)
(#set! "scope" "all")
)

@ -1,22 +1,5 @@
[ (type) @type
"f32" (type "const" @type)
"f64"
"i16"
"i32"
"i64"
"i8"
"int"
"rune"
"str"
"u16"
"u32"
"u64"
"u8"
"uint"
"uintptr"
"void"
] @type
[ [
"else" "else"
@ -36,28 +19,23 @@
"break" "break"
] @keyword.control.repeat ] @keyword.control.repeat
[ "return" @keyword.control.return
"return"
"yield"
] @keyword.control.return
[ [
"abort" "abort"
"assert" "assert"
] @keyword.control.exception ] @keyword.control.exception
[ "fn" @keyword.function
"def"
"fn"
] @keyword.function
[ [
"alloc" "alloc"
"append" "append"
"as" "as"
"bool" "bool"
"char" "case"
"const" "const"
"def"
"defer" "defer"
"delete" "delete"
"enum" "enum"
@ -68,13 +46,14 @@
"match" "match"
"nullable" "nullable"
"offset" "offset"
"size"
"static"
"struct" "struct"
"type" "type"
"union" "union"
"yield"
] @keyword ] @keyword
"static" @keyword.storage.modifier
[ [
"." "."
"!" "!"
@ -137,15 +116,17 @@
"null" "null"
"true" "true"
] @constant.builtin ] @constant.builtin
(literal "void") @constant.builtin
(string_constant) @string (string_literal) @string
(escape_sequence) @constant.character.escape (escape_sequence) @constant.character.escape
(rune_constant) @string (rune_literal) @string
(integer_constant) @constant.numeric.integer (integer_literal) @constant.numeric.integer
(floating_constant) @constant.numeric.float (floating_literal) @constant.numeric.float
(call_expression (call_expression
(postfix_expression) @function) (postfix_expression) @function)
(size_expression "size" @function.builtin)
(function_declaration (function_declaration
name: (identifier) @function) name: (identifier) @function)
@ -158,4 +139,4 @@
(fndec_attrs) @special (fndec_attrs) @special
(identifier) @variable (identifier) @variable
(struct_union_field (name)) @variable

@ -1,20 +1,19 @@
(unit) @local.scope (sub_unit) @local.scope
(function_declaration) @local.scope (function_declaration) @local.scope
(compound_expression) @local.scope
(global_binding (global_binding
(identifier) @local.definition) (identifier) @local.definition)
(constant_binding (constant_binding
(identifier) @local.definition) (identifier) @local.definition)
(type_bindings (type_binding
(identifier) @local.definition) (identifier) @local.definition)
(function_declaration (function_declaration
(prototype (identifier) @local.definition)
(parameter_list (function_declaration
(parameters (parameter (name) @local.definition))
(parameter
(name) @local.definition)))))
(identifier) @local.reference (identifier) @local.reference

@ -24,6 +24,10 @@
"ui.cursor.match" = { fg = "light-yellow", underline = { color = "light-yellow", style = "line" } } "ui.cursor.match" = { fg = "light-yellow", underline = { color = "light-yellow", style = "line" } }
"ui.cursor.primary" = { modifiers = ["reversed", "slow_blink"] } "ui.cursor.primary" = { modifiers = ["reversed", "slow_blink"] }
"ui.cursor.secondary" = { modifiers = ["reversed"] } "ui.cursor.secondary" = { modifiers = ["reversed"] }
"ui.cursorline.primary" = { underline = { color = "light-gray", style = "line" } }
"ui.cursorline.secondary" = { underline = { color = "light-gray", style = "line" } }
"ui.cursorcolumn.primary" = { bg = "gray" }
"ui.cursorcolumn.secondary" = { bg = "gray" }
"ui.virtual.ruler" = { bg = "gray" } "ui.virtual.ruler" = { bg = "gray" }
"ui.virtual.whitespace" = "gray" "ui.virtual.whitespace" = "gray"
"ui.virtual.indent-guide" = "gray" "ui.virtual.indent-guide" = "gray"

@ -77,7 +77,7 @@
"ui.selection" = { bg = "Gray 50" } # .primary "ui.selection" = { bg = "Gray 50" } # .primary
"ui.selection.primary" = { bg = "Blue 40" } "ui.selection.primary" = { bg = "Blue 40" }
"ui.cursorline" = { bg = "Gray 20" } "ui.cursorline" = { bg = "Gray 15" }
"ui.linenr" = "Gray 70" "ui.linenr" = "Gray 70"
"ui.linenr.selected" = "Gray 110" "ui.linenr.selected" = "Gray 110"
@ -121,6 +121,7 @@
"Gray 40" = "#333333" "Gray 40" = "#333333"
"Gray 30" = "#2d2d2d" "Gray 30" = "#2d2d2d"
"Gray 20" = "#292929" "Gray 20" = "#292929"
"Gray 15" = "#1F1F1F"
"Gray 10" = "#181818" "Gray 10" = "#181818"
"Black" = "#000000" "Black" = "#000000"
"Blue 110" = "#6daaf7" "Blue 110" = "#6daaf7"

@ -6,6 +6,7 @@ inherits = "gruvbox"
"ui.cursor.primary" = { modifiers = ["reversed"] } "ui.cursor.primary" = { modifiers = ["reversed"] }
"ui.cursor.match" = { bg = "bg2" } "ui.cursor.match" = { bg = "bg2" }
"ui.cursorline" = { bg = "bg1" }
[palette] [palette]
bg0 = "#fbf1c7" # main background bg0 = "#fbf1c7" # main background

@ -0,0 +1,125 @@
# Kanagawa Dragon
# Author: EricHenry
# Adaptation of https://github.com/rebelot/kanagawa.nvim
# Original author: rebelot
# All credits to the original author, the palette is taken from the README
# because of some theming differences, it's not an exact copy of the original.
inherits = "kanagawa"
## User interface
"ui.selection" = { bg = "waveBlue2" }
"ui.selection.primary" = { bg = "waveBlue1" }
"ui.background" = { fg = "dragonWhite", bg = "dragonBlack3" }
"ui.gutter" = { fg = "dragonBlack6", bg = "dragonBlack4" }
"ui.linenr" = { fg = "dragonBlack6", bg = "dragonBlack4" }
"ui.linenr.selected" = { fg = "roninYellow", modifiers = ["bold"] }
"ui.virtual" = "dragonBlack4"
"ui.virtual.ruler" = { bg = "dragonBlack5" }
"ui.virtual.inlay-hint" = "dragonGray2"
"ui.virtual.inlay-hint.parameter" = { fg = "dragonYellow", modifiers = ["dim"] }
"ui.virtual.inlay-hint.type" = { fg = "dragonAqua", modifiers = ["dim"] }
"ui.virtual.jump-label" = { fg = "dragonRed", modifiers = ["bold"] }
"ui.statusline" = { fg = "oldWhite", bg = "dragonBlack0" }
"ui.bufferline" = { fg = "oldWhite", bg = "dragonBlack0" }
"ui.bufferline.active" = { fg = "oldWhite", bg = "dragonBlack0" }
"ui.bufferline.background" = { bg = "sumiInk0" }
"ui.popup" = { fg = "oldWhite", bg = "dragonBlack0" }
"ui.window" = { fg = "sumiInk0" }
"ui.help" = { fg = "fujiWhite", bg = "sumiInk0" }
"ui.text" = "dragonWhite"
"ui.text.focus" = { fg = "dragonWhite", bg = "waveBlue1", modifiers = ["bold"] }
"ui.cursor" = { fg = "waveBlue1", bg = "waveAqua2" }
"ui.cursor.primary" = { fg = "waveBlue1", bg = "fujiWhite" }
"ui.cursor.match" = { fg = "roninYellow", modifiers = ["bold"] }
"ui.highlight" = { fg = "fujiWhite", bg = "waveBlue2" }
"ui.cursorline.primary" = { bg = "dragonBlack5" }
"ui.cursorcolumn.primary" = { bg = "dragonBlack5" }
"diagnostic.info" = { underline = { color = "dragonBlue", style = "curl" } }
"diagnostic.hint" = { underline = { color = "waveAqua1", style = "curl" } }
"diagnostic.deprecated" = { fg= "katanaGray", modifiers = ["crossed_out"] }
## Syntax highlighting
"attribute" = "waveRed"
"type" = "dragonAqua"
"type.enum.variant" = "dragonOrange"
"constructor" = "dragonTeal"
"constant" = "dragonOrange"
"constant.numeric" = "dragonPink"
"constant.character.escape" = "dragonBlue2"
"string" = "dragonGreen2"
"string.regexp" = "dragonRed"
"string.special.url" = "dragonTeal"
"string.special.symbol" = "dragonTeal"
"comment" = "dragonAsh"
"variable" = "dragonWhite"
"variable.builtin" = "dragonRed"
"variable.parameter" = "dragonGray"
"variable.other.member" = "dragonYellow"
"label" = "dragonRed"
"punctuation" = "dragonWhite"
"keyword" = "dragonViolet"
"keyword.control.return" = "dragonRed"
"keyword.control.exception" = "dragonRed"
"keyword.directive" = "dragonRed"
"operator" = "dragonRed"
"function" = "dragonBlue2"
"function.builtin" = "dragonBlue2"
"function.macro" = "dragonRed"
"tag" = "dragonYellow"
"namespace" = "dragonWhite"
"special" = "dragonYellow"
## Markup modifiers
"markup.heading.marker" = "dragonViolet"
"markup.heading.1" = { fg = "dragonOrange", modifiers = ["bold"] }
"markup.heading.2" = { fg = "dragonYellow", modifiers = ["bold"] }
"markup.heading.3" = { fg = "dragonBlue2", modifiers = ["bold"] }
"markup.heading.4" = { fg = "dragonWhite", modifiers = ["bold"] }
"markup.heading.5" = { fg = "dragonRed", modifiers = ["bold"] }
"markup.heading.6" = { fg = "dragonPink", modifiers = ["bold"] }
"markup.list.numbered" = "dragonPink"
"markup.list.unnumbered" = "dragonRed"
"markup.bold" = { modifiers = ["bold"] }
"markup.italic" = { modifiers = ["italic"] }
"markup.strikethrough" = { modifiers = ["crossed_out"] }
"markup.link.text" = "dragonTeal"
"markup.link.url" = { fg = "dragonPink", underline.style = "line" }
"markup.link.label" = "dragonBlue2"
"markup.quote" = "springViolet1"
"markup.raw" = "dragonGreen2"
[palette]
dragonBlack0 = "#0d0c0c"
dragonBlack1 = "#12120f"
dragonBlack2 = "#1D1C19"
dragonBlack3 = "#181616"
dragonBlack4 = "#282727"
dragonBlack5 = "#393836"
dragonBlack6 = "#625e5a"
dragonWhite = "#c5c9c5"
dragonGreen = "#87a987"
dragonGreen2 = "#8a9a7b"
dragonPink = "#a292a3"
dragonOrange = "#b6927b"
dragonOrange2 = "#b98d7b"
dragonGray = "#a6a69c"
dragonGray2 = "#9e9b93"
dragonGray3 = "#7a8382"
dragonBlue2 = "#8ba4b0"
dragonViolet= "#8992a7"
dragonRed = "#c4746e"
dragonAqua = "#8ea4a2"
dragonAsh = "#737c73"
dragonTeal = "#949fb5"
dragonYellow = "#c4b28a"

@ -1,4 +1,5 @@
# Author : Eric Correia <correia.eh@gmail.com> # Zed OneDark
# Author : EricHenry
"attribute" = { fg = "yellow" } "attribute" = { fg = "yellow" }
"comment" = { fg = "light-gray", modifiers = ["italic"] } "comment" = { fg = "light-gray", modifiers = ["italic"] }

@ -1,4 +1,5 @@
# Author : Eric Correia <correia.eh@gmail.com> # Zed OneLight
# Author : EricHenry
inherits = "zed_onedark" inherits = "zed_onedark"

@ -145,7 +145,7 @@
You can also type wq or write-quit to save and exit. You can also type wq or write-quit to save and exit.
Note: You can optionally enter a filepath after the w / write Note: You can optionally enter a file path after the w / write
command in order to save to that path. command in order to save to that path.
Note: If there are any unsaved changes to a file, a plus [+] Note: If there are any unsaved changes to a file, a plus [+]
will appear next to the file name in the status bar. will appear next to the file name in the status bar.
@ -1124,14 +1124,14 @@ letters! that is not good grammar. you can fix this.
= 11.1 COMMENTING A LINE = = 11.1 COMMENTING A LINE =
================================================================= =================================================================
Press Ctrl-c to comment the line under your cursor. Press Ctrl-c to comment the line under your cursor.
To uncomment the line, press Ctrl-c again. To uncomment the line, press Ctrl-c again.
1. Move your cursor to the line marked '-->' below. 1. Move your cursor to the line marked '-->' below.
2. Now comment the line marked with '-->'. 2. Now comment the line marked with '-->'.
3. Now try uncommenting the line. 3. Now try uncommenting the line.
--> Comment me please --> Comment me please
@ -1146,23 +1146,23 @@ To uncomment the line, press Ctrl-c again.
= 11.2 COMMENTING MULTIPLE LINES = = 11.2 COMMENTING MULTIPLE LINES =
================================================================= =================================================================
Using the selections and multi-cursor functionality, you can Using the selections and multi-cursor functionality, you can
comment multiple lines as long as it is under the selection or comment multiple lines as long as it is under the selection or
cursors. cursors.
1. Move your cursor to the line marked with '-->' below. 1. Move your cursor to the line marked with '-->' below.
2. Now try to select or add more cursors the other lines marked 2. Now try to select or add more cursors the other lines marked
with '-->'. with '-->'.
3. Comment those lines. 3. Comment those lines.
--> How many are you going to comment? --> How many are you going to comment?
--> Is this enough for a comment? --> Is this enough for a comment?
--> What are you doing?! --> What are you doing?!
--> Stop commenting me! --> Stop commenting me!
--> AAAAaargh!!! --> AAAAaargh!!!
Note: If there are already commented lines under selections or Note: If there are already commented lines under selections or
multiple cursors, they won't be uncommented but commented again. multiple cursors, they won't be uncommented but commented again.
================================================================= =================================================================
= CHAPTER 11 RECAP = = CHAPTER 11 RECAP =
@ -1190,20 +1190,20 @@ multiple cursors, they won't be uncommented but commented again.
= 12.1 USING MATCH MODE JUMP = = 12.1 USING MATCH MODE JUMP =
================================================================= =================================================================
To switch to match mode from normal mode, type m. This feature To switch to match mode from normal mode, type m. This feature
is particularly useful for handling bracket pairs and their is particularly useful for handling bracket pairs and their
contents. contents.
There are several actions that can be performed in match mode, There are several actions that can be performed in match mode,
as indicated by the help pop-up. To jump to a matching bracket pair, as indicated by the help pop-up. To jump to a matching bracket pair,
simply press mm. For example on the lines below (starting with simply press mm. For example on the lines below (starting with
-->), move the cursor in normal mode to (, and then press mm to jump -->), move the cursor in normal mode to (, and then press mm to jump
to the matching ). You can do the same on the line below: for example to the matching ). You can do the same on the line below: for example
move to ], and press mm to jump to [ . move to ], and press mm to jump to [ .
--> you can (jump between matching parenthesis) --> you can (jump between matching parenthesis)
--> or between matching [ square brackets ] --> or between matching [ square brackets ]
--> now { you know the drill: this works with brackets too } --> now { you know the drill: this works with brackets too }
@ -1212,41 +1212,41 @@ move to ], and press mm to jump to [ .
= 12.2 USING MATCH MODE SELECT INSIDE = = 12.2 USING MATCH MODE SELECT INSIDE =
================================================================= =================================================================
Match mode also lets you select the "inside" content between a Match mode also lets you select the "inside" content between a
pair of brackets or other delimiters. In the lines below: pair of brackets or other delimiters. In the lines below:
- move to the --> line, put your cursor in normal mode at any - move to the --> line, put your cursor in normal mode at any
location between the parenthesis, for example at 'x', and press location between the parenthesis, for example at 'x', and press
mi( or mi) to select the whole content inside the parenthesis mi( or mi) to select the whole content inside the parenthesis
(parenthesis excluded). As usual, you can then do anything you want (parenthesis excluded). As usual, you can then do anything you want
with the selection (for example, press c to change it) with the selection (for example, press c to change it)
--> outside and (inside x parenthesis) - and outside again --> outside and (inside x parenthesis) - and outside again
Test below that you can do the same with [], or {}, or with Test below that you can do the same with [], or {}, or with
nested combinations of these (this will act on the immediately nested combinations of these (this will act on the immediately
surrounding matching pair). This also works with "" and similar surrounding matching pair). This also works with "" and similar
--> test [ with square brackets ] ! --> test [ with square brackets ] !
--> try ( with nested [ pairs of ( parenthesis) and "brackets" ]) --> try ( with nested [ pairs of ( parenthesis) and "brackets" ])
================================================================= =================================================================
= 12.3 USING MATCH MODE SELECT AROUND = = 12.3 USING MATCH MODE SELECT AROUND =
================================================================= =================================================================
You can also select the "around" content, i.e. both the inside You can also select the "around" content, i.e. both the inside
content and the delimiters themselves, by using the ma select. content and the delimiters themselves, by using the ma select.
For example, move to the line under, move your cursor in normal For example, move to the line under, move your cursor in normal
mode to any position between the (), and select the content of mode to any position between the (), and select the content of
the (), including the surrounding (), by typing ma( or ma). As the (), including the surrounding (), by typing ma( or ma). As
usual, you can do anything you want with the selection, for usual, you can do anything you want with the selection, for
example delete it all with ma(d . example delete it all with ma(d .
--> you ( select x around ) to include delimiters in the select --> you ( select x around ) to include delimiters in the select
This naturally works with other delimiters too: This naturally works with other delimiters too:
--> try [ with 'square' brackets ] too! --> try [ with 'square' brackets ] too!
@ -1256,21 +1256,21 @@ This naturally works with other delimiters too:
= 12.4 USING MATCH MODE SURROUND = = 12.4 USING MATCH MODE SURROUND =
================================================================= =================================================================
The match mode can also be used to add surrounding around the The match mode can also be used to add surrounding around the
current selection. For example, move to the line below, then: current selection. For example, move to the line below, then:
* i) select the "select all of this" line segment (for example, * i) select the "select all of this" line segment (for example,
move in normal mode the cursor to the start of select, then enter move in normal mode the cursor to the start of select, then enter
selection mode with v , then select the 4 next words with 4e ), selection mode with v , then select the 4 next words with 4e ),
* ii) press ms( or ms) to surround the selection with a pair of * ii) press ms( or ms) to surround the selection with a pair of
parenthesis. parenthesis.
--> so, select all of this, and surround it with () --> so, select all of this, and surround it with ()
You can do the same with other delimiters: for example, ms' on You can do the same with other delimiters: for example, ms' on
WORD below to surround it with a pair of ''. You can try also WORD below to surround it with a pair of ''. You can try also
with adding a surrounding pair of "", or {}, or []. with adding a surrounding pair of "", or {}, or [].
--> surround this WORD ! --> surround this WORD !
@ -1278,52 +1278,52 @@ with adding a surrounding pair of "", or {}, or [].
= 12.5 USING MATCH MODE DELETE SURROUND = = 12.5 USING MATCH MODE DELETE SURROUND =
================================================================= =================================================================
You can delete surrounding pair of delimiters with the md You can delete surrounding pair of delimiters with the md
command. On the line below, move the cursor anywhere command. On the line below, move the cursor anywhere
within the pair of (), for example to the 'x', then from there, within the pair of (), for example to the 'x', then from there,
in normal mode, press md( or md) to delete the surrounding in normal mode, press md( or md) to delete the surrounding
pair of parenthesis. pair of parenthesis.
--> delete (the x pair of parenthesis) from within! --> delete (the x pair of parenthesis) from within!
You can naturally delete other kinds of surroundings: You can naturally delete other kinds of surroundings:
--> delete (nested [delimiters]): "this" will delete the nearest --> delete (nested [delimiters]): "this" will delete the nearest
matching surrounding pair. matching surrounding pair.
--> delete "layers "of" quote marks" too: this will delete the --> delete "layers "of" quote marks" too: this will delete the
nearest previous and following quote marks nearest previous and following quote marks
Trying to delete unexisting surrounding delimiters print an error Trying to delete unexisting surrounding delimiters print an error
at the bottom bar and does nothing. at the bottom bar and does nothing.
================================================================= =================================================================
= 12.6 USING MATCH MODE REPLACE SURROUND = = 12.6 USING MATCH MODE REPLACE SURROUND =
================================================================= =================================================================
You can replace surrounding pairs of delimiters with the mr You can replace surrounding pairs of delimiters with the mr
command. On the line below, move the cursor to command. On the line below, move the cursor to
anywhere within the pair of (), for example on the 'x', then in anywhere within the pair of (), for example on the 'x', then in
normal mode, press mr([ to replace the pair of () with a pair normal mode, press mr([ to replace the pair of () with a pair
of []. of [].
--> replace the (pair from x within), with something else --> replace the (pair from x within), with something else
This command will act on the closest enclosing pair, so you This command will act on the closest enclosing pair, so you
can try replacing different surrounding in the following: can try replacing different surrounding in the following:
--> some (nested surroundings [can be replaced]) --> some (nested surroundings [can be replaced])
--> this "works with 'other surroundings' too" --> this "works with 'other surroundings' too"
You can try to replace a non existing pair: this will show You can try to replace a non existing pair: this will show
an error warning at the bottom bar and do nothing. an error warning at the bottom bar and do nothing.
================================================================= =================================================================
= CHAPTER 12 RECAP = = CHAPTER 12 RECAP =
================================================================= =================================================================
You can enter the match mode with the m key; this will show the You can enter the match mode with the m key; this will show the
actions available in a popup. This will allow you to: actions available in a popup. This will allow you to:
* jump to matching pair of delimiters with mm (you must have a * jump to matching pair of delimiters with mm (you must have a
delimiter belonging to a pair under your cursor) delimiter belonging to a pair under your cursor)
* select inside a pair of delimiters surrounding your cursor * select inside a pair of delimiters surrounding your cursor
@ -1344,20 +1344,20 @@ actions available in a popup. This will allow you to:
= CHAPTER 13.1 CREATE NEW SPLIT = = CHAPTER 13.1 CREATE NEW SPLIT =
================================================================= =================================================================
In Normal mode, press Ctrl-w to open the Window menu, which displays In Normal mode, press Ctrl-w to open the Window menu, which displays
a list of available commands. a list of available commands.
To open a new empty buffer in a vertical split on the right half To open a new empty buffer in a vertical split on the right half
of your current window, use Ctrl-w nv (i.e., press Ctrl of your current window, use Ctrl-w nv (i.e., press Ctrl
and w simultaneously, then press n, followed by v). Your current and w simultaneously, then press n, followed by v). Your current
window will now split in 2 vertically. A new empty buffer split window will now split in 2 vertically. A new empty buffer split
will appear on the right half and your cursor will jump to the will appear on the right half and your cursor will jump to the
new vertical split. new vertical split.
To create a new empty buffer in a horizontal split, press To create a new empty buffer in a horizontal split, press
Ctrl-w ns. This action divides your current window into two Ctrl-w ns. This action divides your current window into two
horizontally, creates a new buffer, and moves your cursor to the horizontally, creates a new buffer, and moves your cursor to the
new horizontal split. new horizontal split.
@ -1366,33 +1366,33 @@ new horizontal split.
= CHAPTER 13.2 MOVE BETWEEN SPLITS = = CHAPTER 13.2 MOVE BETWEEN SPLITS =
================================================================= =================================================================
Use Ctrl-w k to move to the split above your current split. Use Use Ctrl-w k to move to the split above your current split. Use
Ctrl-w j to move to the split below. Use Ctrl-w h to move to Ctrl-w j to move to the split below. Use Ctrl-w h to move to
the split on the left and Ctrl-w l to move to the split on the the split on the left and Ctrl-w l to move to the split on the
right. To navigate to the next split (in the order they were right. To navigate to the next split (in the order they were
opened), press Ctrl-w w. opened), press Ctrl-w w.
You can now do whatever you want in your new buffers and splits. You can now do whatever you want in your new buffers and splits.
Once you are done with using your new buffer split, Once you are done with using your new buffer split,
you can close it with Ctrl-w q . Move to the bottom right split you can close it with Ctrl-w q . Move to the bottom right split
with Ctrl-w l then Ctrl-w j, then press Ctrl-w q to close this with Ctrl-w l then Ctrl-w j, then press Ctrl-w q to close this
specific split. specific split.
You can also close all splits except the current one with Ctrl-w o . You can also close all splits except the current one with Ctrl-w o .
Open a third vertical split with Ctrl-w nv , then move to the Open a third vertical split with Ctrl-w nv , then move to the
leftmost split with Ctrl-w h twice, then from inside the split on leftmost split with Ctrl-w h twice, then from inside the split on
the left press Ctrl-w o to close all except this split. the left press Ctrl-w o to close all except this split.
================================================================= =================================================================
= CHAPTER 13.3 SPLIT CURRENT BUFFER = = CHAPTER 13.3 SPLIT CURRENT BUFFER =
================================================================= =================================================================
Use Ctrl-w s to split the view of the current buffer horizontally Use Ctrl-w s to split the view of the current buffer horizontally
and Ctrl-w v to split it vertically with the buffer opened in both and Ctrl-w v to split it vertically with the buffer opened in both
splits. splits.
Close extra splits with Ctrl-w o to return to a single window view. Close extra splits with Ctrl-w o to return to a single window view.
@ -1410,41 +1410,41 @@ Close extra splits with Ctrl-w o to return to a single window view.
= CHAPTER 13.4 USE COMMANDS TO SPLIT = = CHAPTER 13.4 USE COMMANDS TO SPLIT =
================================================================= =================================================================
The :vsplit (or :vs for short) and :hsplit (or :hs) commands can The :vsplit (or :vs for short) and :hsplit (or :hs) commands can
also be used to split a specific buffer vertically or horizontally. also be used to split a specific buffer vertically or horizontally.
For example, enter the command: For example, enter the command:
:vs something :vs something
to open a new vertical split named "something" to the right. Here, to open a new vertical split named "something" to the right. Here,
"something" is not an existing file, so a new buffer with this name "something" is not an existing file, so a new buffer with this name
will open; however, you can replace "something" with any file name will open; however, you can replace "something" with any file name
to open it in a new buffer. Similarly, you can enter the command: to open it in a new buffer. Similarly, you can enter the command:
:hs some_more :hs some_more
to open a new buffer named "some_more" in the lower half. to open a new buffer named "some_more" in the lower half.
"some_more" could be any file or path to open this specific file "some_more" could be any file or path to open this specific file
or path instead of a new empty buffer. or path instead of a new empty buffer.
================================================================= =================================================================
= CHAPTER 13.5 SWAPPING SPLITS = = CHAPTER 13.5 SWAPPING SPLITS =
================================================================= =================================================================
Open a split on the left with :vs hello1 and then a split below Open a split on the left with :vs hello1 and then a split below
with :hs hello2. with :hs hello2.
From hello2, press Ctrl-w K to swap it with the split above. Now From hello2, press Ctrl-w K to swap it with the split above. Now
hello2 is at the top while hello1 is at the bottom. hello2 is at the top while hello1 is at the bottom.
Still from hello2, press Ctrl-w H to swap with the split on the Still from hello2, press Ctrl-w H to swap with the split on the
left: now hello2 is on the left and the tutor is on the top left: now hello2 is on the left and the tutor is on the top
right. After Ctrl-w you can use HJKL to split with the buffer right. After Ctrl-w you can use HJKL to split with the buffer
on the left / below / above / on the right. on the left / below / above / on the right.
Move back to the tutor split, and press Ctrl-w o to only keep Move back to the tutor split, and press Ctrl-w o to only keep
this split. this split.
@ -1454,21 +1454,21 @@ this split.
= CHAPTER 13.6 TRANSPOSE SPLITS = = CHAPTER 13.6 TRANSPOSE SPLITS =
================================================================= =================================================================
Open a split on the left with :vs hello1 and then a split below Open a split on the left with :vs hello1 and then a split below
with :vs hello2. with :vs hello2.
Move to the tutor split, then press Ctrl-w t to transpose the Move to the tutor split, then press Ctrl-w t to transpose the
vertical split opened from this window: now, hello1 and vertical split opened from this window: now, hello1 and
hello2 are below, rather than to the right of, the tutor. Press hello2 are below, rather than to the right of, the tutor. Press
Ctrl-w t again to transpose back. Ctrl-w t again to transpose back.
Move to the hello1 split, then press Ctrl-w t to transpose the Move to the hello1 split, then press Ctrl-w t to transpose the
horizontal split that was opened from this window: now hello2 horizontal split that was opened from this window: now hello2
is on the right, rather than below, hello1. Press Ctrl-w t to is on the right, rather than below, hello1. Press Ctrl-w t to
transpose back. transpose back.
Move back to the tutor split and press Ctrl-w o to close all but Move back to the tutor split and press Ctrl-w o to close all but
the tutor window. the tutor window.
@ -1476,21 +1476,21 @@ the tutor window.
= CHAPTER 13.7 OPEN SPLIT FROM FILEPICKER = = CHAPTER 13.7 OPEN SPLIT FROM FILEPICKER =
================================================================= =================================================================
Splits can also be opened directly from the file picker. Press Splits can also be opened directly from the file picker. Press
space f to open the file picker. From there, you can type in text space f to open the file picker. From there, you can type in text
to perform file lookup with fuzzy matching, and use the arrows to perform file lookup with fuzzy matching, and use the arrows
up and down to move the selected file (indicated by the > symbol). up and down to move the selected file (indicated by the > symbol).
If you want to exit the file picker, press Escape. If you want to exit the file picker, press Escape.
Select any file you like in the file picker. You could open it in Select any file you like in the file picker. You could open it in
the current view by pressing enter (do not do this at present). the current view by pressing enter (do not do this at present).
But you can also open it in a new split. Press Ctrl-v to open But you can also open it in a new split. Press Ctrl-v to open
the selected file in a new vertical split. Press space f again, the selected file in a new vertical split. Press space f again,
select any file you want, and press Ctrl-s to open it in a select any file you want, and press Ctrl-s to open it in a
horizontal split. horizontal split.
Move back to the tutor split, and press Ctrl-w o to close all Move back to the tutor split, and press Ctrl-w o to close all
splits except this one. splits except this one.
@ -1498,18 +1498,18 @@ splits except this one.
= CHAPTER 13 RECAP = = CHAPTER 13 RECAP =
================================================================= =================================================================
Splits can be used to display either the same buffer several times Splits can be used to display either the same buffer several times
or several buffers. To access the main windows and splits commands, or several buffers. To access the main windows and splits commands,
press Ctrl-w . You can move between splits with Ctrl-w hjkl , press Ctrl-w . You can move between splits with Ctrl-w hjkl ,
you can close a split with Ctrl-w q , and you can close all but you can close a split with Ctrl-w q , and you can close all but
the present split with Ctrl-w o . the present split with Ctrl-w o .
Splits can also be opened by using the :vs FILENAME and Splits can also be opened by using the :vs FILENAME and
:hs FILENAME commands. :hs FILENAME commands.
Splits can also be used directly from the file pickers, by using Splits can also be used directly from the file pickers, by using
Ctrl-v to open the file selected in a new vertical split, and Ctrl-v to open the file selected in a new vertical split, and
Ctrl-s in a horizontal split. Ctrl-s in a horizontal split.

Loading…
Cancel
Save