@@ -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
@@ -1278,12 +1282,17 @@ fn reload(
12781282 }
12791283
12801284 let scrolloff = cx.editor.config().scrolloff;
1281− let redraw_handle = cx.editor.redraw_handle.clone();
12821285 let (view, doc) = current!(cx.editor);
1283− doc.reload(view, &cx.editor.diff_providers, redraw_handle)
1284− .map(|_| {
1285− view.ensure_cursor_in_view(doc, scrolloff);
1286− })
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(())
12871296}
12881297
12891298fn reload_all(
@@ -1322,8 +1331,13 @@ fn reload_all(
13221331 // Ensure that the view is synced with the document's history.
13231332 view.sync_changes(doc);
13241333
1325− let redraw_handle = cx.editor.redraw_handle.clone();
1326− 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+ }
13271341
13281342 for view_id in view_ids {
13291343 let view = view_mut!(cx.editor, view_id);
@@ -1372,9 +1386,8 @@ fn lsp_workspace_command(
13721386 .map(|options| (ls.id(), options))
13731387 })
13741388 else {
1375− cx.editor.set_status(
1376− "No active language servers for this document support workspace commands",
1377− );
1389+ cx.editor
1390+ .set_status("No active language servers for this document support workspace commands");
13781391 return Ok(());
13791392 };
13801393
@@ -1530,6 +1543,84 @@ fn tree_sitter_scopes(
15301543 Ok(())
15311544}
15321545
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+
15331624fn vsplit(
15341625 cx: &mut compositor::Context,
15351626 args: &[Cow<str>],
@@ -1539,10 +1630,8 @@ fn vsplit(
15391630 return Ok(());
15401631 }
15411632
1542− let id = view!(cx.editor).doc;
1543−
15441633 if args.is_empty() {
1545− cx.editor.switch(id, Action::VerticalSplit);
1634+ split(cx.editor, Action::VerticalSplit);
15461635 } else {
15471636 for arg in args {
15481637 cx.editor
@@ -1562,10 +1651,8 @@ fn hsplit(
15621651 return Ok(());
15631652 }
15641653
1565− let id = view!(cx.editor).doc;
1566−
15671654 if args.is_empty() {
1568− cx.editor.switch(id, Action::HorizontalSplit);
1655+ split(cx.editor, Action::HorizontalSplit);
15691656 } else {
15701657 for arg in args {
15711658 cx.editor
@@ -1680,7 +1767,7 @@ fn tutor(
16801767 let path = helix_loader::runtime_file(Path::new("tutor"));
16811768 cx.editor.open(&path, Action::Replace)?;
16821769 // Unset path to prevent accidentally saving to the original tutor file.
1683− doc_mut!(cx.editor).set_path(None)?;
1770+ doc_mut!(cx.editor).set_path(None);
16841771 Ok(())
16851772}
16861773
@@ -1857,14 +1944,29 @@ fn toggle_option(
18571944 .to_string(),
18581945 )
18591946 }
1860− 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(_) => {
18611963 anyhow::bail!("Configuration {key} does not support toggle yet")
18621964 }
18631965 };
18641966
18651967 let status = format!("'{key}' is now set to {value}");
18661968 let config = serde_json::from_value(config)
1867− .map_err(|_| anyhow::anyhow!("Could not parse field: `{:?}`", &args))?;
1969+ .map_err(|err| anyhow::anyhow!("Cannot parse `{:?}`, {}", &args, err))?;
18681970
18691971 cx.editor
18701972 .config_events
@@ -2273,606 +2375,721 @@ fn clear_register(
22732375 format!("Invalid register {}", args[0])
22742376 );
22752377 let register = args[0].chars().next().unwrap_or_default();
2276− match cx.editor.registers.remove(register) {
2277− Some(_) => cx
2278− .editor
2279− .set_status(format!("Register {} cleared", register)),
2280− None => cx
2281− .editor
2282− .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(());
22832395 }
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(());
2418+ }
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+
22842482 Ok(())
22852483}
22862484
22872485pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
2288− TypableCommand {
2289− name: "quit",
2290− aliases: &["q"],
2291− doc: "Close the current view.",
2292− fun: quit,
2293− signature: CommandSignature::none(),
2294− },
2295− TypableCommand {
2296− name: "quit!",
2297− aliases: &["q!"],
2298− doc: "Force close the current view, ignoring unsaved changes.",
2299− fun: force_quit,
2300− signature: CommandSignature::none(),
2301− },
2302− TypableCommand {
2303− name: "open",
2304− aliases: &["o"],
2305− doc: "Open a file from disk into the current view.",
2306− fun: open,
2307− signature: CommandSignature::all(completers::filename),
2308− },
2309− TypableCommand {
2310− name: "buffer-close",
2311− aliases: &["bc", "bclose"],
2312− doc: "Close the current buffer.",
2313− fun: buffer_close,
2314− signature: CommandSignature::all(completers::buffer),
2315− },
2316− TypableCommand {
2317− name: "buffer-close!",
2318− aliases: &["bc!", "bclose!"],
2319− doc: "Close the current buffer forcefully, ignoring unsaved changes.",
2320− fun: force_buffer_close,
2321− signature: CommandSignature::all(completers::buffer)
2322− },
2323− TypableCommand {
2324− name: "buffer-close-others",
2325− aliases: &["bco", "bcloseother"],
2326− doc: "Close all buffers but the currently focused one.",
2327− fun: buffer_close_others,
2328− signature: CommandSignature::none(),
2329− },
2330− TypableCommand {
2331− name: "buffer-close-others!",
2332− aliases: &["bco!", "bcloseother!"],
2333− doc: "Force close all buffers but the currently focused one.",
2334− fun: force_buffer_close_others,
2335− signature: CommandSignature::none(),
2336− },
2337− TypableCommand {
2338− name: "buffer-close-all",
2339− aliases: &["bca", "bcloseall"],
2340− doc: "Close all buffers without quitting.",
2341− fun: buffer_close_all,
2342− signature: CommandSignature::none(),
2343− },
2344− TypableCommand {
2345− name: "buffer-close-all!",
2346− aliases: &["bca!", "bcloseall!"],
2347− doc: "Force close all buffers ignoring unsaved changes without quitting.",
2348− fun: force_buffer_close_all,
2349− signature: CommandSignature::none(),
2350− },
2351− TypableCommand {
2352− name: "buffer-next",
2353− aliases: &["bn", "bnext"],
2354− doc: "Goto next buffer.",
2355− fun: buffer_next,
2356− signature: CommandSignature::none(),
2357− },
2358− TypableCommand {
2359− name: "buffer-previous",
2360− aliases: &["bp", "bprev"],
2361− doc: "Goto previous buffer.",
2362− fun: buffer_previous,
2363− signature: CommandSignature::none(),
2364− },
2365− TypableCommand {
2366− name: "write",
2367− aliases: &["w"],
2368− doc: "Write changes to disk. Accepts an optional path (:write some/path.txt)",
2369− fun: write,
2370− signature: CommandSignature::positional(&[completers::filename]),
2371− },
2372− TypableCommand {
2373− name: "write!",
2374− aliases: &["w!"],
2375− doc: "Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write! some/path.txt)",
2376− fun: force_write,
2377− signature: CommandSignature::positional(&[completers::filename]),
2378− },
2379− TypableCommand {
2380− name: "write-buffer-close",
2381− aliases: &["wbc"],
2382− doc: "Write changes to disk and closes the buffer. Accepts an optional path (:write-buffer-close some/path.txt)",
2383− fun: write_buffer_close,
2384− signature: CommandSignature::positional(&[completers::filename]),
2385− },
2386− TypableCommand {
2387− name: "write-buffer-close!",
2388− aliases: &["wbc!"],
2389− doc: "Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt)",
2390− fun: force_write_buffer_close,
2391− signature: CommandSignature::positional(&[completers::filename]),
2392− },
2393− TypableCommand {
2394− name: "new",
2395− aliases: &["n"],
2396− doc: "Create a new scratch buffer.",
2397− fun: new_file,
2398− // TODO: This seems to complete with a filename, but doesn't use that filename to
2399− // set the path of the newly created buffer.
2400− signature: CommandSignature::positional(&[completers::filename]),
2401− },
2402− TypableCommand {
2403− name: "format",
2404− aliases: &["fmt"],
2405− doc: "Format the file using the LSP formatter.",
2406− fun: format,
2407− signature: CommandSignature::none(),
2408− },
2409− TypableCommand {
2410− name: "indent-style",
2411− aliases: &[],
2412− doc: "Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.)",
2413− fun: set_indent_style,
2414− signature: CommandSignature::none(),
2415− },
2416− TypableCommand {
2417− name: "line-ending",
2418− aliases: &[],
2419− #[cfg(not(feature = "unicode-lines"))]
2420− doc: "Set the document's default line ending. Options: crlf, lf.",
2421− #[cfg(feature = "unicode-lines")]
2422− doc: "Set the document's default line ending. Options: crlf, lf, cr, ff, nel.",
2423− fun: set_line_ending,
2424− signature: CommandSignature::none(),
2425− },
2426− TypableCommand {
2427− name: "earlier",
2428− aliases: &["ear"],
2429− doc: "Jump back to an earlier point in edit history. Accepts a number of steps or a time span.",
2430− fun: earlier,
2431− signature: CommandSignature::none(),
2432− },
2433− TypableCommand {
2434− name: "later",
2435− aliases: &["lat"],
2436− doc: "Jump to a later point in edit history. Accepts a number of steps or a time span.",
2437− fun: later,
2438− signature: CommandSignature::none(),
2439− },
2440− TypableCommand {
2441− name: "write-quit",
2442− aliases: &["wq", "x"],
2443− doc: "Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt)",
2444− fun: write_quit,
2445− signature: CommandSignature::positional(&[completers::filename]),
2446− },
2447− TypableCommand {
2448− name: "write-quit!",
2449− aliases: &["wq!", "x!"],
2450− doc: "Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt)",
2451− fun: force_write_quit,
2452− signature: CommandSignature::positional(&[completers::filename]),
2453− },
2454− TypableCommand {
2455− name: "write-all",
2456− aliases: &["wa"],
2457− doc: "Write changes from all buffers to disk.",
2458− fun: write_all,
2459− signature: CommandSignature::none(),
2460− },
2461− TypableCommand {
2462− name: "write-all!",
2463− aliases: &["wa!"],
2464− doc: "Forcefully write changes from all buffers to disk creating necessary subdirectories.",
2465− fun: force_write_all,
2466− signature: CommandSignature::none(),
2467− },
2468− TypableCommand {
2469− name: "write-quit-all",
2470− aliases: &["wqa", "xa"],
2471− doc: "Write changes from all buffers to disk and close all views.",
2472− fun: write_all_quit,
2473− signature: CommandSignature::none(),
2474− },
2475− TypableCommand {
2476− name: "write-quit-all!",
2477− aliases: &["wqa!", "xa!"],
2478− doc: "Write changes from all buffers to disk and close all views forcefully (ignoring unsaved changes).",
2479− fun: force_write_all_quit,
2480− signature: CommandSignature::none(),
2481− },
2482− TypableCommand {
2483− name: "quit-all",
2484− aliases: &["qa"],
2485− doc: "Close all views.",
2486− fun: quit_all,
2487− signature: CommandSignature::none(),
2488− },
2489− TypableCommand {
2490− name: "quit-all!",
2491− aliases: &["qa!"],
2492− doc: "Force close all views ignoring unsaved changes.",
2493− fun: force_quit_all,
2494− signature: CommandSignature::none(),
2495− },
2496− TypableCommand {
2497− name: "cquit",
2498− aliases: &["cq"],
2499− doc: "Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2).",
2500− fun: cquit,
2501− signature: CommandSignature::none(),
2502− },
2503− TypableCommand {
2504− name: "cquit!",
2505− aliases: &["cq!"],
2506− doc: "Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2).",
2507− fun: force_cquit,
2508− signature: CommandSignature::none(),
2509− },
2510− TypableCommand {
2511− name: "theme",
2512− aliases: &[],
2513− doc: "Change the editor theme (show current theme if no name specified).",
2514− fun: theme,
2515− signature: CommandSignature::positional(&[completers::theme]),
2516− },
2517− TypableCommand {
2518− name: "yank-join",
2519− aliases: &[],
2520− doc: "Yank joined selections. A separator can be provided as first argument. Default value is newline.",
2521− fun: yank_joined,
2522− signature: CommandSignature::none(),
2523− },
2524− TypableCommand {
2525− name: "clipboard-yank",
2526− aliases: &[],
2527− doc: "Yank main selection into system clipboard.",
2528− fun: yank_main_selection_to_clipboard,
2529− signature: CommandSignature::none(),
2530− },
2531− TypableCommand {
2532− name: "clipboard-yank-join",
2533− aliases: &[],
2534− 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.
2535− fun: yank_joined_to_clipboard,
2536− signature: CommandSignature::none(),
2537− },
2538− TypableCommand {
2539− name: "primary-clipboard-yank",
2540− aliases: &[],
2541− doc: "Yank main selection into system primary clipboard.",
2542− fun: yank_main_selection_to_primary_clipboard,
2543− signature: CommandSignature::none(),
2544− },
2545− TypableCommand {
2546− name: "primary-clipboard-yank-join",
2547− aliases: &[],
2548− 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.
2549− fun: yank_joined_to_primary_clipboard,
2550− signature: CommandSignature::none(),
2551− },
2552− TypableCommand {
2553− name: "clipboard-paste-after",
2554− aliases: &[],
2555− doc: "Paste system clipboard after selections.",
2556− fun: paste_clipboard_after,
2557− signature: CommandSignature::none(),
2558− },
2559− TypableCommand {
2560− name: "clipboard-paste-before",
2561− aliases: &[],
2562− doc: "Paste system clipboard before selections.",
2563− fun: paste_clipboard_before,
2564− signature: CommandSignature::none(),
2565− },
2566− TypableCommand {
2567− name: "clipboard-paste-replace",
2568− aliases: &[],
2569− doc: "Replace selections with content of system clipboard.",
2570− fun: replace_selections_with_clipboard,
2571− signature: CommandSignature::none(),
2572− },
2573− TypableCommand {
2574− name: "primary-clipboard-paste-after",
2575− aliases: &[],
2576− doc: "Paste primary clipboard after selections.",
2577− fun: paste_primary_clipboard_after,
2578− signature: CommandSignature::none(),
2579− },
2580− TypableCommand {
2581− name: "primary-clipboard-paste-before",
2582− aliases: &[],
2583− doc: "Paste primary clipboard before selections.",
2584− fun: paste_primary_clipboard_before,
2585− signature: CommandSignature::none(),
2586− },
2587− TypableCommand {
2588− name: "primary-clipboard-paste-replace",
2589− aliases: &[],
2590− doc: "Replace selections with content of system primary clipboard.",
2591− fun: replace_selections_with_primary_clipboard,
2592− signature: CommandSignature::none(),
2593− },
2594− TypableCommand {
2595− name: "show-clipboard-provider",
2596− aliases: &[],
2597− doc: "Show clipboard provider name in status bar.",
2598− fun: show_clipboard_provider,
2599− signature: CommandSignature::none(),
2600− },
2601− TypableCommand {
2602− name: "change-current-directory",
2603− aliases: &["cd"],
2604− doc: "Change the current working directory.",
2605− fun: change_current_directory,
2606− signature: CommandSignature::positional(&[completers::directory]),
2607− },
2608− TypableCommand {
2609− name: "show-directory",
2610− aliases: &["pwd"],
2611− doc: "Show the current working directory.",
2612− fun: show_current_directory,
2613− signature: CommandSignature::none(),
2614− },
2615− TypableCommand {
2616− name: "encoding",
2617− aliases: &[],
2618− doc: "Set encoding. Based on `https://encoding.spec.whatwg.org`.",
2619− fun: set_encoding,
2620− signature: CommandSignature::none(),
2621− },
2622− TypableCommand {
2623− name: "character-info",
2624− aliases: &["char"],
2625− doc: "Get info about the character under the primary cursor.",
2626− fun: get_character_info,
2627− signature: CommandSignature::none(),
2628− },
2629− TypableCommand {
2630− name: "reload",
2631− aliases: &["rl"],
2632− doc: "Discard changes and reload from the source file.",
2633− fun: reload,
2634− signature: CommandSignature::none(),
2635− },
2636− TypableCommand {
2637− name: "reload-all",
2638− aliases: &["rla"],
2639− doc: "Discard changes and reload all documents from the source files.",
2640− fun: reload_all,
2641− signature: CommandSignature::none(),
2642− },
2643− TypableCommand {
2644− name: "update",
2645− aliases: &["u"],
2646− doc: "Write changes only if the file has been modified.",
2647− fun: update,
2648− signature: CommandSignature::none(),
2649− },
2650− TypableCommand {
2651− name: "lsp-workspace-command",
2652− aliases: &[],
2653− doc: "Open workspace command picker",
2654− fun: lsp_workspace_command,
2655− signature: CommandSignature::positional(&[completers::lsp_workspace_command]),
2656− },
2657− TypableCommand {
2658− name: "lsp-restart",
2659− aliases: &[],
2660− doc: "Restarts the language servers used by the current doc",
2661− fun: lsp_restart,
2662− signature: CommandSignature::none(),
2663− },
2664− TypableCommand {
2665− name: "lsp-stop",
2666− aliases: &[],
2667− doc: "Stops the language servers that are used by the current doc",
2668− fun: lsp_stop,
2669− signature: CommandSignature::none(),
2670− },
2671− TypableCommand {
2672− name: "tree-sitter-scopes",
2673− aliases: &[],
2674− doc: "Display tree sitter scopes, primarily for theming and development.",
2675− fun: tree_sitter_scopes,
2676− signature: CommandSignature::none(),
2677− },
2678− TypableCommand {
2679− name: "debug-start",
2680− aliases: &["dbg"],
2681− doc: "Start a debug session from a given template with given parameters.",
2682− fun: debug_start,
2683− signature: CommandSignature::none(),
2684− },
2685− TypableCommand {
2686− name: "debug-remote",
2687− aliases: &["dbg-tcp"],
2688− doc: "Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters.",
2689− fun: debug_remote,
2690− signature: CommandSignature::none(),
2691− },
2692− TypableCommand {
2693− name: "debug-eval",
2694− aliases: &[],
2695− doc: "Evaluate expression in current debug context.",
2696− fun: debug_eval,
2697− signature: CommandSignature::none(),
2698− },
2699− TypableCommand {
2700− name: "vsplit",
2701− aliases: &["vs"],
2702− doc: "Open the file in a vertical split.",
2703− fun: vsplit,
2704− signature: CommandSignature::all(completers::filename)
2705− },
2706− TypableCommand {
2707− name: "vsplit-new",
2708− aliases: &["vnew"],
2709− doc: "Open a scratch buffer in a vertical split.",
2710− fun: vsplit_new,
2711− signature: CommandSignature::none(),
2712− },
2713− TypableCommand {
2714− name: "hsplit",
2715− aliases: &["hs", "sp"],
2716− doc: "Open the file in a horizontal split.",
2717− fun: hsplit,
2718− signature: CommandSignature::all(completers::filename)
2719− },
2720− TypableCommand {
2721− name: "hsplit-new",
2722− aliases: &["hnew"],
2723− doc: "Open a scratch buffer in a horizontal split.",
2724− fun: hsplit_new,
2725− signature: CommandSignature::none(),
2726− },
2727− TypableCommand {
2728− name: "tutor",
2729− aliases: &[],
2730− doc: "Open the tutorial.",
2731− fun: tutor,
2732− signature: CommandSignature::none(),
2733− },
2734− TypableCommand {
2735− name: "goto",
2736− aliases: &["g"],
2737− doc: "Goto line number.",
2738− fun: goto_line_number,
2739− signature: CommandSignature::none(),
2740− },
2741− TypableCommand {
2742− name: "set-language",
2743− aliases: &["lang"],
2744− doc: "Set the language of current buffer (show current language if no value specified).",
2745− fun: language,
2746− signature: CommandSignature::positional(&[completers::language]),
2747− },
2748− TypableCommand {
2749− name: "set-option",
2750− aliases: &["set"],
2751− doc: "Set a config option at runtime.\nFor example to disable smart case search, use `:set search.smart-case false`.",
2752− fun: set_option,
2753− // TODO: Add support for completion of the options value(s), when appropriate.
2754− signature: CommandSignature::positional(&[completers::setting]),
2755− },
2756− TypableCommand {
2757− name: "toggle-option",
2758− aliases: &["toggle"],
2759− doc: "Toggle a boolean config option at runtime.\nFor example to toggle smart case search, use `:toggle search.smart-case`.",
2760− fun: toggle_option,
2761− signature: CommandSignature::positional(&[completers::setting]),
2762− },
2763− TypableCommand {
2764− name: "get-option",
2765− aliases: &["get"],
2766− doc: "Get the current value of a config option.",
2767− fun: get_option,
2768− signature: CommandSignature::positional(&[completers::setting]),
2769− },
2770− TypableCommand {
2771− name: "sort",
2772− aliases: &[],
2773− doc: "Sort ranges in selection.",
2774− fun: sort,
2775− signature: CommandSignature::none(),
2776− },
2777− TypableCommand {
2778− name: "rsort",
2779− aliases: &[],
2780− doc: "Sort ranges in selection in reverse order.",
2781− fun: sort_reverse,
2782− signature: CommandSignature::none(),
2783− },
2784− TypableCommand {
2785− name: "reflow",
2786− aliases: &[],
2787− doc: "Hard-wrap the current selection of lines to a given width.",
2788− fun: reflow,
2789− signature: CommandSignature::none(),
2790− },
2791− TypableCommand {
2792− name: "tree-sitter-subtree",
2793− aliases: &["ts-subtree"],
2794− doc: "Display tree sitter subtree under cursor, primarily for debugging queries.",
2795− fun: tree_sitter_subtree,
2796− signature: CommandSignature::none(),
2797− },
2798− TypableCommand {
2799− name: "config-reload",
2800− aliases: &[],
2801− doc: "Refresh user config.",
2802− fun: refresh_config,
2803− signature: CommandSignature::none(),
2804− },
2805− TypableCommand {
2806− name: "config-open",
2807− aliases: &[],
2808− doc: "Open the user config.toml file.",
2809− fun: open_config,
2810− signature: CommandSignature::none(),
2811− },
2812− TypableCommand {
2813− name: "config-open-workspace",
2814− aliases: &[],
2815− doc: "Open the workspace config.toml file.",
2816− fun: open_workspace_config,
2817− signature: CommandSignature::none(),
2818− },
2819− TypableCommand {
2820− name: "log-open",
2821− aliases: &[],
2822− doc: "Open the helix log file.",
2823− fun: open_log,
2824− signature: CommandSignature::none(),
2825− },
2826− TypableCommand {
2827− name: "insert-output",
2828− aliases: &[],
2829− doc: "Run shell command, inserting output before each selection.",
2830− fun: insert_output,
2831− signature: CommandSignature::none(),
2832− },
2833− TypableCommand {
2834− name: "append-output",
2835− aliases: &[],
2836− doc: "Run shell command, appending output after each selection.",
2837− fun: append_output,
2838− signature: CommandSignature::none(),
2839− },
2840− TypableCommand {
2841− name: "pipe",
2842− aliases: &[],
2843− doc: "Pipe each selection to the shell command.",
2844− fun: pipe,
2845− signature: CommandSignature::none(),
2846− },
2847− TypableCommand {
2848− name: "pipe-to",
2849− aliases: &[],
2850− doc: "Pipe each selection to the shell command, ignoring output.",
2851− fun: pipe_to,
2852− signature: CommandSignature::none(),
2853− },
2854− TypableCommand {
2855− name: "run-shell-command",
2856− aliases: &["sh"],
2857− doc: "Run a shell command",
2858− fun: run_shell_command,
2859− signature: CommandSignature::all(completers::filename)
2860− },
2861− TypableCommand {
2862− name: "reset-diff-change",
2863− aliases: &["diffget", "diffg"],
2864− doc: "Reset the diff change at the cursor position.",
2865− fun: reset_diff_change,
2866− signature: CommandSignature::none(),
2867− },
2868− TypableCommand {
2869− name: "clear-register",
2870− aliases: &[],
2871− doc: "Clear given register. If no argument is provided, clear all registers.",
2872− fun: clear_register,
2873− signature: CommandSignature::none(),
2874− },
2875− ];
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+];
28763093
28773094pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
28783095 Lazy::new(|| {
@@ -2891,28 +3108,18 @@ pub(super) fn command_mode(cx: &mut Context) {
28913108 ":".into(),
28923109 Some(':'),
28933110 |editor: &Editor, input: &str| {
2894− static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
2895− Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
2896−
28973111 let shellwords = Shellwords::from(input);
28983112 let words = shellwords.words();
28993113
29003114 if words.is_empty() || (words.len() == 1 && !shellwords.ends_with_whitespace()) {
2901− // If the command has not been finished yet, complete commands.
2902− let mut matches: Vec<_> = typed::TYPABLE_COMMAND_LIST
2903− .iter()
2904− .filter_map(|command| {
2905− FUZZY_MATCHER
2906− .fuzzy_match(command.name, input)
2907− .map(|score| (command.name, score))
2908− })
2909− .collect();
2910−
2911− matches.sort_unstable_by_key(|(_file, score)| std::cmp::Reverse(*score));
2912− matches
2913− .into_iter()
2914− .map(|(name, _)| (0.., name.into()))
2915− .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()
29163123 } else {
29173124 // Otherwise, use the command's completer and the last shellword
29183125 // as completion input.