@@ -5,7 +5,10 @@ use crate::job::Job;
55
66use super::*;
77
8−use helix_core::{encoding, shellwords::Shellwords};
8+use helix_core::fuzzy::fuzzy_match;
9+use helix_core::indent::MAX_INDENT;
10+use helix_core::{encoding, line_ending, path::get_canonicalized_path, shellwords::Shellwords};
11+use helix_lsp::{OffsetEncoding, Url};
912use helix_view::document::DEFAULT_LANGUAGE_NAME;
1013use helix_view::editor::{Action, CloseError, ConfigEvent};
1114use serde_json::Value;
@@ -329,12 +332,16 @@ fn write_impl(
329332 path: Option<&Cow<str>>,
330333 force: bool,
331334) -> anyhow::Result<()> {
332− let editor_auto_fmt = cx.editor.config().auto_format;
335+ let config = cx.editor.config();
333336 let jobs = &mut cx.jobs;
334337 let (view, doc) = current!(cx.editor);
335338 let path = path.map(AsRef::as_ref);
336339
337− let fmt = if editor_auto_fmt {
340+ if config.insert_final_newline {
341+ insert_final_newline(doc, view);
342+ }
343+
344+ let fmt = if config.auto_format {
338345 doc.auto_format().map(|fmt| {
339346 let callback = make_format_callback(
340347 doc.id(),
@@ -358,6 +365,16 @@ fn write_impl(
358365 Ok(())
359366}
360367
368+fn insert_final_newline(doc: &mut Document, view: &mut View) {
369+ let text = doc.text();
370+ if line_ending::get_line_ending(&text.slice(..)).is_none() {
371+ let eof = Selection::point(text.len_chars());
372+ let insert = Transaction::insert(text, &eof, doc.line_ending.as_str().into());
373+ doc.apply(&insert, view.id);
374+ doc.append_changes_to_history(view);
375+ }
376+}
377+
361378fn write(
362379 cx: &mut compositor::Context,
363380 args: &[Cow<str>],
@@ -460,8 +477,7 @@ fn set_indent_style(
460477 cx.editor.set_status(match style {
461478 Tabs => "tabs".to_owned(),
462479 Spaces(1) => "1 space".to_owned(),
463− Spaces(n) if (2..=8).contains(&n) => format!("{} spaces", n),
464− _ => unreachable!(), // Shouldn't happen.
480+ Spaces(n) => format!("{} spaces", n),
465481 });
466482 return Ok(());
467483 }
@@ -473,7 +489,7 @@ fn set_indent_style(
473489 Some(arg) => arg
474490 .parse::<u8>()
475491 .ok()
476− .filter(|n| (1..=8).contains(n))
492+ .filter(|n| (1..=MAX_INDENT).contains(n))
477493 .map(Spaces),
478494 _ => None,
479495 };
@@ -558,7 +574,6 @@ fn set_line_ending(
558574
559575 Ok(())
560576}
561−
562577fn earlier(
563578 cx: &mut compositor::Context,
564579 args: &[Cow<str>],
@@ -657,11 +672,10 @@ pub fn write_all_impl(
657672 write_scratch: bool,
658673) -> anyhow::Result<()> {
659674 let mut errors: Vec<&'static str> = Vec::new();
660− let auto_format = cx.editor.config().auto_format;
675+ let config = cx.editor.config();
661676 let jobs = &mut cx.jobs;
662677 let current_view = view!(cx.editor);
663678
664− // save all documents
665679 let saves: Vec<_> = cx
666680 .editor
667681 .documents
@@ -672,7 +686,7 @@ pub fn write_all_impl(
672686 }
673687 if doc.path().is_none() {
674688 if write_scratch {
675− errors.push("cannot write a buffer without a filename\n");
689+ errors.push("cannot write a buffer without a filename");
676690 }
677691 return None;
678692 }
@@ -692,32 +706,35 @@ pub fn write_all_impl(
692706 current_view.id
693707 };
694708
695− let fmt = if auto_format {
696− doc.auto_format().map(|fmt| {
697− let callback = make_format_callback(
698− doc.id(),
699− doc.version(),
700− target_view,
701− fmt,
702− Some((None, force)),
703− );
704− jobs.add(Job::with_callback(callback).wait_before_exiting());
705− })
706− } else {
707− None
708− };
709+ Some((doc.id(), target_view))
710+ })
711+ .collect();
709712
710− if fmt.is_none() {
711− return Some(doc.id());
712− }
713+ for (doc_id, target_view) in saves {
714+ let doc = doc_mut!(cx.editor, &doc_id);
715+
716+ if config.insert_final_newline {
717+ insert_final_newline(doc, view_mut!(cx.editor, target_view));
718+ }
713719
720+ let fmt = if config.auto_format {
721+ doc.auto_format().map(|fmt| {
722+ let callback = make_format_callback(
723+ doc_id,
724+ doc.version(),
725+ target_view,
726+ fmt,
727+ Some((None, force)),
728+ );
729+ jobs.add(Job::with_callback(callback).wait_before_exiting());
730+ })
731+ } else {
714732 None
715− })
716− .collect();
733+ };
717734
718− // manually call save for the rest of docs that don't have a formatter
719− for id in saves {
720− cx.editor.save::<PathBuf>(id, None, force)?;
735+ if fmt.is_none() {
736+ cx.editor.save::<PathBuf>(doc_id, None, force)?;
737+ }
721738 }
722739
723740 if !errors.is_empty() && !force {
@@ -904,7 +921,8 @@ fn yank_main_selection_to_clipboard(
904921 return Ok(());
905922 }
906923
907− yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard)
924+ yank_primary_selection_impl(cx.editor, '+');
925+ Ok(())
908926}
909927
910928fn yank_joined(
@@ -938,7 +956,8 @@ fn yank_joined_to_clipboard(
938956 let doc = doc!(cx.editor);
939957 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
940958 let separator = args.first().unwrap_or(&default_sep);
941− yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Clipboard)
959+ yank_joined_impl(cx.editor, separator, '+');
960+ Ok(())
942961}
943962
944963fn yank_main_selection_to_primary_clipboard(
@@ -950,7 +969,8 @@ fn yank_main_selection_to_primary_clipboard(
950969 return Ok(());
951970 }
952971
953− yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection)
972+ yank_primary_selection_impl(cx.editor, '*');
973+ Ok(())
954974}
955975
956976fn yank_joined_to_primary_clipboard(
@@ -965,7 +985,8 @@ fn yank_joined_to_primary_clipboard(
965985 let doc = doc!(cx.editor);
966986 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
967987 let separator = args.first().unwrap_or(&default_sep);
968− yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Selection)
988+ yank_joined_impl(cx.editor, separator, '*');
989+ Ok(())
969990}
970991
971992fn paste_clipboard_after(
@@ -977,7 +998,8 @@ fn paste_clipboard_after(
977998 return Ok(());
978999 }
9791000
980− paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1)
1001+ paste(cx.editor, '+', Paste::After, 1);
1002+ Ok(())
9811003}
9821004
9831005fn paste_clipboard_before(
@@ -989,7 +1011,8 @@ fn paste_clipboard_before(
9891011 return Ok(());
9901012 }
9911013
992− paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1)
1014+ paste(cx.editor, '+', Paste::Before, 1);
1015+ Ok(())
9931016}
9941017
9951018fn paste_primary_clipboard_after(
@@ -1001,7 +1024,8 @@ fn paste_primary_clipboard_after(
10011024 return Ok(());
10021025 }
10031026
1004− paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1)
1027+ paste(cx.editor, '*', Paste::After, 1);
1028+ Ok(())
10051029}
10061030
10071031fn paste_primary_clipboard_before(
@@ -1013,30 +1037,8 @@ fn paste_primary_clipboard_before(
10131037 return Ok(());
10141038 }
10151039
1016− paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Selection, 1)
1017−}
1018−
1019−fn replace_selections_with_clipboard_impl(
1020− cx: &mut compositor::Context,
1021− clipboard_type: ClipboardType,
1022−) -> anyhow::Result<()> {
1023− let scrolloff = cx.editor.config().scrolloff;
1024− let (view, doc) = current!(cx.editor);
1025−
1026− match cx.editor.clipboard_provider.get_contents(clipboard_type) {
1027− Ok(contents) => {
1028− let selection = doc.selection(view.id);
1029− let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
1030− (range.from(), range.to(), Some(contents.as_str().into()))
1031− });
1032−
1033− doc.apply(&transaction, view.id);
1034− doc.append_changes_to_history(view);
1035− view.ensure_cursor_in_view(doc, scrolloff);
1036− Ok(())
1037− }
1038− Err(e) => Err(e.context("Couldn't get system clipboard contents")),
1039− }
1040+ paste(cx.editor, '*', Paste::Before, 1);
1041+ Ok(())
10401042}
10411043
10421044fn replace_selections_with_clipboard(
@@ -1048,7 +1050,8 @@ fn replace_selections_with_clipboard(
10481050 return Ok(());
10491051 }
10501052
1051− replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
1053+ replace_with_yanked_impl(cx.editor, '+', 1);
1054+ Ok(())
10521055}
10531056
10541057fn replace_selections_with_primary_clipboard(
@@ -1060,7 +1063,8 @@ fn replace_selections_with_primary_clipboard(
10601063 return Ok(());
10611064 }
10621065
1063− replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
1066+ replace_with_yanked_impl(cx.editor, '*', 1);
1067+ Ok(())
10641068}
10651069
10661070fn show_clipboard_provider(
@@ -1073,7 +1077,7 @@ fn show_clipboard_provider(
10731077 }
10741078
10751079 cx.editor
1076− .set_status(cx.editor.clipboard_provider.name().to_string());
1080+ .set_status(cx.editor.registers.clipboard_provider_name().to_string());
10771081 Ok(())
10781082}
10791083
@@ -1093,14 +1097,11 @@ fn change_current_directory(
10931097 .as_ref(),
10941098 );
10951099
1096− if let Err(e) = std::env::set_current_dir(dir) {
1097− bail!("Couldn't change the current working directory: {}", e);
1098− }
1100+ helix_loader::set_current_working_dir(dir)?;
10991101
1100− let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
11011102 cx.editor.set_status(format!(
11021103 "Current working directory is now {}",
1103− cwd.display()
1104+ helix_loader::current_working_dir().display()
11041105 ));
11051106 Ok(())
11061107}
@@ -1114,9 +1115,14 @@ fn show_current_directory(
11141115 return Ok(());
11151116 }
11161117
1117− let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
1118− cx.editor
1119− .set_status(format!("Current working directory is {}", cwd.display()));
1118+ let cwd = helix_loader::current_working_dir();
1119+ let message = format!("Current working directory is {}", cwd.display());
1120+
1121+ if cwd.exists() {
1122+ cx.editor.set_status(message);
1123+ } else {
1124+ cx.editor.set_error(format!("{} (deleted)", message));
1125+ }
11201126 Ok(())
11211127}
11221128
@@ -1276,12 +1282,17 @@ fn reload(
12761282 }
12771283
12781284 let scrolloff = cx.editor.config().scrolloff;
1279− let redraw_handle = cx.editor.redraw_handle.clone();
12801285 let (view, doc) = current!(cx.editor);
1281− doc.reload(view, &cx.editor.diff_providers, redraw_handle)
1282− .map(|_| {
1283− view.ensure_cursor_in_view(doc, scrolloff);
1284− })
1286+ doc.reload(view, &cx.editor.diff_providers).map(|_| {
1287+ view.ensure_cursor_in_view(doc, scrolloff);
1288+ })?;
1289+ if let Some(path) = doc.path() {
1290+ cx.editor
1291+ .language_servers
1292+ .file_event_handler
1293+ .file_changed(path.clone());
1294+ }
1295+ Ok(())
12851296}
12861297
12871298fn reload_all(
@@ -1320,8 +1331,13 @@ fn reload_all(
13201331 // Ensure that the view is synced with the document's history.
13211332 view.sync_changes(doc);
13221333
1323− let redraw_handle = cx.editor.redraw_handle.clone();
1324− doc.reload(view, &cx.editor.diff_providers, redraw_handle)?;
1334+ doc.reload(view, &cx.editor.diff_providers)?;
1335+ if let Some(path) = doc.path() {
1336+ cx.editor
1337+ .language_servers
1338+ .file_event_handler
1339+ .file_changed(path.clone());
1340+ }
13251341
13261342 for view_id in view_ids {
13271343 let view = view_mut!(cx.editor, view_id);
@@ -1370,9 +1386,8 @@ fn lsp_workspace_command(
13701386 .map(|options| (ls.id(), options))
13711387 })
13721388 else {
1373− cx.editor.set_status(
1374− "No active language servers for this document support workspace commands",
1375− );
1389+ cx.editor
1390+ .set_status("No active language servers for this document support workspace commands");
13761391 return Ok(());
13771392 };
13781393
@@ -1528,6 +1543,84 @@ fn tree_sitter_scopes(
15281543 Ok(())
15291544}
15301545
1546+fn tree_sitter_highlight_name(
1547+ cx: &mut compositor::Context,
1548+ _args: &[Cow<str>],
1549+ event: PromptEvent,
1550+) -> anyhow::Result<()> {
1551+ fn find_highlight_at_cursor(
1552+ cx: &mut compositor::Context<'_>,
1553+ ) -> Option<helix_core::syntax::Highlight> {
1554+ use helix_core::syntax::HighlightEvent;
1555+
1556+ let (view, doc) = current!(cx.editor);
1557+ let syntax = doc.syntax()?;
1558+ let text = doc.text().slice(..);
1559+ let cursor = doc.selection(view.id).primary().cursor(text);
1560+ let byte = text.char_to_byte(cursor);
1561+ let node = syntax
1562+ .tree()
1563+ .root_node()
1564+ .descendant_for_byte_range(byte, byte)?;
1565+ // Query the same range as the one used in syntax highlighting.
1566+ let range = {
1567+ // Calculate viewport byte ranges:
1568+ let row = text.char_to_line(view.offset.anchor.min(text.len_chars()));
1569+ // Saturating subs to make it inclusive zero indexing.
1570+ let last_line = text.len_lines().saturating_sub(1);
1571+ let height = view.inner_area(doc).height;
1572+ let last_visible_line = (row + height as usize).saturating_sub(1).min(last_line);
1573+ let start = text.line_to_byte(row.min(last_line));
1574+ let end = text.line_to_byte(last_visible_line + 1);
1575+
1576+ start..end
1577+ };
1578+
1579+ let mut highlight = None;
1580+
1581+ for event in syntax.highlight_iter(text, Some(range), None) {
1582+ match event.unwrap() {
1583+ HighlightEvent::Source { start, end }
1584+ if start == node.start_byte() && end == node.end_byte() =>
1585+ {
1586+ return highlight;
1587+ }
1588+ HighlightEvent::HighlightStart(hl) => {
1589+ highlight = Some(hl);
1590+ }
1591+ _ => (),
1592+ }
1593+ }
1594+
1595+ None
1596+ }
1597+
1598+ if event != PromptEvent::Validate {
1599+ return Ok(());
1600+ }
1601+
1602+ let Some(highlight) = find_highlight_at_cursor(cx) else {
1603+ return Ok(());
1604+ };
1605+
1606+ let content = cx.editor.theme.scope(highlight.0).to_string();
1607+
1608+ let callback = async move {
1609+ let call: job::Callback = Callback::EditorCompositor(Box::new(
1610+ move |editor: &mut Editor, compositor: &mut Compositor| {
1611+ let content = ui::Markdown::new(content, editor.syn_loader.clone());
1612+ let popup = Popup::new("hover", content).auto_close(true);
1613+ compositor.replace_or_push("hover", popup);
1614+ },
1615+ ));
1616+ Ok(call)
1617+ };
1618+
1619+ cx.jobs.callback(callback);
1620+
1621+ Ok(())
1622+}
1623+
15311624fn vsplit(
15321625 cx: &mut compositor::Context,
15331626 args: &[Cow<str>],
@@ -1537,10 +1630,8 @@ fn vsplit(
15371630 return Ok(());
15381631 }
15391632
1540− let id = view!(cx.editor).doc;
1541−
15421633 if args.is_empty() {
1543− cx.editor.switch(id, Action::VerticalSplit);
1634+ split(cx.editor, Action::VerticalSplit);
15441635 } else {
15451636 for arg in args {
15461637 cx.editor
@@ -1560,10 +1651,8 @@ fn hsplit(
15601651 return Ok(());
15611652 }
15621653
1563− let id = view!(cx.editor).doc;
1564−
15651654 if args.is_empty() {
1566− cx.editor.switch(id, Action::HorizontalSplit);
1655+ split(cx.editor, Action::HorizontalSplit);
15671656 } else {
15681657 for arg in args {
15691658 cx.editor
@@ -1678,7 +1767,7 @@ fn tutor(
16781767 let path = helix_loader::runtime_file(Path::new("tutor"));
16791768 cx.editor.open(&path, Action::Replace)?;
16801769 // Unset path to prevent accidentally saving to the original tutor file.
1681− doc_mut!(cx.editor).set_path(None)?;
1770+ doc_mut!(cx.editor).set_path(None);
16821771 Ok(())
16831772}
16841773
@@ -1855,14 +1944,29 @@ fn toggle_option(
18551944 .to_string(),
18561945 )
18571946 }
1858− Value::Null | Value::Object(_) | Value::Array(_) | Value::Number(_) => {
1947+ Value::Number(ref value) => {
1948+ ensure!(
1949+ args.len() > 2,
1950+ "Bad arguments. For number configurations use: `:toggle key val1 val2 ...`",
1951+ );
1952+
1953+ Value::Number(
1954+ args[1..]
1955+ .iter()
1956+ .skip_while(|&e| value.to_string() != *e.to_string())
1957+ .nth(1)
1958+ .unwrap_or_else(|| &args[1])
1959+ .parse()?,
1960+ )
1961+ }
1962+ Value::Null | Value::Object(_) | Value::Array(_) => {
18591963 anyhow::bail!("Configuration {key} does not support toggle yet")
18601964 }
18611965 };
18621966
18631967 let status = format!("'{key}' is now set to {value}");
18641968 let config = serde_json::from_value(config)
1865− .map_err(|_| anyhow::anyhow!("Could not parse field: `{:?}`", &args))?;
1969+ .map_err(|err| anyhow::anyhow!("Cannot parse `{:?}`, {}", &args, err))?;
18661970
18671971 cx.editor
18681972 .config_events
@@ -2271,606 +2375,721 @@ fn clear_register(
22712375 format!("Invalid register {}", args[0])
22722376 );
22732377 let register = args[0].chars().next().unwrap_or_default();
2274− match cx.editor.registers.remove(register) {
2275− Some(_) => cx
2276− .editor
2277− .set_status(format!("Register {} cleared", register)),
2278− None => cx
2279− .editor
2280− .set_error(format!("Register {} not found", register)),
2378+ if cx.editor.registers.remove(register) {
2379+ cx.editor
2380+ .set_status(format!("Register {} cleared", register));
2381+ } else {
2382+ cx.editor
2383+ .set_error(format!("Register {} not found", register));
2384+ }
2385+ Ok(())
2386+}
2387+
2388+fn redraw(
2389+ cx: &mut compositor::Context,
2390+ _args: &[Cow<str>],
2391+ event: PromptEvent,
2392+) -> anyhow::Result<()> {
2393+ if event != PromptEvent::Validate {
2394+ return Ok(());
2395+ }
2396+
2397+ let callback = Box::pin(async move {
2398+ let call: job::Callback =
2399+ job::Callback::EditorCompositor(Box::new(|_editor, compositor| {
2400+ compositor.need_full_redraw();
2401+ }));
2402+
2403+ Ok(call)
2404+ });
2405+
2406+ cx.jobs.callback(callback);
2407+
2408+ Ok(())
2409+}
2410+
2411+fn move_buffer(
2412+ cx: &mut compositor::Context,
2413+ args: &[Cow<str>],
2414+ event: PromptEvent,
2415+) -> anyhow::Result<()> {
2416+ if event != PromptEvent::Validate {
2417+ return Ok(());
22812418 }
2419+
2420+ ensure!(args.len() == 1, format!(":move takes one argument"));
2421+ let doc = doc!(cx.editor);
2422+
2423+ let new_path = get_canonicalized_path(&PathBuf::from(args.first().unwrap().to_string()));
2424+ let old_path = doc
2425+ .path()
2426+ .ok_or_else(|| anyhow!("Scratch buffer cannot be moved. Use :write instead"))?
2427+ .clone();
2428+ let old_path_as_url = doc.url().unwrap();
2429+ let new_path_as_url = Url::from_file_path(&new_path).unwrap();
2430+
2431+ let edits: Vec<(
2432+ helix_lsp::Result<helix_lsp::lsp::WorkspaceEdit>,
2433+ OffsetEncoding,
2434+ String,
2435+ )> = doc
2436+ .language_servers()
2437+ .map(|lsp| {
2438+ (
2439+ lsp.prepare_file_rename(&old_path_as_url, &new_path_as_url),
2440+ lsp.offset_encoding(),
2441+ lsp.name().to_owned(),
2442+ )
2443+ })
2444+ .filter(|(f, _, _)| f.is_some())
2445+ .map(|(f, encoding, name)| (helix_lsp::block_on(f.unwrap()), encoding, name))
2446+ .collect();
2447+
2448+ for (lsp_reply, encoding, name) in edits {
2449+ match lsp_reply {
2450+ Ok(edit) => {
2451+ if let Err(e) = apply_workspace_edit(cx.editor, encoding, &edit) {
2452+ log::error!(
2453+ ":move command failed to apply edits from lsp {}: {:?}",
2454+ name,
2455+ e
2456+ );
2457+ };
2458+ }
2459+ Err(e) => {
2460+ log::error!("LSP {} failed to treat willRename request: {:?}", name, e);
2461+ }
2462+ };
2463+ }
2464+
2465+ let doc = doc_mut!(cx.editor);
2466+
2467+ doc.set_path(Some(new_path.as_path()));
2468+ if let Err(e) = std::fs::rename(&old_path, &new_path) {
2469+ doc.set_path(Some(old_path.as_path()));
2470+ bail!("Could not move file: {}", e);
2471+ };
2472+
2473+ doc.language_servers().for_each(|lsp| {
2474+ lsp.did_file_rename(&old_path_as_url, &new_path_as_url);
2475+ });
2476+
2477+ cx.editor
2478+ .language_servers
2479+ .file_event_handler
2480+ .file_changed(new_path);
2481+
22822482 Ok(())
22832483}
22842484
22852485pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
2286− TypableCommand {
2287− name: "quit",
2288− aliases: &["q"],
2289− doc: "Close the current view.",
2290− fun: quit,
2291− signature: CommandSignature::none(),
2292− },
2293− TypableCommand {
2294− name: "quit!",
2295− aliases: &["q!"],
2296− doc: "Force close the current view, ignoring unsaved changes.",
2297− fun: force_quit,
2298− signature: CommandSignature::none(),
2299− },
2300− TypableCommand {
2301− name: "open",
2302− aliases: &["o"],
2303− doc: "Open a file from disk into the current view.",
2304− fun: open,
2305− signature: CommandSignature::all(completers::filename),
2306− },
2307− TypableCommand {
2308− name: "buffer-close",
2309− aliases: &["bc", "bclose"],
2310− doc: "Close the current buffer.",
2311− fun: buffer_close,
2312− signature: CommandSignature::all(completers::buffer),
2313− },
2314− TypableCommand {
2315− name: "buffer-close!",
2316− aliases: &["bc!", "bclose!"],
2317− doc: "Close the current buffer forcefully, ignoring unsaved changes.",
2318− fun: force_buffer_close,
2319− signature: CommandSignature::all(completers::buffer)
2320− },
2321− TypableCommand {
2322− name: "buffer-close-others",
2323− aliases: &["bco", "bcloseother"],
2324− doc: "Close all buffers but the currently focused one.",
2325− fun: buffer_close_others,
2326− signature: CommandSignature::none(),
2327− },
2328− TypableCommand {
2329− name: "buffer-close-others!",
2330− aliases: &["bco!", "bcloseother!"],
2331− doc: "Force close all buffers but the currently focused one.",
2332− fun: force_buffer_close_others,
2333− signature: CommandSignature::none(),
2334− },
2335− TypableCommand {
2336− name: "buffer-close-all",
2337− aliases: &["bca", "bcloseall"],
2338− doc: "Close all buffers without quitting.",
2339− fun: buffer_close_all,
2340− signature: CommandSignature::none(),
2341− },
2342− TypableCommand {
2343− name: "buffer-close-all!",
2344− aliases: &["bca!", "bcloseall!"],
2345− doc: "Force close all buffers ignoring unsaved changes without quitting.",
2346− fun: force_buffer_close_all,
2347− signature: CommandSignature::none(),
2348− },
2349− TypableCommand {
2350− name: "buffer-next",
2351− aliases: &["bn", "bnext"],
2352− doc: "Goto next buffer.",
2353− fun: buffer_next,
2354− signature: CommandSignature::none(),
2355− },
2356− TypableCommand {
2357− name: "buffer-previous",
2358− aliases: &["bp", "bprev"],
2359− doc: "Goto previous buffer.",
2360− fun: buffer_previous,
2361− signature: CommandSignature::none(),
2362− },
2363− TypableCommand {
2364− name: "write",
2365− aliases: &["w"],
2366− doc: "Write changes to disk. Accepts an optional path (:write some/path.txt)",
2367− fun: write,
2368− signature: CommandSignature::positional(&[completers::filename]),
2369− },
2370− TypableCommand {
2371− name: "write!",
2372− aliases: &["w!"],
2373− doc: "Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write! some/path.txt)",
2374− fun: force_write,
2375− signature: CommandSignature::positional(&[completers::filename]),
2376− },
2377− TypableCommand {
2378− name: "write-buffer-close",
2379− aliases: &["wbc"],
2380− doc: "Write changes to disk and closes the buffer. Accepts an optional path (:write-buffer-close some/path.txt)",
2381− fun: write_buffer_close,
2382− signature: CommandSignature::positional(&[completers::filename]),
2383− },
2384− TypableCommand {
2385− name: "write-buffer-close!",
2386− aliases: &["wbc!"],
2387− doc: "Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt)",
2388− fun: force_write_buffer_close,
2389− signature: CommandSignature::positional(&[completers::filename]),
2390− },
2391− TypableCommand {
2392− name: "new",
2393− aliases: &["n"],
2394− doc: "Create a new scratch buffer.",
2395− fun: new_file,
2396− // TODO: This seems to complete with a filename, but doesn't use that filename to
2397− // set the path of the newly created buffer.
2398− signature: CommandSignature::positional(&[completers::filename]),
2399− },
2400− TypableCommand {
2401− name: "format",
2402− aliases: &["fmt"],
2403− doc: "Format the file using the LSP formatter.",
2404− fun: format,
2405− signature: CommandSignature::none(),
2406− },
2407− TypableCommand {
2408− name: "indent-style",
2409− aliases: &[],
2410− doc: "Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.)",
2411− fun: set_indent_style,
2412− signature: CommandSignature::none(),
2413− },
2414− TypableCommand {
2415− name: "line-ending",
2416− aliases: &[],
2417− #[cfg(not(feature = "unicode-lines"))]
2418− doc: "Set the document's default line ending. Options: crlf, lf.",
2419− #[cfg(feature = "unicode-lines")]
2420− doc: "Set the document's default line ending. Options: crlf, lf, cr, ff, nel.",
2421− fun: set_line_ending,
2422− signature: CommandSignature::none(),
2423− },
2424− TypableCommand {
2425− name: "earlier",
2426− aliases: &["ear"],
2427− doc: "Jump back to an earlier point in edit history. Accepts a number of steps or a time span.",
2428− fun: earlier,
2429− signature: CommandSignature::none(),
2430− },
2431− TypableCommand {
2432− name: "later",
2433− aliases: &["lat"],
2434− doc: "Jump to a later point in edit history. Accepts a number of steps or a time span.",
2435− fun: later,
2436− signature: CommandSignature::none(),
2437− },
2438− TypableCommand {
2439− name: "write-quit",
2440− aliases: &["wq", "x"],
2441− doc: "Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt)",
2442− fun: write_quit,
2443− signature: CommandSignature::positional(&[completers::filename]),
2444− },
2445− TypableCommand {
2446− name: "write-quit!",
2447− aliases: &["wq!", "x!"],
2448− doc: "Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt)",
2449− fun: force_write_quit,
2450− signature: CommandSignature::positional(&[completers::filename]),
2451− },
2452− TypableCommand {
2453− name: "write-all",
2454− aliases: &["wa"],
2455− doc: "Write changes from all buffers to disk.",
2456− fun: write_all,
2457− signature: CommandSignature::none(),
2458− },
2459− TypableCommand {
2460− name: "write-all!",
2461− aliases: &["wa!"],
2462− doc: "Forcefully write changes from all buffers to disk creating necessary subdirectories.",
2463− fun: force_write_all,
2464− signature: CommandSignature::none(),
2465− },
2466− TypableCommand {
2467− name: "write-quit-all",
2468− aliases: &["wqa", "xa"],
2469− doc: "Write changes from all buffers to disk and close all views.",
2470− fun: write_all_quit,
2471− signature: CommandSignature::none(),
2472− },
2473− TypableCommand {
2474− name: "write-quit-all!",
2475− aliases: &["wqa!", "xa!"],
2476− doc: "Write changes from all buffers to disk and close all views forcefully (ignoring unsaved changes).",
2477− fun: force_write_all_quit,
2478− signature: CommandSignature::none(),
2479− },
2480− TypableCommand {
2481− name: "quit-all",
2482− aliases: &["qa"],
2483− doc: "Close all views.",
2484− fun: quit_all,
2485− signature: CommandSignature::none(),
2486− },
2487− TypableCommand {
2488− name: "quit-all!",
2489− aliases: &["qa!"],
2490− doc: "Force close all views ignoring unsaved changes.",
2491− fun: force_quit_all,
2492− signature: CommandSignature::none(),
2493− },
2494− TypableCommand {
2495− name: "cquit",
2496− aliases: &["cq"],
2497− doc: "Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2).",
2498− fun: cquit,
2499− signature: CommandSignature::none(),
2500− },
2501− TypableCommand {
2502− name: "cquit!",
2503− aliases: &["cq!"],
2504− doc: "Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2).",
2505− fun: force_cquit,
2506− signature: CommandSignature::none(),
2507− },
2508− TypableCommand {
2509− name: "theme",
2510− aliases: &[],
2511− doc: "Change the editor theme (show current theme if no name specified).",
2512− fun: theme,
2513− signature: CommandSignature::positional(&[completers::theme]),
2514− },
2515− TypableCommand {
2516− name: "yank-join",
2517− aliases: &[],
2518− doc: "Yank joined selections. A separator can be provided as first argument. Default value is newline.",
2519− fun: yank_joined,
2520− signature: CommandSignature::none(),
2521− },
2522− TypableCommand {
2523− name: "clipboard-yank",
2524− aliases: &[],
2525− doc: "Yank main selection into system clipboard.",
2526− fun: yank_main_selection_to_clipboard,
2527− signature: CommandSignature::none(),
2528− },
2529− TypableCommand {
2530− name: "clipboard-yank-join",
2531− aliases: &[],
2532− doc: "Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline.", // FIXME: current UI can't display long doc.
2533− fun: yank_joined_to_clipboard,
2534− signature: CommandSignature::none(),
2535− },
2536− TypableCommand {
2537− name: "primary-clipboard-yank",
2538− aliases: &[],
2539− doc: "Yank main selection into system primary clipboard.",
2540− fun: yank_main_selection_to_primary_clipboard,
2541− signature: CommandSignature::none(),
2542− },
2543− TypableCommand {
2544− name: "primary-clipboard-yank-join",
2545− aliases: &[],
2546− doc: "Yank joined selections into system primary clipboard. A separator can be provided as first argument. Default value is newline.", // FIXME: current UI can't display long doc.
2547− fun: yank_joined_to_primary_clipboard,
2548− signature: CommandSignature::none(),
2549− },
2550− TypableCommand {
2551− name: "clipboard-paste-after",
2552− aliases: &[],
2553− doc: "Paste system clipboard after selections.",
2554− fun: paste_clipboard_after,
2555− signature: CommandSignature::none(),
2556− },
2557− TypableCommand {
2558− name: "clipboard-paste-before",
2559− aliases: &[],
2560− doc: "Paste system clipboard before selections.",
2561− fun: paste_clipboard_before,
2562− signature: CommandSignature::none(),
2563− },
2564− TypableCommand {
2565− name: "clipboard-paste-replace",
2566− aliases: &[],
2567− doc: "Replace selections with content of system clipboard.",
2568− fun: replace_selections_with_clipboard,
2569− signature: CommandSignature::none(),
2570− },
2571− TypableCommand {
2572− name: "primary-clipboard-paste-after",
2573− aliases: &[],
2574− doc: "Paste primary clipboard after selections.",
2575− fun: paste_primary_clipboard_after,
2576− signature: CommandSignature::none(),
2577− },
2578− TypableCommand {
2579− name: "primary-clipboard-paste-before",
2580− aliases: &[],
2581− doc: "Paste primary clipboard before selections.",
2582− fun: paste_primary_clipboard_before,
2583− signature: CommandSignature::none(),
2584− },
2585− TypableCommand {
2586− name: "primary-clipboard-paste-replace",
2587− aliases: &[],
2588− doc: "Replace selections with content of system primary clipboard.",
2589− fun: replace_selections_with_primary_clipboard,
2590− signature: CommandSignature::none(),
2591− },
2592− TypableCommand {
2593− name: "show-clipboard-provider",
2594− aliases: &[],
2595− doc: "Show clipboard provider name in status bar.",
2596− fun: show_clipboard_provider,
2597− signature: CommandSignature::none(),
2598− },
2599− TypableCommand {
2600− name: "change-current-directory",
2601− aliases: &["cd"],
2602− doc: "Change the current working directory.",
2603− fun: change_current_directory,
2604− signature: CommandSignature::positional(&[completers::directory]),
2605− },
2606− TypableCommand {
2607− name: "show-directory",
2608− aliases: &["pwd"],
2609− doc: "Show the current working directory.",
2610− fun: show_current_directory,
2611− signature: CommandSignature::none(),
2612− },
2613− TypableCommand {
2614− name: "encoding",
2615− aliases: &[],
2616− doc: "Set encoding. Based on `https://encoding.spec.whatwg.org`.",
2617− fun: set_encoding,
2618− signature: CommandSignature::none(),
2619− },
2620− TypableCommand {
2621− name: "character-info",
2622− aliases: &["char"],
2623− doc: "Get info about the character under the primary cursor.",
2624− fun: get_character_info,
2625− signature: CommandSignature::none(),
2626− },
2627− TypableCommand {
2628− name: "reload",
2629− aliases: &["rl"],
2630− doc: "Discard changes and reload from the source file.",
2631− fun: reload,
2632− signature: CommandSignature::none(),
2633− },
2634− TypableCommand {
2635− name: "reload-all",
2636− aliases: &["rla"],
2637− doc: "Discard changes and reload all documents from the source files.",
2638− fun: reload_all,
2639− signature: CommandSignature::none(),
2640− },
2641− TypableCommand {
2642− name: "update",
2643− aliases: &["u"],
2644− doc: "Write changes only if the file has been modified.",
2645− fun: update,
2646− signature: CommandSignature::none(),
2647− },
2648− TypableCommand {
2649− name: "lsp-workspace-command",
2650− aliases: &[],
2651− doc: "Open workspace command picker",
2652− fun: lsp_workspace_command,
2653− signature: CommandSignature::positional(&[completers::lsp_workspace_command]),
2654− },
2655− TypableCommand {
2656− name: "lsp-restart",
2657− aliases: &[],
2658− doc: "Restarts the language servers used by the current doc",
2659− fun: lsp_restart,
2660− signature: CommandSignature::none(),
2661− },
2662− TypableCommand {
2663− name: "lsp-stop",
2664− aliases: &[],
2665− doc: "Stops the language servers that are used by the current doc",
2666− fun: lsp_stop,
2667− signature: CommandSignature::none(),
2668− },
2669− TypableCommand {
2670− name: "tree-sitter-scopes",
2671− aliases: &[],
2672− doc: "Display tree sitter scopes, primarily for theming and development.",
2673− fun: tree_sitter_scopes,
2674− signature: CommandSignature::none(),
2675− },
2676− TypableCommand {
2677− name: "debug-start",
2678− aliases: &["dbg"],
2679− doc: "Start a debug session from a given template with given parameters.",
2680− fun: debug_start,
2681− signature: CommandSignature::none(),
2682− },
2683− TypableCommand {
2684− name: "debug-remote",
2685− aliases: &["dbg-tcp"],
2686− doc: "Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters.",
2687− fun: debug_remote,
2688− signature: CommandSignature::none(),
2689− },
2690− TypableCommand {
2691− name: "debug-eval",
2692− aliases: &[],
2693− doc: "Evaluate expression in current debug context.",
2694− fun: debug_eval,
2695− signature: CommandSignature::none(),
2696− },
2697− TypableCommand {
2698− name: "vsplit",
2699− aliases: &["vs"],
2700− doc: "Open the file in a vertical split.",
2701− fun: vsplit,
2702− signature: CommandSignature::all(completers::filename)
2703− },
2704− TypableCommand {
2705− name: "vsplit-new",
2706− aliases: &["vnew"],
2707− doc: "Open a scratch buffer in a vertical split.",
2708− fun: vsplit_new,
2709− signature: CommandSignature::none(),
2710− },
2711− TypableCommand {
2712− name: "hsplit",
2713− aliases: &["hs", "sp"],
2714− doc: "Open the file in a horizontal split.",
2715− fun: hsplit,
2716− signature: CommandSignature::all(completers::filename)
2717− },
2718− TypableCommand {
2719− name: "hsplit-new",
2720− aliases: &["hnew"],
2721− doc: "Open a scratch buffer in a horizontal split.",
2722− fun: hsplit_new,
2723− signature: CommandSignature::none(),
2724− },
2725− TypableCommand {
2726− name: "tutor",
2727− aliases: &[],
2728− doc: "Open the tutorial.",
2729− fun: tutor,
2730− signature: CommandSignature::none(),
2731− },
2732− TypableCommand {
2733− name: "goto",
2734− aliases: &["g"],
2735− doc: "Goto line number.",
2736− fun: goto_line_number,
2737− signature: CommandSignature::none(),
2738− },
2739− TypableCommand {
2740− name: "set-language",
2741− aliases: &["lang"],
2742− doc: "Set the language of current buffer (show current language if no value specified).",
2743− fun: language,
2744− signature: CommandSignature::positional(&[completers::language]),
2745− },
2746− TypableCommand {
2747− name: "set-option",
2748− aliases: &["set"],
2749− doc: "Set a config option at runtime.\nFor example to disable smart case search, use `:set search.smart-case false`.",
2750− fun: set_option,
2751− // TODO: Add support for completion of the options value(s), when appropriate.
2752− signature: CommandSignature::positional(&[completers::setting]),
2753− },
2754− TypableCommand {
2755− name: "toggle-option",
2756− aliases: &["toggle"],
2757− doc: "Toggle a boolean config option at runtime.\nFor example to toggle smart case search, use `:toggle search.smart-case`.",
2758− fun: toggle_option,
2759− signature: CommandSignature::positional(&[completers::setting]),
2760− },
2761− TypableCommand {
2762− name: "get-option",
2763− aliases: &["get"],
2764− doc: "Get the current value of a config option.",
2765− fun: get_option,
2766− signature: CommandSignature::positional(&[completers::setting]),
2767− },
2768− TypableCommand {
2769− name: "sort",
2770− aliases: &[],
2771− doc: "Sort ranges in selection.",
2772− fun: sort,
2773− signature: CommandSignature::none(),
2774− },
2775− TypableCommand {
2776− name: "rsort",
2777− aliases: &[],
2778− doc: "Sort ranges in selection in reverse order.",
2779− fun: sort_reverse,
2780− signature: CommandSignature::none(),
2781− },
2782− TypableCommand {
2783− name: "reflow",
2784− aliases: &[],
2785− doc: "Hard-wrap the current selection of lines to a given width.",
2786− fun: reflow,
2787− signature: CommandSignature::none(),
2788− },
2789− TypableCommand {
2790− name: "tree-sitter-subtree",
2791− aliases: &["ts-subtree"],
2792− doc: "Display tree sitter subtree under cursor, primarily for debugging queries.",
2793− fun: tree_sitter_subtree,
2794− signature: CommandSignature::none(),
2795− },
2796− TypableCommand {
2797− name: "config-reload",
2798− aliases: &[],
2799− doc: "Refresh user config.",
2800− fun: refresh_config,
2801− signature: CommandSignature::none(),
2802− },
2803− TypableCommand {
2804− name: "config-open",
2805− aliases: &[],
2806− doc: "Open the user config.toml file.",
2807− fun: open_config,
2808− signature: CommandSignature::none(),
2809− },
2810− TypableCommand {
2811− name: "config-open-workspace",
2812− aliases: &[],
2813− doc: "Open the workspace config.toml file.",
2814− fun: open_workspace_config,
2815− signature: CommandSignature::none(),
2816− },
2817− TypableCommand {
2818− name: "log-open",
2819− aliases: &[],
2820− doc: "Open the helix log file.",
2821− fun: open_log,
2822− signature: CommandSignature::none(),
2823− },
2824− TypableCommand {
2825− name: "insert-output",
2826− aliases: &[],
2827− doc: "Run shell command, inserting output before each selection.",
2828− fun: insert_output,
2829− signature: CommandSignature::none(),
2830− },
2831− TypableCommand {
2832− name: "append-output",
2833− aliases: &[],
2834− doc: "Run shell command, appending output after each selection.",
2835− fun: append_output,
2836− signature: CommandSignature::none(),
2837− },
2838− TypableCommand {
2839− name: "pipe",
2840− aliases: &[],
2841− doc: "Pipe each selection to the shell command.",
2842− fun: pipe,
2843− signature: CommandSignature::none(),
2844− },
2845− TypableCommand {
2846− name: "pipe-to",
2847− aliases: &[],
2848− doc: "Pipe each selection to the shell command, ignoring output.",
2849− fun: pipe_to,
2850− signature: CommandSignature::none(),
2851− },
2852− TypableCommand {
2853− name: "run-shell-command",
2854− aliases: &["sh"],
2855− doc: "Run a shell command",
2856− fun: run_shell_command,
2857− signature: CommandSignature::all(completers::filename)
2858− },
2859− TypableCommand {
2860− name: "reset-diff-change",
2861− aliases: &["diffget", "diffg"],
2862− doc: "Reset the diff change at the cursor position.",
2863− fun: reset_diff_change,
2864− signature: CommandSignature::none(),
2865− },
2866− TypableCommand {
2867− name: "clear-register",
2868− aliases: &[],
2869− doc: "Clear given register. If no argument is provided, clear all registers.",
2870− fun: clear_register,
2871− signature: CommandSignature::none(),
2872− },
2873− ];
2486+ TypableCommand {
2487+ name: "quit",
2488+ aliases: &["q"],
2489+ doc: "Close the current view.",
2490+ fun: quit,
2491+ signature: CommandSignature::none(),
2492+ },
2493+ TypableCommand {
2494+ name: "quit!",
2495+ aliases: &["q!"],
2496+ doc: "Force close the current view, ignoring unsaved changes.",
2497+ fun: force_quit,
2498+ signature: CommandSignature::none(),
2499+ },
2500+ TypableCommand {
2501+ name: "open",
2502+ aliases: &["o"],
2503+ doc: "Open a file from disk into the current view.",
2504+ fun: open,
2505+ signature: CommandSignature::all(completers::filename),
2506+ },
2507+ TypableCommand {
2508+ name: "buffer-close",
2509+ aliases: &["bc", "bclose"],
2510+ doc: "Close the current buffer.",
2511+ fun: buffer_close,
2512+ signature: CommandSignature::all(completers::buffer),
2513+ },
2514+ TypableCommand {
2515+ name: "buffer-close!",
2516+ aliases: &["bc!", "bclose!"],
2517+ doc: "Close the current buffer forcefully, ignoring unsaved changes.",
2518+ fun: force_buffer_close,
2519+ signature: CommandSignature::all(completers::buffer)
2520+ },
2521+ TypableCommand {
2522+ name: "buffer-close-others",
2523+ aliases: &["bco", "bcloseother"],
2524+ doc: "Close all buffers but the currently focused one.",
2525+ fun: buffer_close_others,
2526+ signature: CommandSignature::none(),
2527+ },
2528+ TypableCommand {
2529+ name: "buffer-close-others!",
2530+ aliases: &["bco!", "bcloseother!"],
2531+ doc: "Force close all buffers but the currently focused one.",
2532+ fun: force_buffer_close_others,
2533+ signature: CommandSignature::none(),
2534+ },
2535+ TypableCommand {
2536+ name: "buffer-close-all",
2537+ aliases: &["bca", "bcloseall"],
2538+ doc: "Close all buffers without quitting.",
2539+ fun: buffer_close_all,
2540+ signature: CommandSignature::none(),
2541+ },
2542+ TypableCommand {
2543+ name: "buffer-close-all!",
2544+ aliases: &["bca!", "bcloseall!"],
2545+ doc: "Force close all buffers ignoring unsaved changes without quitting.",
2546+ fun: force_buffer_close_all,
2547+ signature: CommandSignature::none(),
2548+ },
2549+ TypableCommand {
2550+ name: "buffer-next",
2551+ aliases: &["bn", "bnext"],
2552+ doc: "Goto next buffer.",
2553+ fun: buffer_next,
2554+ signature: CommandSignature::none(),
2555+ },
2556+ TypableCommand {
2557+ name: "buffer-previous",
2558+ aliases: &["bp", "bprev"],
2559+ doc: "Goto previous buffer.",
2560+ fun: buffer_previous,
2561+ signature: CommandSignature::none(),
2562+ },
2563+ TypableCommand {
2564+ name: "write",
2565+ aliases: &["w"],
2566+ doc: "Write changes to disk. Accepts an optional path (:write some/path.txt)",
2567+ fun: write,
2568+ signature: CommandSignature::positional(&[completers::filename]),
2569+ },
2570+ TypableCommand {
2571+ name: "write!",
2572+ aliases: &["w!"],
2573+ doc: "Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write! some/path.txt)",
2574+ fun: force_write,
2575+ signature: CommandSignature::positional(&[completers::filename]),
2576+ },
2577+ TypableCommand {
2578+ name: "write-buffer-close",
2579+ aliases: &["wbc"],
2580+ doc: "Write changes to disk and closes the buffer. Accepts an optional path (:write-buffer-close some/path.txt)",
2581+ fun: write_buffer_close,
2582+ signature: CommandSignature::positional(&[completers::filename]),
2583+ },
2584+ TypableCommand {
2585+ name: "write-buffer-close!",
2586+ aliases: &["wbc!"],
2587+ doc: "Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt)",
2588+ fun: force_write_buffer_close,
2589+ signature: CommandSignature::positional(&[completers::filename]),
2590+ },
2591+ TypableCommand {
2592+ name: "new",
2593+ aliases: &["n"],
2594+ doc: "Create a new scratch buffer.",
2595+ fun: new_file,
2596+ signature: CommandSignature::none(),
2597+ },
2598+ TypableCommand {
2599+ name: "format",
2600+ aliases: &["fmt"],
2601+ doc: "Format the file using the LSP formatter.",
2602+ fun: format,
2603+ signature: CommandSignature::none(),
2604+ },
2605+ TypableCommand {
2606+ name: "indent-style",
2607+ aliases: &[],
2608+ doc: "Set the indentation style for editing. ('t' for tabs or 1-16 for number of spaces.)",
2609+ fun: set_indent_style,
2610+ signature: CommandSignature::none(),
2611+ },
2612+ TypableCommand {
2613+ name: "line-ending",
2614+ aliases: &[],
2615+ #[cfg(not(feature = "unicode-lines"))]
2616+ doc: "Set the document's default line ending. Options: crlf, lf.",
2617+ #[cfg(feature = "unicode-lines")]
2618+ doc: "Set the document's default line ending. Options: crlf, lf, cr, ff, nel.",
2619+ fun: set_line_ending,
2620+ signature: CommandSignature::none(),
2621+ },
2622+ TypableCommand {
2623+ name: "earlier",
2624+ aliases: &["ear"],
2625+ doc: "Jump back to an earlier point in edit history. Accepts a number of steps or a time span.",
2626+ fun: earlier,
2627+ signature: CommandSignature::none(),
2628+ },
2629+ TypableCommand {
2630+ name: "later",
2631+ aliases: &["lat"],
2632+ doc: "Jump to a later point in edit history. Accepts a number of steps or a time span.",
2633+ fun: later,
2634+ signature: CommandSignature::none(),
2635+ },
2636+ TypableCommand {
2637+ name: "write-quit",
2638+ aliases: &["wq", "x"],
2639+ doc: "Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt)",
2640+ fun: write_quit,
2641+ signature: CommandSignature::positional(&[completers::filename]),
2642+ },
2643+ TypableCommand {
2644+ name: "write-quit!",
2645+ aliases: &["wq!", "x!"],
2646+ doc: "Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt)",
2647+ fun: force_write_quit,
2648+ signature: CommandSignature::positional(&[completers::filename]),
2649+ },
2650+ TypableCommand {
2651+ name: "write-all",
2652+ aliases: &["wa"],
2653+ doc: "Write changes from all buffers to disk.",
2654+ fun: write_all,
2655+ signature: CommandSignature::none(),
2656+ },
2657+ TypableCommand {
2658+ name: "write-all!",
2659+ aliases: &["wa!"],
2660+ doc: "Forcefully write changes from all buffers to disk creating necessary subdirectories.",
2661+ fun: force_write_all,
2662+ signature: CommandSignature::none(),
2663+ },
2664+ TypableCommand {
2665+ name: "write-quit-all",
2666+ aliases: &["wqa", "xa"],
2667+ doc: "Write changes from all buffers to disk and close all views.",
2668+ fun: write_all_quit,
2669+ signature: CommandSignature::none(),
2670+ },
2671+ TypableCommand {
2672+ name: "write-quit-all!",
2673+ aliases: &["wqa!", "xa!"],
2674+ doc: "Write changes from all buffers to disk and close all views forcefully (ignoring unsaved changes).",
2675+ fun: force_write_all_quit,
2676+ signature: CommandSignature::none(),
2677+ },
2678+ TypableCommand {
2679+ name: "quit-all",
2680+ aliases: &["qa"],
2681+ doc: "Close all views.",
2682+ fun: quit_all,
2683+ signature: CommandSignature::none(),
2684+ },
2685+ TypableCommand {
2686+ name: "quit-all!",
2687+ aliases: &["qa!"],
2688+ doc: "Force close all views ignoring unsaved changes.",
2689+ fun: force_quit_all,
2690+ signature: CommandSignature::none(),
2691+ },
2692+ TypableCommand {
2693+ name: "cquit",
2694+ aliases: &["cq"],
2695+ doc: "Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2).",
2696+ fun: cquit,
2697+ signature: CommandSignature::none(),
2698+ },
2699+ TypableCommand {
2700+ name: "cquit!",
2701+ aliases: &["cq!"],
2702+ doc: "Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2).",
2703+ fun: force_cquit,
2704+ signature: CommandSignature::none(),
2705+ },
2706+ TypableCommand {
2707+ name: "theme",
2708+ aliases: &[],
2709+ doc: "Change the editor theme (show current theme if no name specified).",
2710+ fun: theme,
2711+ signature: CommandSignature::positional(&[completers::theme]),
2712+ },
2713+ TypableCommand {
2714+ name: "yank-join",
2715+ aliases: &[],
2716+ doc: "Yank joined selections. A separator can be provided as first argument. Default value is newline.",
2717+ fun: yank_joined,
2718+ signature: CommandSignature::none(),
2719+ },
2720+ TypableCommand {
2721+ name: "clipboard-yank",
2722+ aliases: &[],
2723+ doc: "Yank main selection into system clipboard.",
2724+ fun: yank_main_selection_to_clipboard,
2725+ signature: CommandSignature::none(),
2726+ },
2727+ TypableCommand {
2728+ name: "clipboard-yank-join",
2729+ aliases: &[],
2730+ doc: "Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline.", // FIXME: current UI can't display long doc.
2731+ fun: yank_joined_to_clipboard,
2732+ signature: CommandSignature::none(),
2733+ },
2734+ TypableCommand {
2735+ name: "primary-clipboard-yank",
2736+ aliases: &[],
2737+ doc: "Yank main selection into system primary clipboard.",
2738+ fun: yank_main_selection_to_primary_clipboard,
2739+ signature: CommandSignature::none(),
2740+ },
2741+ TypableCommand {
2742+ name: "primary-clipboard-yank-join",
2743+ aliases: &[],
2744+ doc: "Yank joined selections into system primary clipboard. A separator can be provided as first argument. Default value is newline.", // FIXME: current UI can't display long doc.
2745+ fun: yank_joined_to_primary_clipboard,
2746+ signature: CommandSignature::none(),
2747+ },
2748+ TypableCommand {
2749+ name: "clipboard-paste-after",
2750+ aliases: &[],
2751+ doc: "Paste system clipboard after selections.",
2752+ fun: paste_clipboard_after,
2753+ signature: CommandSignature::none(),
2754+ },
2755+ TypableCommand {
2756+ name: "clipboard-paste-before",
2757+ aliases: &[],
2758+ doc: "Paste system clipboard before selections.",
2759+ fun: paste_clipboard_before,
2760+ signature: CommandSignature::none(),
2761+ },
2762+ TypableCommand {
2763+ name: "clipboard-paste-replace",
2764+ aliases: &[],
2765+ doc: "Replace selections with content of system clipboard.",
2766+ fun: replace_selections_with_clipboard,
2767+ signature: CommandSignature::none(),
2768+ },
2769+ TypableCommand {
2770+ name: "primary-clipboard-paste-after",
2771+ aliases: &[],
2772+ doc: "Paste primary clipboard after selections.",
2773+ fun: paste_primary_clipboard_after,
2774+ signature: CommandSignature::none(),
2775+ },
2776+ TypableCommand {
2777+ name: "primary-clipboard-paste-before",
2778+ aliases: &[],
2779+ doc: "Paste primary clipboard before selections.",
2780+ fun: paste_primary_clipboard_before,
2781+ signature: CommandSignature::none(),
2782+ },
2783+ TypableCommand {
2784+ name: "primary-clipboard-paste-replace",
2785+ aliases: &[],
2786+ doc: "Replace selections with content of system primary clipboard.",
2787+ fun: replace_selections_with_primary_clipboard,
2788+ signature: CommandSignature::none(),
2789+ },
2790+ TypableCommand {
2791+ name: "show-clipboard-provider",
2792+ aliases: &[],
2793+ doc: "Show clipboard provider name in status bar.",
2794+ fun: show_clipboard_provider,
2795+ signature: CommandSignature::none(),
2796+ },
2797+ TypableCommand {
2798+ name: "change-current-directory",
2799+ aliases: &["cd"],
2800+ doc: "Change the current working directory.",
2801+ fun: change_current_directory,
2802+ signature: CommandSignature::positional(&[completers::directory]),
2803+ },
2804+ TypableCommand {
2805+ name: "show-directory",
2806+ aliases: &["pwd"],
2807+ doc: "Show the current working directory.",
2808+ fun: show_current_directory,
2809+ signature: CommandSignature::none(),
2810+ },
2811+ TypableCommand {
2812+ name: "encoding",
2813+ aliases: &[],
2814+ doc: "Set encoding. Based on `https://encoding.spec.whatwg.org`.",
2815+ fun: set_encoding,
2816+ signature: CommandSignature::none(),
2817+ },
2818+ TypableCommand {
2819+ name: "character-info",
2820+ aliases: &["char"],
2821+ doc: "Get info about the character under the primary cursor.",
2822+ fun: get_character_info,
2823+ signature: CommandSignature::none(),
2824+ },
2825+ TypableCommand {
2826+ name: "reload",
2827+ aliases: &["rl"],
2828+ doc: "Discard changes and reload from the source file.",
2829+ fun: reload,
2830+ signature: CommandSignature::none(),
2831+ },
2832+ TypableCommand {
2833+ name: "reload-all",
2834+ aliases: &["rla"],
2835+ doc: "Discard changes and reload all documents from the source files.",
2836+ fun: reload_all,
2837+ signature: CommandSignature::none(),
2838+ },
2839+ TypableCommand {
2840+ name: "update",
2841+ aliases: &["u"],
2842+ doc: "Write changes only if the file has been modified.",
2843+ fun: update,
2844+ signature: CommandSignature::none(),
2845+ },
2846+ TypableCommand {
2847+ name: "lsp-workspace-command",
2848+ aliases: &[],
2849+ doc: "Open workspace command picker",
2850+ fun: lsp_workspace_command,
2851+ signature: CommandSignature::positional(&[completers::lsp_workspace_command]),
2852+ },
2853+ TypableCommand {
2854+ name: "lsp-restart",
2855+ aliases: &[],
2856+ doc: "Restarts the language servers used by the current doc",
2857+ fun: lsp_restart,
2858+ signature: CommandSignature::none(),
2859+ },
2860+ TypableCommand {
2861+ name: "lsp-stop",
2862+ aliases: &[],
2863+ doc: "Stops the language servers that are used by the current doc",
2864+ fun: lsp_stop,
2865+ signature: CommandSignature::none(),
2866+ },
2867+ TypableCommand {
2868+ name: "tree-sitter-scopes",
2869+ aliases: &[],
2870+ doc: "Display tree sitter scopes, primarily for theming and development.",
2871+ fun: tree_sitter_scopes,
2872+ signature: CommandSignature::none(),
2873+ },
2874+ TypableCommand {
2875+ name: "tree-sitter-highlight-name",
2876+ aliases: &[],
2877+ doc: "Display name of tree-sitter highlight scope under the cursor.",
2878+ fun: tree_sitter_highlight_name,
2879+ signature: CommandSignature::none(),
2880+ },
2881+ TypableCommand {
2882+ name: "debug-start",
2883+ aliases: &["dbg"],
2884+ doc: "Start a debug session from a given template with given parameters.",
2885+ fun: debug_start,
2886+ signature: CommandSignature::none(),
2887+ },
2888+ TypableCommand {
2889+ name: "debug-remote",
2890+ aliases: &["dbg-tcp"],
2891+ doc: "Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters.",
2892+ fun: debug_remote,
2893+ signature: CommandSignature::none(),
2894+ },
2895+ TypableCommand {
2896+ name: "debug-eval",
2897+ aliases: &[],
2898+ doc: "Evaluate expression in current debug context.",
2899+ fun: debug_eval,
2900+ signature: CommandSignature::none(),
2901+ },
2902+ TypableCommand {
2903+ name: "vsplit",
2904+ aliases: &["vs"],
2905+ doc: "Open the file in a vertical split.",
2906+ fun: vsplit,
2907+ signature: CommandSignature::all(completers::filename)
2908+ },
2909+ TypableCommand {
2910+ name: "vsplit-new",
2911+ aliases: &["vnew"],
2912+ doc: "Open a scratch buffer in a vertical split.",
2913+ fun: vsplit_new,
2914+ signature: CommandSignature::none(),
2915+ },
2916+ TypableCommand {
2917+ name: "hsplit",
2918+ aliases: &["hs", "sp"],
2919+ doc: "Open the file in a horizontal split.",
2920+ fun: hsplit,
2921+ signature: CommandSignature::all(completers::filename)
2922+ },
2923+ TypableCommand {
2924+ name: "hsplit-new",
2925+ aliases: &["hnew"],
2926+ doc: "Open a scratch buffer in a horizontal split.",
2927+ fun: hsplit_new,
2928+ signature: CommandSignature::none(),
2929+ },
2930+ TypableCommand {
2931+ name: "tutor",
2932+ aliases: &[],
2933+ doc: "Open the tutorial.",
2934+ fun: tutor,
2935+ signature: CommandSignature::none(),
2936+ },
2937+ TypableCommand {
2938+ name: "goto",
2939+ aliases: &["g"],
2940+ doc: "Goto line number.",
2941+ fun: goto_line_number,
2942+ signature: CommandSignature::none(),
2943+ },
2944+ TypableCommand {
2945+ name: "set-language",
2946+ aliases: &["lang"],
2947+ doc: "Set the language of current buffer (show current language if no value specified).",
2948+ fun: language,
2949+ signature: CommandSignature::positional(&[completers::language]),
2950+ },
2951+ TypableCommand {
2952+ name: "set-option",
2953+ aliases: &["set"],
2954+ doc: "Set a config option at runtime.\nFor example to disable smart case search, use `:set search.smart-case false`.",
2955+ fun: set_option,
2956+ // TODO: Add support for completion of the options value(s), when appropriate.
2957+ signature: CommandSignature::positional(&[completers::setting]),
2958+ },
2959+ TypableCommand {
2960+ name: "toggle-option",
2961+ aliases: &["toggle"],
2962+ doc: "Toggle a boolean config option at runtime.\nFor example to toggle smart case search, use `:toggle search.smart-case`.",
2963+ fun: toggle_option,
2964+ signature: CommandSignature::positional(&[completers::setting]),
2965+ },
2966+ TypableCommand {
2967+ name: "get-option",
2968+ aliases: &["get"],
2969+ doc: "Get the current value of a config option.",
2970+ fun: get_option,
2971+ signature: CommandSignature::positional(&[completers::setting]),
2972+ },
2973+ TypableCommand {
2974+ name: "sort",
2975+ aliases: &[],
2976+ doc: "Sort ranges in selection.",
2977+ fun: sort,
2978+ signature: CommandSignature::none(),
2979+ },
2980+ TypableCommand {
2981+ name: "rsort",
2982+ aliases: &[],
2983+ doc: "Sort ranges in selection in reverse order.",
2984+ fun: sort_reverse,
2985+ signature: CommandSignature::none(),
2986+ },
2987+ TypableCommand {
2988+ name: "reflow",
2989+ aliases: &[],
2990+ doc: "Hard-wrap the current selection of lines to a given width.",
2991+ fun: reflow,
2992+ signature: CommandSignature::none(),
2993+ },
2994+ TypableCommand {
2995+ name: "tree-sitter-subtree",
2996+ aliases: &["ts-subtree"],
2997+ doc: "Display tree sitter subtree under cursor, primarily for debugging queries.",
2998+ fun: tree_sitter_subtree,
2999+ signature: CommandSignature::none(),
3000+ },
3001+ TypableCommand {
3002+ name: "config-reload",
3003+ aliases: &[],
3004+ doc: "Refresh user config.",
3005+ fun: refresh_config,
3006+ signature: CommandSignature::none(),
3007+ },
3008+ TypableCommand {
3009+ name: "config-open",
3010+ aliases: &[],
3011+ doc: "Open the user config.toml file.",
3012+ fun: open_config,
3013+ signature: CommandSignature::none(),
3014+ },
3015+ TypableCommand {
3016+ name: "config-open-workspace",
3017+ aliases: &[],
3018+ doc: "Open the workspace config.toml file.",
3019+ fun: open_workspace_config,
3020+ signature: CommandSignature::none(),
3021+ },
3022+ TypableCommand {
3023+ name: "log-open",
3024+ aliases: &[],
3025+ doc: "Open the helix log file.",
3026+ fun: open_log,
3027+ signature: CommandSignature::none(),
3028+ },
3029+ TypableCommand {
3030+ name: "insert-output",
3031+ aliases: &[],
3032+ doc: "Run shell command, inserting output before each selection.",
3033+ fun: insert_output,
3034+ signature: CommandSignature::none(),
3035+ },
3036+ TypableCommand {
3037+ name: "append-output",
3038+ aliases: &[],
3039+ doc: "Run shell command, appending output after each selection.",
3040+ fun: append_output,
3041+ signature: CommandSignature::none(),
3042+ },
3043+ TypableCommand {
3044+ name: "pipe",
3045+ aliases: &[],
3046+ doc: "Pipe each selection to the shell command.",
3047+ fun: pipe,
3048+ signature: CommandSignature::none(),
3049+ },
3050+ TypableCommand {
3051+ name: "pipe-to",
3052+ aliases: &[],
3053+ doc: "Pipe each selection to the shell command, ignoring output.",
3054+ fun: pipe_to,
3055+ signature: CommandSignature::none(),
3056+ },
3057+ TypableCommand {
3058+ name: "run-shell-command",
3059+ aliases: &["sh"],
3060+ doc: "Run a shell command",
3061+ fun: run_shell_command,
3062+ signature: CommandSignature::all(completers::filename)
3063+ },
3064+ TypableCommand {
3065+ name: "reset-diff-change",
3066+ aliases: &["diffget", "diffg"],
3067+ doc: "Reset the diff change at the cursor position.",
3068+ fun: reset_diff_change,
3069+ signature: CommandSignature::none(),
3070+ },
3071+ TypableCommand {
3072+ name: "clear-register",
3073+ aliases: &[],
3074+ doc: "Clear given register. If no argument is provided, clear all registers.",
3075+ fun: clear_register,
3076+ signature: CommandSignature::none(),
3077+ },
3078+ TypableCommand {
3079+ name: "redraw",
3080+ aliases: &[],
3081+ doc: "Clear and re-render the whole UI",
3082+ fun: redraw,
3083+ signature: CommandSignature::none(),
3084+ },
3085+ TypableCommand {
3086+ name: "move",
3087+ aliases: &[],
3088+ doc: "Move the current buffer and its corresponding file to a different path",
3089+ fun: move_buffer,
3090+ signature: CommandSignature::positional(&[completers::filename]),
3091+ },
3092+];
28743093
28753094pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
28763095 Lazy::new(|| {
@@ -2889,28 +3108,18 @@ pub(super) fn command_mode(cx: &mut Context) {
28893108 ":".into(),
28903109 Some(':'),
28913110 |editor: &Editor, input: &str| {
2892− static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
2893− Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
2894−
28953111 let shellwords = Shellwords::from(input);
28963112 let words = shellwords.words();
28973113
28983114 if words.is_empty() || (words.len() == 1 && !shellwords.ends_with_whitespace()) {
2899− // If the command has not been finished yet, complete commands.
2900− let mut matches: Vec<_> = typed::TYPABLE_COMMAND_LIST
2901− .iter()
2902− .filter_map(|command| {
2903− FUZZY_MATCHER
2904− .fuzzy_match(command.name, input)
2905− .map(|score| (command.name, score))
2906− })
2907− .collect();
2908−
2909− matches.sort_unstable_by_key(|(_file, score)| std::cmp::Reverse(*score));
2910− matches
2911− .into_iter()
2912− .map(|(name, _)| (0.., name.into()))
2913− .collect()
3115+ fuzzy_match(
3116+ input,
3117+ TYPABLE_COMMAND_LIST.iter().map(|command| command.name),
3118+ false,
3119+ )
3120+ .into_iter()
3121+ .map(|(name, _)| (0.., name.into()))
3122+ .collect()
29143123 } else {
29153124 // Otherwise, use the command's completer and the last shellword
29163125 // as completion input.