@@ -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
@@ -1282,17 +1278,12 @@ fn reload(
12821278 }
12831279
12841280 let scrolloff = cx.editor.config().scrolloff;
1281+ let redraw_handle = cx.editor.redraw_handle.clone();
12851282 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(())
1283+ doc.reload(view, &cx.editor.diff_providers, redraw_handle)
1284+ .map(|_| {
1285+ view.ensure_cursor_in_view(doc, scrolloff);
1286+ })
12961287}
12971288
12981289fn reload_all(
@@ -1331,13 +1322,8 @@ fn reload_all(
13311322 // Ensure that the view is synced with the document's history.
13321323 view.sync_changes(doc);
13331324
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− }
1325+ let redraw_handle = cx.editor.redraw_handle.clone();
1326+ doc.reload(view, &cx.editor.diff_providers, redraw_handle)?;
13411327
13421328 for view_id in view_ids {
13431329 let view = view_mut!(cx.editor, view_id);
@@ -1386,8 +1372,9 @@ fn lsp_workspace_command(
13861372 .map(|options| (ls.id(), options))
13871373 })
13881374 else {
1389− cx.editor
1390− .set_status("No active language servers for this document support workspace commands");
1375+ cx.editor.set_status(
1376+ "No active language servers for this document support workspace commands",
1377+ );
13911378 return Ok(());
13921379 };
13931380
@@ -1543,84 +1530,6 @@ fn tree_sitter_scopes(
15431530 Ok(())
15441531}
15451532
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−
16241533fn vsplit(
16251534 cx: &mut compositor::Context,
16261535 args: &[Cow<str>],
@@ -1630,8 +1539,10 @@ fn vsplit(
16301539 return Ok(());
16311540 }
16321541
1542+ let id = view!(cx.editor).doc;
1543+
16331544 if args.is_empty() {
1634− split(cx.editor, Action::VerticalSplit);
1545+ cx.editor.switch(id, Action::VerticalSplit);
16351546 } else {
16361547 for arg in args {
16371548 cx.editor
@@ -1651,8 +1562,10 @@ fn hsplit(
16511562 return Ok(());
16521563 }
16531564
1565+ let id = view!(cx.editor).doc;
1566+
16541567 if args.is_empty() {
1655− split(cx.editor, Action::HorizontalSplit);
1568+ cx.editor.switch(id, Action::HorizontalSplit);
16561569 } else {
16571570 for arg in args {
16581571 cx.editor
@@ -1767,7 +1680,7 @@ fn tutor(
17671680 let path = helix_loader::runtime_file(Path::new("tutor"));
17681681 cx.editor.open(&path, Action::Replace)?;
17691682 // Unset path to prevent accidentally saving to the original tutor file.
1770− doc_mut!(cx.editor).set_path(None);
1683+ doc_mut!(cx.editor).set_path(None)?;
17711684 Ok(())
17721685}
17731686
@@ -1944,29 +1857,14 @@ fn toggle_option(
19441857 .to_string(),
19451858 )
19461859 }
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(_) => {
1860+ Value::Null | Value::Object(_) | Value::Array(_) | Value::Number(_) => {
19631861 anyhow::bail!("Configuration {key} does not support toggle yet")
19641862 }
19651863 };
19661864
19671865 let status = format!("'{key}' is now set to {value}");
19681866 let config = serde_json::from_value(config)
1969− .map_err(|err| anyhow::anyhow!("Cannot parse `{:?}`, {}", &args, err))?;
1867+ .map_err(|_| anyhow::anyhow!("Could not parse field: `{:?}`", &args))?;
19701868
19711869 cx.editor
19721870 .config_events
@@ -2375,721 +2273,606 @@ fn clear_register(
23752273 format!("Invalid register {}", args[0])
23762274 );
23772275 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(());
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)),
23952283 }
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−
24822284 Ok(())
24832285}
24842286
24852287pub 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−];
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+ ];
30932876
30942877pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
30952878 Lazy::new(|| {
@@ -3108,18 +2891,28 @@ pub(super) fn command_mode(cx: &mut Context) {
31082891 ":".into(),
31092892 Some(':'),
31102893 |editor: &Editor, input: &str| {
2894+ static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
2895+ Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
2896+
31112897 let shellwords = Shellwords::from(input);
31122898 let words = shellwords.words();
31132899
31142900 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()
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()
31232916 } else {
31242917 // Otherwise, use the command's completer and the last shellword
31252918 // as completion input.