mirror of https://github.com/helix-editor/helix
Merge ff250d86f0
into 205e7ece70
commit
62f15e95a9
|
@ -70,6 +70,7 @@
|
|||
| `:toggle-option`, `:toggle` | Toggle a config option at runtime.<br>For example to toggle smart case search, use `:toggle search.smart-case`. |
|
||||
| `:get-option`, `:get` | Get the current value of a config option. |
|
||||
| `:sort` | Sort ranges in selection. |
|
||||
| `:index` | Inserts indexes into selections. |
|
||||
| `:reflow` | Hard-wrap the current selection of lines to a given width. |
|
||||
| `:tree-sitter-subtree`, `:ts-subtree` | Display the smallest tree-sitter subtree that spans the primary selection, primarily for debugging queries. |
|
||||
| `:config-reload` | Refresh user config. |
|
||||
|
|
|
@ -9,7 +9,7 @@ use super::*;
|
|||
use helix_core::command_line::{Args, Flag, Signature, Token, TokenKind};
|
||||
use helix_core::fuzzy::fuzzy_match;
|
||||
use helix_core::indent::MAX_INDENT;
|
||||
use helix_core::line_ending;
|
||||
use helix_core::{line_ending, SmartString};
|
||||
use helix_stdx::path::home_dir;
|
||||
use helix_view::document::{read_to_string, DEFAULT_LANGUAGE_NAME};
|
||||
use helix_view::editor::{CloseError, ConfigEvent};
|
||||
|
@ -2146,6 +2146,115 @@ fn sort(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow:
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn index(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let step = if let Some(arg) = args.first() {
|
||||
arg.parse()
|
||||
.ok()
|
||||
.filter(|&step| step != 0)
|
||||
.context("Step must be a positive integer greater than zero")?
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let start = if let Some(arg) = args.get_flag("start") {
|
||||
arg.parse().context("Argument to --start must be an integer")?
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
index_impl(
|
||||
cx,
|
||||
args.has_flag("reverse"),
|
||||
args.has_flag("desc"),
|
||||
args.has_flag("pad"),
|
||||
start,
|
||||
step,
|
||||
)
|
||||
}
|
||||
|
||||
fn index_impl(
|
||||
cx: &mut compositor::Context,
|
||||
reverse: bool,
|
||||
desc: bool,
|
||||
pad: bool,
|
||||
start: isize,
|
||||
step: usize,
|
||||
) -> anyhow::Result<()> {
|
||||
let scrolloff = cx.editor.config().scrolloff;
|
||||
let (view, doc) = current!(cx.editor);
|
||||
let text = doc.text().slice(..);
|
||||
|
||||
let selection = doc.selection(view.id);
|
||||
|
||||
if selection.len() == 1 {
|
||||
bail!("Sorting requires multiple selections. Hint: split selection first");
|
||||
}
|
||||
|
||||
let mut fragments: Vec<_> = selection
|
||||
.slices(text)
|
||||
.map(|fragment| fragment.chunks().collect())
|
||||
.collect();
|
||||
|
||||
let count_selections = fragments.len();
|
||||
|
||||
let mut iter: Vec<isize> = if desc {
|
||||
let start_from = start - ((count_selections - 1) * step) as isize;
|
||||
(start_from..=start)
|
||||
.rev()
|
||||
.step_by(step)
|
||||
.take(count_selections)
|
||||
.collect()
|
||||
} else {
|
||||
(start..).step_by(step).take(count_selections).collect()
|
||||
};
|
||||
|
||||
if reverse {
|
||||
iter.reverse();
|
||||
}
|
||||
|
||||
fragments.iter_mut().zip(&iter).for_each(|(frag, index)| {
|
||||
let index_str = if pad {
|
||||
let width = iter
|
||||
.iter()
|
||||
.map(|&num| {
|
||||
if num == 0 {
|
||||
return 1;
|
||||
}
|
||||
let width = num.abs().ilog10() as usize + 1;
|
||||
if num > 0 {
|
||||
width
|
||||
} else {
|
||||
width + 1
|
||||
}
|
||||
})
|
||||
.max()
|
||||
.unwrap(); // we already checked that we have multiple selections
|
||||
format!("{:0width$}", index, width = width)
|
||||
} else {
|
||||
index.to_string()
|
||||
};
|
||||
*frag = SmartString::from(index_str);
|
||||
});
|
||||
|
||||
let transaction = Transaction::change(
|
||||
doc.text(),
|
||||
selection
|
||||
.into_iter()
|
||||
.zip(fragments)
|
||||
.map(|(s, fragment)| (s.from(), s.to(), Some(fragment))),
|
||||
);
|
||||
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
view.ensure_cursor_in_view(doc, scrolloff);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reflow(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> {
|
||||
if event != PromptEvent::Validate {
|
||||
return Ok(());
|
||||
|
@ -3373,6 +3482,43 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
|||
..Signature::DEFAULT
|
||||
},
|
||||
},
|
||||
TypableCommand {
|
||||
name: "index",
|
||||
aliases: &["i"],
|
||||
doc: "Inserts indexes into selections.",
|
||||
fun: index,
|
||||
completer: CommandCompleter::none(),
|
||||
signature: Signature {
|
||||
positionals: (0, Some(1)),
|
||||
flags: &[
|
||||
Flag {
|
||||
name: "start",
|
||||
alias: Some('x'),
|
||||
doc: "Set the starting number to count from",
|
||||
completions: Some(&[])
|
||||
},
|
||||
Flag {
|
||||
name: "reverse",
|
||||
alias: Some('r'),
|
||||
doc: "Index in reverse order",
|
||||
..Flag::DEFAULT
|
||||
},
|
||||
Flag {
|
||||
name: "desc",
|
||||
alias: Some('d'),
|
||||
doc: "Index in descending order",
|
||||
..Flag::DEFAULT
|
||||
},
|
||||
Flag {
|
||||
name: "pad",
|
||||
alias: Some('p'),
|
||||
doc: "Add leading zeros to start",
|
||||
..Flag::DEFAULT
|
||||
},
|
||||
],
|
||||
..Signature::DEFAULT
|
||||
},
|
||||
},
|
||||
TypableCommand {
|
||||
name: "reflow",
|
||||
aliases: &[],
|
||||
|
|
Loading…
Reference in New Issue