diff --git a/Cargo.lock b/Cargo.lock index 577298d8b..7c797aaca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.100" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" +checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" [[package]] name = "cfg-if" @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ "error-code", ] @@ -351,6 +351,15 @@ dependencies = [ "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]] name = "dunce" version = "1.0.4" @@ -1601,12 +1610,12 @@ dependencies = [ [[package]] name = "imara-diff" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8" +checksum = "af13c8ceb376860ff0c6a66d83a8cdd4ecd9e464da24621bbffcd02b49619434" dependencies = [ "ahash", - "hashbrown 0.12.3", + "hashbrown 0.14.5", ] [[package]] @@ -1711,9 +1720,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lsp-types" @@ -1795,6 +1804,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.15" @@ -1840,9 +1855,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open" -version = "5.1.4" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5ca541f22b1c46d4bb9801014f234758ab4297e7870b904b6a8415b980a7388" +checksum = "9d2c909a3fce3bd80efef4cd1c6c056bd9376a8fe06fcfdbebaf32cb485a7e37" dependencies = [ "is-wsl", "libc", @@ -1896,6 +1911,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.76" @@ -2098,18 +2119,18 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", @@ -2118,9 +2139,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -2358,13 +2379,16 @@ dependencies = [ [[package]] name = "time" -version = "0.3.23" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ + "deranged", "itoa", "libc", + "num-conv", "num_threads", + "powerfmt", "serde", "time-core", "time-macros", @@ -2372,16 +2396,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] diff --git a/README.md b/README.md index 3f166db1b..3b639214d 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Note: Only certain languages have indentation definitions at the moment. Check [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 diff --git a/book/src/building-from-source.md b/book/src/building-from-source.md index 666917ea4..42ed57a27 100644 --- a/book/src/building-from-source.md +++ b/book/src/building-from-source.md @@ -148,6 +148,12 @@ provided `.desktop` and icon files to their correct folders: cp contrib/Helix.desktop ~/.local/share/applications 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` file. For example, to use `kitty`: diff --git a/contrib/completion/hx.fish b/contrib/completion/hx.fish index 119776057..f05dba8dd 100644 --- a/contrib/completion/hx.fish +++ b/contrib/completion/hx.fish @@ -1,15 +1,18 @@ #!/usr/bin/env fish # 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 -l tutor -d "Loads the tutorial" -complete -c hx -l health -x -a "$langs" -d "Checks for errors in editor setup" -complete -c hx -s g -l grammar -x -a "fetch build" -d "Fetches or builds tree-sitter grammars" +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 "Fetch or build tree-sitter grammars" 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 -l vsplit -d "Splits all given files vertically into different windows" -complete -c hx -l hsplit -d "Splits all given files horizontally into different windows" -complete -c hx -s c -l config -r -d "Specifies a file to use for completion" -complete -c hx -l log -r -d "Specifies a file to write log data into" +complete -c hx -l vsplit -d "Splits all given files vertically" +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 config" +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 diff --git a/contrib/completion/hx.zsh b/contrib/completion/hx.zsh index aaad6f844..2631d2283 100644 --- a/contrib/completion/hx.zsh +++ b/contrib/completion/hx.zsh @@ -14,16 +14,18 @@ _hx() { "--health[Checks for errors in editor setup]:language:->health" \ "-g[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]" \ - "--hsplit[Splits all given files horizontally into different windows]" \ + "--vsplit[Splits all given files vertically]" \ + "--hsplit[Splits all given files horizontally]" \ "-c[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" case "$state" in 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 ;; grammar) @@ -31,4 +33,3 @@ _hx() { ;; esac } - diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 3d5d554fd..c6b87e682 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -40,7 +40,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" toml = "0.8" -imara-diff = "0.1.0" +imara-diff = "0.1.6" encoding_rs = "0.8" diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 8995da8f9..eff1fcd70 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -175,7 +175,7 @@ impl Range { /// 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 /// 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 { use std::cmp::Ordering; if changes.is_empty() { @@ -541,6 +541,8 @@ impl Selection { } /// Normalizes a `Selection`. + /// + /// Ranges are sorted by [Range::from], with overlapping ranges merged. fn normalize(mut self) -> Self { if self.len() < 2 { return self; diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index 2f5b3d0fc..ed4515fe9 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -157,8 +157,8 @@ impl Client { ) } - pub fn starting_request_args(&self) -> &Option { - &self.starting_request_args + pub fn starting_request_args(&self) -> Option<&Value> { + self.starting_request_args.as_ref() } pub async fn tcp_process( diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs index 7977c6df8..99e911544 100644 --- a/helix-loader/src/grammar.rs +++ b/helix-loader/src/grammar.rs @@ -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")?; if !recompile { @@ -568,7 +568,7 @@ fn build_tree_sitter_library( fn needs_recompile( lib_path: &Path, parser_c_path: &Path, - scanner_path: &Option, + scanner_path: Option<&PathBuf>, ) -> Result { if !lib_path.exists() { return Ok(true); diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 254625a3a..643aa9a26 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -123,7 +123,7 @@ impl Client { { client.add_workspace_folder( 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()) .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 } else { // the server doesn't support multi workspaces, we need a new client @@ -147,7 +150,7 @@ impl Client { fn add_workspace_folder( &self, root_uri: Option, - change_notifications: &Option>, + change_notifications: Option<&OneOf>, ) { // root_uri is None just means that there isn't really any LSP workspace // associated with this file. For servers that support multiple workspaces @@ -162,7 +165,7 @@ impl Client { self.workspace_folders .lock() .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 // let's assume the server will request the workspace folders itself // and that we can therefore reuse the client (but are done now) @@ -616,6 +619,9 @@ impl Client { prepare_support_default_behavior: None, honors_change_annotations: Some(false), }), + formatting: Some(lsp::DocumentFormattingClientCapabilities { + dynamic_registration: Some(false), + }), code_action: Some(lsp::CodeActionClientCapabilities { code_action_literal_support: Some(lsp::CodeActionLiteralSupport { code_action_kind: lsp::CodeActionKindLiteralSupport { diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 99f966d31..fb850f7c0 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -58,7 +58,7 @@ pulldown-cmark = { version = "0.11", default-features = false } content_inspector = "0.2.4" # opening URLs -open = "5.1.4" +open = "5.2.0" url = "2.5.2" # config diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 6d8bce9e5..0ee03705f 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1210,19 +1210,15 @@ fn goto_file_impl(cx: &mut Context, action: Action) { let (view, doc) = current_ref!(cx.editor); let text = doc.text(); let selections = doc.selection(view.id); + let primary = selections.primary(); let rel_path = doc .relative_path() .map(|path| path.parent().unwrap().to_path_buf()) .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| { #[cfg(target_os = "windows")] let valid_chars = &[ @@ -1257,29 +1253,29 @@ fn goto_file_impl(cx: &mut Context, action: Action) { .take_while(is_valid_path_char) .count(); - let path: Cow = text + let path: String = text .slice((start_pos - prefix_len)..(start_pos + postfix_len)) .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() { - Some(path) => paths.push(path.to_string()), - None => cx.editor.set_error("Couldn't get string out of path."), - }; - } + vec![path] + } else { + // 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 { - let p = sel.trim(); - if p.is_empty() { - continue; - } - - if let Ok(url) = Url::parse(p) { + if let Ok(url) = Url::parse(&sel) { open_url(cx, url, action); 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() { let picker = ui::file_picker(path.into(), &cx.editor.config()); 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); match shell_impl(shell, cmd, pipe.then(|| fragment.into())) { Ok(result) => { + let result = Tendril::from(result.trim_end()); if !pipe { shell_output = Some(result.clone()); } diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index c74d82cb8..428a7cfa9 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -9,6 +9,7 @@ use super::*; use helix_core::fuzzy::fuzzy_match; use helix_core::indent::MAX_INDENT; use helix_core::{line_ending, shellwords::Shellwords}; +use helix_lsp::LanguageServerId; use helix_view::document::{read_to_string, DEFAULT_LANGUAGE_NAME}; use helix_view::editor::{CloseError, ConfigEvent}; use serde_json::Value; @@ -339,9 +340,12 @@ fn write_impl( let path = path.map(AsRef::as_ref); 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 { doc.auto_format().map(|fmt| { let callback = make_format_callback( @@ -366,13 +370,12 @@ fn write_impl( 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(); if line_ending::get_line_ending(&text.slice(..)).is_none() { let eof = Selection::point(text.len_chars()); let insert = Transaction::insert(text, &eof, doc.line_ending.as_str().into()); - doc.apply(&insert, view.id); - doc.append_changes_to_history(view); + doc.apply(&insert, view_id); } } @@ -703,11 +706,15 @@ pub fn write_all_impl( for (doc_id, target_view) in saves { let doc = doc_mut!(cx.editor, &doc_id); + let view = view_mut!(cx.editor, target_view); 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 { doc.auto_format().map(|fmt| { let callback = make_format_callback( @@ -1370,37 +1377,51 @@ fn lsp_workspace_command( if event != PromptEvent::Validate { 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 Some((language_server_id, options)) = doc + let ls_id_commands = doc .language_servers_with_feature(LanguageServerFeature::WorkspaceCommand) - .find_map(|ls| { + .flat_map(|ls| { ls.capabilities() .execute_command_provider - .as_ref() - .map(|options| (ls.id(), options)) - }) - else { - cx.editor - .set_status("No active language servers for this document support workspace commands"); - return Ok(()); - }; + .iter() + .flat_map(|options| options.commands.iter()) + .map(|command| (ls.id(), command)) + }); if args.is_empty() { - let commands = options - .commands - .iter() - .map(|command| helix_lsp::lsp::Command { - title: command.clone(), - command: command.clone(), - arguments: None, + let commands = ls_id_commands + .map(|(ls_id, command)| { + LsIdCommand( + ls_id, + helix_lsp::lsp::Command { + title: command.clone(), + command: command.clone(), + arguments: None, + }, + ) }) .collect::>(); let callback = async move { let call: job::Callback = Callback::EditorCompositor(Box::new( move |_editor: &mut Editor, compositor: &mut Compositor| { - let picker = ui::Picker::new(commands, (), move |cx, command, _action| { - execute_lsp_command(cx.editor, language_server_id, command.clone()); - }); + let picker = ui::Picker::new( + commands, + (), + move |cx, LsIdCommand(ls_id, command), _action| { + execute_lsp_command(cx.editor, *ls_id, command.clone()); + }, + ); compositor.push(Box::new(overlaid(picker))) }, )); @@ -1409,21 +1430,32 @@ fn lsp_workspace_command( cx.jobs.callback(callback); } else { let command = args.join(" "); - if options.commands.iter().any(|c| c == &command) { - execute_lsp_command( - cx.editor, - language_server_id, - helix_lsp::lsp::Command { - title: command.clone(), - arguments: None, - command, - }, - ); - } else { - cx.editor.set_status(format!( - "`{command}` is not supported for this language server" - )); - return Ok(()); + let matches: Vec<_> = ls_id_commands + .filter(|(_ls_id, c)| *c == &command) + .collect(); + + match matches.as_slice() { + [(ls_id, _command)] => { + execute_lsp_command( + cx.editor, + *ls_id, + helix_lsp::lsp::Command { + title: command.clone(), + arguments: None, + command, + }, + ); + } + [] => { + cx.editor.set_status(format!( + "`{command}` is not supported for any language server" + )); + } + _ => { + cx.editor.set_status(format!( + "`{command}` supported by multiple language servers" + )); + } } } Ok(()) diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 6a4655fde..0a65b12b5 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -364,14 +364,16 @@ pub mod completers { } pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec { - let Some(options) = doc!(editor) + let commands = doc!(editor) .language_servers_with_feature(LanguageServerFeature::WorkspaceCommand) - .find_map(|ls| ls.capabilities().execute_command_provider.as_ref()) - else { - return vec![]; - }; - - fuzzy_match(input, &options.commands, false) + .flat_map(|ls| { + ls.capabilities() + .execute_command_provider + .iter() + .flat_map(|options| options.commands.iter()) + }); + + fuzzy_match(input, commands, false) .into_iter() .map(|(name, _)| ((0..), name.to_owned().into())) .collect() diff --git a/helix-term/tests/test/commands.rs b/helix-term/tests/test/commands.rs index 9acd6eb2e..596c064f2 100644 --- a/helix-term/tests/test/commands.rs +++ b/helix-term/tests/test/commands.rs @@ -210,13 +210,10 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> { "}, "|echo foo", indoc! {"\ - #[|foo\n]# - - #(|foo\n)# - - #(|foo\n)# - - "}, + #[|foo]# + #(|foo)# + #(|foo)#" + }, )) .await?; @@ -229,12 +226,9 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> { "}, "!echo foo", indoc! {"\ - #[|foo\n]# - lorem - #(|foo\n)# - ipsum - #(|foo\n)# - dolor + #[|foo]#lorem + #(|foo)#ipsum + #(|foo)#dolor "}, )) .await?; @@ -248,12 +242,9 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> { "}, "echo foo", indoc! {"\ - lorem#[|foo\n]# - - ipsum#(|foo\n)# - - dolor#(|foo\n)# - + lorem#[|foo]# + ipsum#(|foo)# + dolor#(|foo)# "}, )) .await?; diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index 9d6c9a73c..bc8c5b7ba 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -20,7 +20,7 @@ parking_lot = "0.12" arc-swap = { version = "1.7.1" } 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" log = "0.4" diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 5de689876..81729af09 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -53,7 +53,7 @@ parking_lot = "0.12.3" thiserror.workspace = true [target.'cfg(windows)'.dependencies] -clipboard-win = { version = "5.3", features = ["std"] } +clipboard-win = { version = "5.4", features = ["std"] } [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index a56cbc2ff..ccf2fa8c3 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1410,6 +1410,11 @@ impl Document { } 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 txn = if undo { history.undo() } else { history.redo() }; 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 { + if earlier { + self.append_changes_to_history(view); + } else if !self.changes.is_empty() { + return false; + } let txns = if earlier { self.history.get_mut().earlier(uk) } else { diff --git a/languages.toml b/languages.toml index 7ffc998b8..97ee3e54a 100644 --- a/languages.toml +++ b/languages.toml @@ -73,6 +73,7 @@ prisma-language-server = { command = "prisma-language-server", args = ["--stdio" purescript-language-server = { command = "purescript-language-server", args = ["--stdio"] } pylsp = { command = "pylsp" } pyright = { command = "pyright-langserver", args = ["--stdio"], config = {} } +basedpyright = { command = "basedpyright-langserver", args = ["--stdio"], config = {} } pylyzer = { command = "pylyzer", args = ["--server"] } qmlls = { command = "qmlls" } 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" scope = "source.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 = "#" language-servers = [ "taplo" ] indent = { tab-width = 2, unit = " " } @@ -1269,7 +1270,7 @@ source = { git = "https://github.com/ikatyang/tree-sitter-yaml", rev = "0e36bed1 name = "haskell" scope = "source.haskell" injection-regex = "hs|haskell" -file-types = ["hs", "hs-boot"] +file-types = ["hs", "hs-boot", "hsc"] roots = ["Setup.hs", "stack.yaml", "cabal.project"] comment-token = "--" block-comment-tokens = { start = "{-", end = "-}" } @@ -1693,7 +1694,7 @@ source = { git = "https://github.com/mtoohey31/tree-sitter-gitattributes", rev = [[language]] name = "git-ignore" 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" comment-token = "#" grammar = "gitignore" @@ -2054,7 +2055,7 @@ indent = { tab-width = 8, unit = "\t" } [[grammar]] 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]] name = "devicetree" @@ -2081,7 +2082,7 @@ language-servers = [ "cairo-language-server" ] [[grammar]] 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]] name = "cpon" @@ -2702,6 +2703,8 @@ file-types = [ "kube", "network", { glob = ".editorconfig" }, + { glob = ".npmrc" }, + { glob = "npmrc" }, { glob = "rclone.conf" }, "properties", "cfg", @@ -3242,7 +3245,7 @@ auto-format = true [[grammar]] 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]] name = "strace" @@ -3513,7 +3516,7 @@ scope = "source.helm" roots = ["Chart.yaml"] comment-token = "#" 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]] name = "glimmer" diff --git a/runtime/queries/bash/highlights.scm b/runtime/queries/bash/highlights.scm index 1aa35aa7c..c3e504f32 100644 --- a/runtime/queries/bash/highlights.scm +++ b/runtime/queries/bash/highlights.scm @@ -1,10 +1,15 @@ [ (string) (raw_string) + (ansi_c_string) (heredoc_body) - (heredoc_start) ] @string +[ + (heredoc_start) + (heredoc_end) +] @label + (command_name) @function (variable_name) @variable.other.member diff --git a/runtime/queries/bash/injections.scm b/runtime/queries/bash/injections.scm index 0fddb10ff..d6771e445 100644 --- a/runtime/queries/bash/injections.scm +++ b/runtime/queries/bash/injections.scm @@ -5,4 +5,7 @@ name: (command_name (word) @_command) argument: (raw_string) @injection.content (#match? @_command "^[gnm]?awk$") - (#set! injection.language "awk")) \ No newline at end of file + (#set! injection.language "awk")) + +((regex) @injection.content + (#set! injection.language "regex")) diff --git a/runtime/queries/cairo/highlights.scm b/runtime/queries/cairo/highlights.scm index d2cabd1c5..16918c141 100644 --- a/runtime/queries/cairo/highlights.scm +++ b/runtime/queries/cairo/highlights.scm @@ -95,6 +95,12 @@ ; ------- ; Keywords ; ------- + +(for_expression + "for" @keyword.control.repeat) + +"in" @keyword.control + [ "match" "if" diff --git a/runtime/queries/cairo/indents.scm b/runtime/queries/cairo/indents.scm index 35c162429..b20317ab0 100644 --- a/runtime/queries/cairo/indents.scm +++ b/runtime/queries/cairo/indents.scm @@ -115,4 +115,10 @@ (#not-same-line? @expr-start @pattern-guard) ) @indent - +(for_expression + "in" @in + . + (_) @indent + (#not-same-line? @in @indent) + (#set! "scope" "all") +) diff --git a/runtime/queries/hare/highlights.scm b/runtime/queries/hare/highlights.scm index 4b9731488..5115328d4 100644 --- a/runtime/queries/hare/highlights.scm +++ b/runtime/queries/hare/highlights.scm @@ -1,22 +1,5 @@ -[ - "f32" - "f64" - "i16" - "i32" - "i64" - "i8" - "int" - "rune" - "str" - "u16" - "u32" - "u64" - "u8" - "uint" - "uintptr" - "void" -] @type - +(type) @type +(type "const" @type) [ "else" @@ -36,28 +19,23 @@ "break" ] @keyword.control.repeat -[ - "return" - "yield" -] @keyword.control.return +"return" @keyword.control.return [ "abort" "assert" ] @keyword.control.exception -[ - "def" - "fn" -] @keyword.function +"fn" @keyword.function [ "alloc" "append" "as" "bool" - "char" + "case" "const" + "def" "defer" "delete" "enum" @@ -68,13 +46,14 @@ "match" "nullable" "offset" - "size" - "static" "struct" "type" "union" + "yield" ] @keyword +"static" @keyword.storage.modifier + [ "." "!" @@ -137,15 +116,17 @@ "null" "true" ] @constant.builtin +(literal "void") @constant.builtin -(string_constant) @string +(string_literal) @string (escape_sequence) @constant.character.escape -(rune_constant) @string -(integer_constant) @constant.numeric.integer -(floating_constant) @constant.numeric.float +(rune_literal) @string +(integer_literal) @constant.numeric.integer +(floating_literal) @constant.numeric.float (call_expression (postfix_expression) @function) +(size_expression "size" @function.builtin) (function_declaration name: (identifier) @function) @@ -158,4 +139,4 @@ (fndec_attrs) @special (identifier) @variable - +(struct_union_field (name)) @variable diff --git a/runtime/queries/hare/locals.scm b/runtime/queries/hare/locals.scm index b8b9b9f71..b9e0a91b5 100644 --- a/runtime/queries/hare/locals.scm +++ b/runtime/queries/hare/locals.scm @@ -1,20 +1,19 @@ -(unit) @local.scope +(sub_unit) @local.scope (function_declaration) @local.scope +(compound_expression) @local.scope (global_binding (identifier) @local.definition) -(constant_binding +(constant_binding (identifier) @local.definition) -(type_bindings +(type_binding (identifier) @local.definition) (function_declaration - (prototype - (parameter_list - (parameters - (parameter - (name) @local.definition))))) + (identifier) @local.definition) +(function_declaration + (parameter (name) @local.definition)) (identifier) @local.reference diff --git a/runtime/themes/base16_transparent.toml b/runtime/themes/base16_transparent.toml index 70452366f..d5786f89e 100644 --- a/runtime/themes/base16_transparent.toml +++ b/runtime/themes/base16_transparent.toml @@ -24,6 +24,10 @@ "ui.cursor.match" = { fg = "light-yellow", underline = { color = "light-yellow", style = "line" } } "ui.cursor.primary" = { modifiers = ["reversed", "slow_blink"] } "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.whitespace" = "gray" "ui.virtual.indent-guide" = "gray" diff --git a/runtime/themes/fleet_dark.toml b/runtime/themes/fleet_dark.toml index 7fd26c1f8..b8f28d1f9 100644 --- a/runtime/themes/fleet_dark.toml +++ b/runtime/themes/fleet_dark.toml @@ -77,7 +77,7 @@ "ui.selection" = { bg = "Gray 50" } # .primary "ui.selection.primary" = { bg = "Blue 40" } -"ui.cursorline" = { bg = "Gray 20" } +"ui.cursorline" = { bg = "Gray 15" } "ui.linenr" = "Gray 70" "ui.linenr.selected" = "Gray 110" @@ -121,6 +121,7 @@ "Gray 40" = "#333333" "Gray 30" = "#2d2d2d" "Gray 20" = "#292929" +"Gray 15" = "#1F1F1F" "Gray 10" = "#181818" "Black" = "#000000" "Blue 110" = "#6daaf7" diff --git a/runtime/themes/gruvbox_light.toml b/runtime/themes/gruvbox_light.toml index 59ce99011..ba216020b 100644 --- a/runtime/themes/gruvbox_light.toml +++ b/runtime/themes/gruvbox_light.toml @@ -6,6 +6,7 @@ inherits = "gruvbox" "ui.cursor.primary" = { modifiers = ["reversed"] } "ui.cursor.match" = { bg = "bg2" } +"ui.cursorline" = { bg = "bg1" } [palette] bg0 = "#fbf1c7" # main background diff --git a/runtime/themes/kanagawa-dragon.toml b/runtime/themes/kanagawa-dragon.toml new file mode 100644 index 000000000..e775f2815 --- /dev/null +++ b/runtime/themes/kanagawa-dragon.toml @@ -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" diff --git a/runtime/themes/zed_onedark.toml b/runtime/themes/zed_onedark.toml index 3eeabab30..74db8f75c 100644 --- a/runtime/themes/zed_onedark.toml +++ b/runtime/themes/zed_onedark.toml @@ -1,4 +1,5 @@ -# Author : Eric Correia +# Zed OneDark +# Author : EricHenry "attribute" = { fg = "yellow" } "comment" = { fg = "light-gray", modifiers = ["italic"] } diff --git a/runtime/themes/zed_onelight.toml b/runtime/themes/zed_onelight.toml index 086fce34b..936366250 100644 --- a/runtime/themes/zed_onelight.toml +++ b/runtime/themes/zed_onelight.toml @@ -1,4 +1,5 @@ -# Author : Eric Correia +# Zed OneLight +# Author : EricHenry inherits = "zed_onedark" diff --git a/runtime/tutor b/runtime/tutor index 055718379..505f88482 100644 --- a/runtime/tutor +++ b/runtime/tutor @@ -145,7 +145,7 @@ 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. Note: If there are any unsaved changes to a file, a plus [+] 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 = ================================================================= -Press Ctrl-c to comment the line under your cursor. -To uncomment the line, press Ctrl-c again. + Press Ctrl-c to comment the line under your cursor. + To uncomment the line, press Ctrl-c again. -1. Move your cursor to the line marked '-->' below. -2. Now comment the line marked with '-->'. -3. Now try uncommenting the line. + 1. Move your cursor to the line marked '-->' below. + 2. Now comment the line marked with '-->'. + 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 = ================================================================= -Using the selections and multi-cursor functionality, you can -comment multiple lines as long as it is under the selection or -cursors. + Using the selections and multi-cursor functionality, you can + comment multiple lines as long as it is under the selection or + cursors. -1. Move your cursor to the line marked with '-->' below. -2. Now try to select or add more cursors the other lines marked - with '-->'. -3. Comment those lines. + 1. Move your cursor to the line marked with '-->' below. + 2. Now try to select or add more cursors the other lines marked + with '-->'. + 3. Comment those lines. ---> How many are you going to comment? ---> Is this enough for a comment? ---> What are you doing?! ---> Stop commenting me! ---> AAAAaargh!!! + --> How many are you going to comment? + --> Is this enough for a comment? + --> What are you doing?! + --> Stop commenting me! + --> AAAAaargh!!! -Note: If there are already commented lines under selections or -multiple cursors, they won't be uncommented but commented again. + Note: If there are already commented lines under selections or + multiple cursors, they won't be uncommented but commented again. ================================================================= = CHAPTER 11 RECAP = @@ -1190,20 +1190,20 @@ multiple cursors, they won't be uncommented but commented again. = 12.1 USING MATCH MODE JUMP = ================================================================= -To switch to match mode from normal mode, type m. This feature -is particularly useful for handling bracket pairs and their -contents. + To switch to match mode from normal mode, type m. This feature + is particularly useful for handling bracket pairs and their + contents. -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, -simply press mm. For example on the lines below (starting with --->), 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 -move to ], and press mm to jump to [ . + 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, + simply press mm. For example on the lines below (starting with + -->), 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 + move to ], and press mm to jump to [ . ---> you can (jump between matching parenthesis) ---> or between matching [ square brackets ] ---> now { you know the drill: this works with brackets too } + --> you can (jump between matching parenthesis) + --> or between matching [ square brackets ] + --> 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 = ================================================================= -Match mode also lets you select the "inside" content between a -pair of brackets or other delimiters. In the lines below: + Match mode also lets you select the "inside" content between a + pair of brackets or other delimiters. In the lines below: -- move to the --> line, put your cursor in normal mode at any -location between the parenthesis, for example at 'x', and press -mi( or mi) to select the whole content inside the parenthesis -(parenthesis excluded). As usual, you can then do anything you want -with the selection (for example, press c to change it) + - move to the --> line, put your cursor in normal mode at any + location between the parenthesis, for example at 'x', and press + mi( or mi) to select the whole content inside the parenthesis + (parenthesis excluded). As usual, you can then do anything you want + 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 -nested combinations of these (this will act on the immediately -surrounding matching pair). This also works with "" and similar + Test below that you can do the same with [], or {}, or with + nested combinations of these (this will act on the immediately + surrounding matching pair). This also works with "" and similar ---> test [ with square brackets ] ! ---> try ( with nested [ pairs of ( parenthesis) and "brackets" ]) + --> test [ with square brackets ] ! + --> try ( with nested [ pairs of ( parenthesis) and "brackets" ]) ================================================================= = 12.3 USING MATCH MODE SELECT AROUND = ================================================================= -You can also select the "around" content, i.e. both the inside -content and the delimiters themselves, by using the ma select. -For example, move to the line under, move your cursor in normal -mode to any position between the (), and select the content of -the (), including the surrounding (), by typing ma( or ma). As -usual, you can do anything you want with the selection, for -example delete it all with ma(d . + You can also select the "around" content, i.e. both the inside + content and the delimiters themselves, by using the ma select. + For example, move to the line under, move your cursor in normal + mode to any position between the (), and select the content of + the (), including the surrounding (), by typing ma( or ma). As + usual, you can do anything you want with the selection, for + 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 = ================================================================= -The match mode can also be used to add surrounding around the -current selection. For example, move to the line below, then: - * i) select the "select all of this" line segment (for example, -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 ), - * ii) press ms( or ms) to surround the selection with a pair of -parenthesis. + The match mode can also be used to add surrounding around the + current selection. For example, move to the line below, then: + * i) select the "select all of this" line segment (for example, + 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 ), + * ii) press ms( or ms) to surround the selection with a pair of + 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 -WORD below to surround it with a pair of ''. You can try also -with adding a surrounding pair of "", or {}, or []. + 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 + with adding a surrounding pair of "", or {}, or []. ---> surround this WORD ! + --> surround this WORD ! @@ -1278,64 +1278,64 @@ with adding a surrounding pair of "", or {}, or []. = 12.5 USING MATCH MODE DELETE SURROUND = ================================================================= -You can delete surrounding pair of delimiters with the md -command. On the line below, move the cursor anywhere -within the pair of (), for example to the 'x', then from there, -in normal mode, press md( or md) to delete the surrounding -pair of parenthesis. + You can delete surrounding pair of delimiters with the md + command. On the line below, move the cursor anywhere + within the pair of (), for example to the 'x', then from there, + in normal mode, press md( or md) to delete the surrounding + 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 -matching surrounding pair. ---> delete "layers "of" quote marks" too: this will delete the -nearest previous and following quote marks + --> delete (nested [delimiters]): "this" will delete the nearest + matching surrounding pair. + --> delete "layers "of" quote marks" too: this will delete the + nearest previous and following quote marks -Trying to delete unexisting surrounding delimiters print an error -at the bottom bar and does nothing. + Trying to delete unexisting surrounding delimiters print an error + at the bottom bar and does nothing. ================================================================= = 12.6 USING MATCH MODE REPLACE SURROUND = ================================================================= -You can replace surrounding pairs of delimiters with the mr -command. On the line below, move the cursor to -anywhere within the pair of (), for example on the 'x', then in -normal mode, press mr([ to replace the pair of () with a pair -of []. + You can replace surrounding pairs of delimiters with the mr + command. On the line below, move the cursor to + anywhere within the pair of (), for example on the 'x', then in + normal mode, press mr([ to replace the pair of () with a pair + 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 -can try replacing different surrounding in the following: + This command will act on the closest enclosing pair, so you + can try replacing different surrounding in the following: ---> some (nested surroundings [can be replaced]) ---> this "works with 'other surroundings' too" + --> some (nested surroundings [can be replaced]) + --> this "works with 'other surroundings' too" -You can try to replace a non existing pair: this will show -an error warning at the bottom bar and do nothing. + You can try to replace a non existing pair: this will show + an error warning at the bottom bar and do nothing. ================================================================= = CHAPTER 12 RECAP = ================================================================= -You can enter the match mode with the m key; this will show the -actions available in a popup. This will allow you to: - * jump to matching pair of delimiters with mm (you must have a - delimiter belonging to a pair under your cursor) - * select inside a pair of delimiters surrounding your cursor - (i.e. select the content but not the delimiters) with mi( - and similar - * select around a pair of delimiters surrounding your cursor - (i.e. select the content and the delimiters) with ma( and - similar - * delete surrounding delimiters with md( and similar - * add surrounding delimiters around the selection with ms( - * replace a pair of delimiters surrounding your selection with - mr([ to replace for example surrounding () with [] + You can enter the match mode with the m key; this will show the + actions available in a popup. This will allow you to: + * jump to matching pair of delimiters with mm (you must have a + delimiter belonging to a pair under your cursor) + * select inside a pair of delimiters surrounding your cursor + (i.e. select the content but not the delimiters) with mi( + and similar + * select around a pair of delimiters surrounding your cursor + (i.e. select the content and the delimiters) with ma( and + similar + * delete surrounding delimiters with md( and similar + * add surrounding delimiters around the selection with ms( + * replace a pair of delimiters surrounding your selection with + mr([ to replace for example surrounding () with [] @@ -1344,20 +1344,20 @@ actions available in a popup. This will allow you to: = CHAPTER 13.1 CREATE NEW SPLIT = ================================================================= -In Normal mode, press Ctrl-w to open the Window menu, which displays -a list of available commands. + In Normal mode, press Ctrl-w to open the Window menu, which displays + a list of available commands. -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 -and w simultaneously, then press n, followed by v). Your current -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 -new vertical split. + 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 + and w simultaneously, then press n, followed by v). Your current + 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 + new vertical split. -To create a new empty buffer in a horizontal split, press -Ctrl-w ns. This action divides your current window into two -horizontally, creates a new buffer, and moves your cursor to the -new horizontal split. + To create a new empty buffer in a horizontal split, press + Ctrl-w ns. This action divides your current window into two + horizontally, creates a new buffer, and moves your cursor to the + new horizontal split. @@ -1366,33 +1366,33 @@ new horizontal split. = CHAPTER 13.2 MOVE BETWEEN SPLITS = ================================================================= -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 -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 -opened), press Ctrl-w w. + 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 + 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 + opened), press Ctrl-w w. -You can now do whatever you want in your new buffers and splits. -Once you are done with using your new buffer 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 -specific split. + You can now do whatever you want in your new buffers and splits. + Once you are done with using your new buffer 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 + specific split. -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 -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. + 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 + 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. ================================================================= = CHAPTER 13.3 SPLIT CURRENT BUFFER = ================================================================= -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 -splits. + 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 + 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 = ================================================================= -The :vsplit (or :vs for short) and :hsplit (or :hs) commands can -also be used to split a specific buffer vertically or horizontally. -For example, enter the command: + The :vsplit (or :vs for short) and :hsplit (or :hs) commands can + also be used to split a specific buffer vertically or horizontally. + For example, enter the command: -:vs something + :vs something -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 -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 a new vertical split named "something" to the right. Here, + "something" is not an existing file, so a new buffer with this 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: -:hs some_more + :hs some_more -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 -or path instead of a new empty buffer. + 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 + or path instead of a new empty buffer. ================================================================= = CHAPTER 13.5 SWAPPING SPLITS = ================================================================= -Open a split on the left with :vs hello1 and then a split below -with :hs hello2. + Open a split on the left with :vs hello1 and then a split below + with :hs hello2. -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. + 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. -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 -right. After Ctrl-w you can use HJKL to split with the buffer -on the left / below / above / on the right. + 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 + right. After Ctrl-w you can use HJKL to split with the buffer + on the left / below / above / on the right. -Move back to the tutor split, and press Ctrl-w o to only keep -this split. + Move back to the tutor split, and press Ctrl-w o to only keep + this split. @@ -1454,21 +1454,21 @@ this split. = CHAPTER 13.6 TRANSPOSE SPLITS = ================================================================= -Open a split on the left with :vs hello1 and then a split below -with :vs hello2. + Open a split on the left with :vs hello1 and then a split below + with :vs hello2. -Move to the tutor split, then press Ctrl-w t to transpose the -vertical split opened from this window: now, hello1 and -hello2 are below, rather than to the right of, the tutor. Press -Ctrl-w t again to transpose back. + Move to the tutor split, then press Ctrl-w t to transpose the + vertical split opened from this window: now, hello1 and + hello2 are below, rather than to the right of, the tutor. Press + Ctrl-w t again to transpose back. -Move to the hello1 split, then press Ctrl-w t to transpose the -horizontal split that was opened from this window: now hello2 -is on the right, rather than below, hello1. Press Ctrl-w t to -transpose back. + Move to the hello1 split, then press Ctrl-w t to transpose the + horizontal split that was opened from this window: now hello2 + is on the right, rather than below, hello1. Press Ctrl-w t to + transpose back. -Move back to the tutor split and press Ctrl-w o to close all but -the tutor window. + Move back to the tutor split and press Ctrl-w o to close all but + the tutor window. @@ -1476,21 +1476,21 @@ the tutor window. = CHAPTER 13.7 OPEN SPLIT FROM FILEPICKER = ================================================================= -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 -to perform file lookup with fuzzy matching, and use the arrows -up and down to move the selected file (indicated by the > symbol). -If you want to exit the file picker, press Escape. + 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 + to perform file lookup with fuzzy matching, and use the arrows + up and down to move the selected file (indicated by the > symbol). + If you want to exit the file picker, press Escape. -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). -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, -select any file you want, and press Ctrl-s to open it in a -horizontal split. + 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). + 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, + select any file you want, and press Ctrl-s to open it in a + horizontal split. -Move back to the tutor split, and press Ctrl-w o to close all -splits except this one. + Move back to the tutor split, and press Ctrl-w o to close all + splits except this one. @@ -1498,18 +1498,18 @@ splits except this one. = CHAPTER 13 RECAP = ================================================================= -Splits can be used to display either the same buffer several times -or several buffers. To access the main windows and splits commands, -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 -the present split with Ctrl-w o . + Splits can be used to display either the same buffer several times + or several buffers. To access the main windows and splits commands, + 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 + the present split with Ctrl-w o . -Splits can also be opened by using the :vs FILENAME and -:hs FILENAME commands. + Splits can also be opened by using the :vs FILENAME and + :hs FILENAME commands. -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-s in a horizontal split. + 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-s in a horizontal split.