@@ -5,10 +5,7 @@ use crate::job::Job;
55
66use super::*;
77
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};
8+use helix_core::{encoding, shellwords::Shellwords};
129use helix_view::document::DEFAULT_LANGUAGE_NAME;
1310use helix_view::editor::{Action, CloseError, ConfigEvent};
1411use serde_json::Value;
@@ -332,16 +329,12 @@ fn write_impl(
332329 path: Option<&Cow<str>>,
333330 force: bool,
334331) -> anyhow::Result<()> {
335− let config = cx.editor.config();
332+ let editor_auto_fmt = cx.editor.config().auto_format;
336333 let jobs = &mut cx.jobs;
337334 let (view, doc) = current!(cx.editor);
338335 let path = path.map(AsRef::as_ref);
339336
340− if config.insert_final_newline {
341− insert_final_newline(doc, view);
342− }
343−
344− let fmt = if config.auto_format {
337+ let fmt = if editor_auto_fmt {
345338 doc.auto_format().map(|fmt| {
346339 let callback = make_format_callback(
347340 doc.id(),
@@ -365,16 +358,6 @@ fn write_impl(
365358 Ok(())
366359}
367360
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−
378361fn write(
379362 cx: &mut compositor::Context,
380363 args: &[Cow<str>],
@@ -477,7 +460,8 @@ fn set_indent_style(
477460 cx.editor.set_status(match style {
478461 Tabs => "tabs".to_owned(),
479462 Spaces(1) => "1 space".to_owned(),
480− Spaces(n) => format!("{} spaces", n),
463+ Spaces(n) if (2..=8).contains(&n) => format!("{} spaces", n),
464+ _ => unreachable!(), // Shouldn't happen.
481465 });
482466 return Ok(());
483467 }
@@ -489,7 +473,7 @@ fn set_indent_style(
489473 Some(arg) => arg
490474 .parse::<u8>()
491475 .ok()
492− .filter(|n| (1..=MAX_INDENT).contains(n))
476+ .filter(|n| (1..=8).contains(n))
493477 .map(Spaces),
494478 _ => None,
495479 };
@@ -574,6 +558,7 @@ fn set_line_ending(
574558
575559 Ok(())
576560}
561+
577562fn earlier(
578563 cx: &mut compositor::Context,
579564 args: &[Cow<str>],
@@ -672,10 +657,11 @@ pub fn write_all_impl(
672657 write_scratch: bool,
673658) -> anyhow::Result<()> {
674659 let mut errors: Vec<&'static str> = Vec::new();
675− let config = cx.editor.config();
660+ let auto_format = cx.editor.config().auto_format;
676661 let jobs = &mut cx.jobs;
677662 let current_view = view!(cx.editor);
678663
664+ // save all documents
679665 let saves: Vec<_> = cx
680666 .editor
681667 .documents
@@ -686,7 +672,7 @@ pub fn write_all_impl(
686672 }
687673 if doc.path().is_none() {
688674 if write_scratch {
689− errors.push("cannot write a buffer without a filename");
675+ errors.push("cannot write a buffer without a filename\n");
690676 }
691677 return None;
692678 }
@@ -706,35 +692,32 @@ pub fn write_all_impl(
706692 current_view.id
707693 };
708694
709− Some((doc.id(), target_view))
710− })
711− .collect();
712−
713− for (doc_id, target_view) in saves {
714− let doc = doc_mut!(cx.editor, &doc_id);
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+ };
715709
716− if config.insert_final_newline {
717− insert_final_newline(doc, view_mut!(cx.editor, target_view));
718− }
710+ if fmt.is_none() {
711+ return Some(doc.id());
712+ }
719713
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 {
732714 None
733− };
715+ })
716+ .collect();
734717
735− if fmt.is_none() {
736− cx.editor.save::<PathBuf>(doc_id, None, force)?;
737− }
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)?;
738721 }
739722
740723 if !errors.is_empty() && !force {
@@ -921,8 +904,7 @@ fn yank_main_selection_to_clipboard(
921904 return Ok(());
922905 }
923906
924− yank_primary_selection_impl(cx.editor, '+');
925− Ok(())
907+ yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard)
926908}
927909
928910fn yank_joined(
@@ -956,8 +938,7 @@ fn yank_joined_to_clipboard(
956938 let doc = doc!(cx.editor);
957939 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
958940 let separator = args.first().unwrap_or(&default_sep);
959− yank_joined_impl(cx.editor, separator, '+');
960− Ok(())
941+ yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Clipboard)
961942}
962943
963944fn yank_main_selection_to_primary_clipboard(
@@ -969,8 +950,7 @@ fn yank_main_selection_to_primary_clipboard(
969950 return Ok(());
970951 }
971952
972− yank_primary_selection_impl(cx.editor, '*');
973− Ok(())
953+ yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection)
974954}
975955
976956fn yank_joined_to_primary_clipboard(
@@ -985,8 +965,7 @@ fn yank_joined_to_primary_clipboard(
985965 let doc = doc!(cx.editor);
986966 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
987967 let separator = args.first().unwrap_or(&default_sep);
988− yank_joined_impl(cx.editor, separator, '*');
989− Ok(())
968+ yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Selection)
990969}
991970
992971fn paste_clipboard_after(
@@ -998,8 +977,7 @@ fn paste_clipboard_after(
998977 return Ok(());
999978 }
1000979
1001− paste(cx.editor, '+', Paste::After, 1);
1002− Ok(())
980+ paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard, 1)
1003981}
1004982
1005983fn paste_clipboard_before(
@@ -1011,8 +989,7 @@ fn paste_clipboard_before(
1011989 return Ok(());
1012990 }
1013991
1014− paste(cx.editor, '+', Paste::Before, 1);
1015− Ok(())
992+ paste_clipboard_impl(cx.editor, Paste::Before, ClipboardType::Clipboard, 1)
1016993}
1017994
1018995fn paste_primary_clipboard_after(
@@ -1024,8 +1001,7 @@ fn paste_primary_clipboard_after(
10241001 return Ok(());
10251002 }
10261003
1027− paste(cx.editor, '*', Paste::After, 1);
1028− Ok(())
1004+ paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection, 1)
10291005}
10301006
10311007fn paste_primary_clipboard_before(
@@ -1037,8 +1013,30 @@ fn paste_primary_clipboard_before(
10371013 return Ok(());
10381014 }
10391015
1040− paste(cx.editor, '*', Paste::Before, 1);
1041− Ok(())
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+ }
10421040}
10431041
10441042fn replace_selections_with_clipboard(
@@ -1050,8 +1048,7 @@ fn replace_selections_with_clipboard(
10501048 return Ok(());
10511049 }
10521050
1053− replace_with_yanked_impl(cx.editor, '+', 1);
1054− Ok(())
1051+ replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard)
10551052}
10561053
10571054fn replace_selections_with_primary_clipboard(
@@ -1063,8 +1060,7 @@ fn replace_selections_with_primary_clipboard(
10631060 return Ok(());
10641061 }
10651062
1066− replace_with_yanked_impl(cx.editor, '*', 1);
1067− Ok(())
1063+ replace_selections_with_clipboard_impl(cx, ClipboardType::Selection)
10681064}
10691065
10701066fn show_clipboard_provider(
@@ -1077,7 +1073,7 @@ fn show_clipboard_provider(
10771073 }
10781074
10791075 cx.editor
1080− .set_status(cx.editor.registers.clipboard_provider_name().to_string());
1076+ .set_status(cx.editor.clipboard_provider.name().to_string());
10811077 Ok(())
10821078}
10831079
@@ -1097,11 +1093,14 @@ fn change_current_directory(
10971093 .as_ref(),
10981094 );
10991095
1100− helix_loader::set_current_working_dir(dir)?;
1096+ if let Err(e) = std::env::set_current_dir(dir) {
1097+ bail!("Couldn't change the current working directory: {}", e);
1098+ }
11011099
1100+ let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
11021101 cx.editor.set_status(format!(
11031102 "Current working directory is now {}",
1104− helix_loader::current_working_dir().display()
1103+ cwd.display()
11051104 ));
11061105 Ok(())
11071106}
@@ -1115,14 +1114,9 @@ fn show_current_directory(
11151114 return Ok(());
11161115 }
11171116
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− }
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()));
11261120 Ok(())
11271121}
11281122
@@ -1282,17 +1276,12 @@ fn reload(
12821276 }
12831277
12841278 let scrolloff = cx.editor.config().scrolloff;
1279+ let redraw_handle = cx.editor.redraw_handle.clone();
12851280 let (view, doc) = current!(cx.editor);
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(())
1281+ doc.reload(view, &cx.editor.diff_providers, redraw_handle)
1282+ .map(|_| {
1283+ view.ensure_cursor_in_view(doc, scrolloff);
1284+ })
12961285}
12971286
12981287fn reload_all(
@@ -1331,13 +1320,8 @@ fn reload_all(
13311320 // Ensure that the view is synced with the document's history.
13321321 view.sync_changes(doc);
13331322
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− }
1323+ let redraw_handle = cx.editor.redraw_handle.clone();
1324+ doc.reload(view, &cx.editor.diff_providers, redraw_handle)?;
13411325
13421326 for view_id in view_ids {
13431327 let view = view_mut!(cx.editor, view_id);
@@ -1386,8 +1370,9 @@ fn lsp_workspace_command(
13861370 .map(|options| (ls.id(), options))
13871371 })
13881372 else {
1389− cx.editor
1390− .set_status("No active language servers for this document support workspace commands");
1373+ cx.editor.set_status(
1374+ "No active language servers for this document support workspace commands",
1375+ );
13911376 return Ok(());
13921377 };
13931378
@@ -1543,84 +1528,6 @@ fn tree_sitter_scopes(
15431528 Ok(())
15441529}
15451530
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−
16241531fn vsplit(
16251532 cx: &mut compositor::Context,
16261533 args: &[Cow<str>],
@@ -1630,8 +1537,10 @@ fn vsplit(
16301537 return Ok(());
16311538 }
16321539
1540+ let id = view!(cx.editor).doc;
1541+
16331542 if args.is_empty() {
1634− split(cx.editor, Action::VerticalSplit);
1543+ cx.editor.switch(id, Action::VerticalSplit);
16351544 } else {
16361545 for arg in args {
16371546 cx.editor
@@ -1651,8 +1560,10 @@ fn hsplit(
16511560 return Ok(());
16521561 }
16531562
1563+ let id = view!(cx.editor).doc;
1564+
16541565 if args.is_empty() {
1655− split(cx.editor, Action::HorizontalSplit);
1566+ cx.editor.switch(id, Action::HorizontalSplit);
16561567 } else {
16571568 for arg in args {
16581569 cx.editor
@@ -1767,7 +1678,7 @@ fn tutor(
17671678 let path = helix_loader::runtime_file(Path::new("tutor"));
17681679 cx.editor.open(&path, Action::Replace)?;
17691680 // Unset path to prevent accidentally saving to the original tutor file.
1770− doc_mut!(cx.editor).set_path(None);
1681+ doc_mut!(cx.editor).set_path(None)?;
17711682 Ok(())
17721683}
17731684
@@ -1944,29 +1855,14 @@ fn toggle_option(
19441855 .to_string(),
19451856 )
19461857 }
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(_) => {
1858+ Value::Null | Value::Object(_) | Value::Array(_) | Value::Number(_) => {
19631859 anyhow::bail!("Configuration {key} does not support toggle yet")
19641860 }
19651861 };
19661862
19671863 let status = format!("'{key}' is now set to {value}");
19681864 let config = serde_json::from_value(config)
1969− .map_err(|err| anyhow::anyhow!("Cannot parse `{:?}`, {}", &args, err))?;
1865+ .map_err(|_| anyhow::anyhow!("Could not parse field: `{:?}`", &args))?;
19701866
19711867 cx.editor
19721868 .config_events
@@ -2375,721 +2271,606 @@ fn clear_register(
23752271 format!("Invalid register {}", args[0])
23762272 );
23772273 let register = args[0].chars().next().unwrap_or_default();
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(());
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)),
24182281 }
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−
24822282 Ok(())
24832283}
24842284
24852285pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
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−];
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+ ];
30932874
30942875pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
30952876 Lazy::new(|| {
@@ -3108,18 +2889,28 @@ pub(super) fn command_mode(cx: &mut Context) {
31082889 ":".into(),
31092890 Some(':'),
31102891 |editor: &Editor, input: &str| {
2892+ static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
2893+ Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
2894+
31112895 let shellwords = Shellwords::from(input);
31122896 let words = shellwords.words();
31132897
31142898 if words.is_empty() || (words.len() == 1 && !shellwords.ends_with_whitespace()) {
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()
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()
31232914 } else {
31242915 // Otherwise, use the command's completer and the last shellword
31252916 // as completion input.