From 9ee03b03bef7aa5dcf6704e702b0df67218c643e Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Mon, 24 Feb 2025 00:37:55 +0000 Subject: [PATCH 1/9] feat: implement :left, :right and :center commands --- helix-term/src/commands/typed.rs | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 7ec594874..818e0570e 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -210,6 +210,77 @@ fn buffer_gather_paths_impl(editor: &mut Editor, args: &[Cow]) -> Vec], + event: PromptEvent, + format: fn(usize, &str) -> String, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + let editor_text_width = cx.editor.config().text_width; + let (view, doc) = current!(cx.editor); + let text = doc.text().slice(..); + + let custom_text_width = args.first().map(|arg| { + arg.parse::() + .map_err(|_| anyhow!("Could not parse argument as a number: {arg}")) + }); + + let text_width = custom_text_width.unwrap_or(Ok(doc + .language_config() + .and_then(|c| c.text_width) + .unwrap_or(editor_text_width)))?; + + let lines = get_lines(doc, view.id); + let mut changes = Vec::with_capacity(lines.len()); + + for line_idx in lines { + let line = doc.text().line(line_idx); + let line = line.to_string(); + + // pad with spaces + // let line = format!("{:^text_width$}", line.trim()); + let line = format(text_width, line.trim()); + + changes.push(( + text.line_to_char(line_idx), + text.line_to_char(line_idx + 1) - doc.line_ending.len_chars(), + Some(Tendril::from(line)), + )) + } + + let transaction = Transaction::change(doc.text(), changes.into_iter()); + + doc.apply(&transaction, view.id); + + Ok(()) +} + +fn left(cx: &mut compositor::Context, args: &[Cow], event: PromptEvent) -> anyhow::Result<()> { + ensure!(args.len() <= 1, ":left takes at most 1 argument"); + align_text_impl(cx, args, event, |a, b| format!("{:], + event: PromptEvent, +) -> anyhow::Result<()> { + ensure!(args.len() <= 1, ":center takes at most 1 argument"); + align_text_impl(cx, args, event, |a, b| format!("{:^a$}", b)) +} + +fn right( + cx: &mut compositor::Context, + args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + ensure!(args.len() <= 1, ":right takes at most 1 argument"); + align_text_impl(cx, args, event, |a, b| format!("{:>a$}", b)) +} + fn buffer_close( cx: &mut compositor::Context, args: &[Cow], @@ -3256,6 +3327,27 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: read, signature: CommandSignature::positional(&[completers::filename]), }, + TypableCommand { + name: "left", + aliases: &[], + doc: "Align text to the left", + fun: left, + signature: CommandSignature::none(), + }, + TypableCommand { + name: "center", + aliases: &[], + doc: "Center-align text", + fun: center, + signature: CommandSignature::none(), + }, + TypableCommand { + name: "right", + aliases: &[], + doc: "Align text to the right", + fun: right, + signature: CommandSignature::none(), + }, ]; pub static TYPABLE_COMMAND_MAP: Lazy> = From 64c1f234b0f616a128becd3f51b2ca7c4acd4fcd Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Mon, 24 Feb 2025 00:38:34 +0000 Subject: [PATCH 2/9] refactor: rename variables _ refactor: remove useless comment --- helix-term/src/commands/typed.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 818e0570e..3a83501f2 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -240,14 +240,13 @@ fn align_text_impl( let line = doc.text().line(line_idx); let line = line.to_string(); - // pad with spaces - // let line = format!("{:^text_width$}", line.trim()); let line = format(text_width, line.trim()); + let tendril = Tendril::from(line); changes.push(( text.line_to_char(line_idx), text.line_to_char(line_idx + 1) - doc.line_ending.len_chars(), - Some(Tendril::from(line)), + Some(tendril), )) } @@ -260,7 +259,9 @@ fn align_text_impl( fn left(cx: &mut compositor::Context, args: &[Cow], event: PromptEvent) -> anyhow::Result<()> { ensure!(args.len() <= 1, ":left takes at most 1 argument"); - align_text_impl(cx, args, event, |a, b| format!("{: anyhow::Result<()> { ensure!(args.len() <= 1, ":center takes at most 1 argument"); - align_text_impl(cx, args, event, |a, b| format!("{:^a$}", b)) + align_text_impl(cx, args, event, |text_width, text| { + format!("{:^text_width$}", text) + }) } fn right( @@ -278,7 +281,9 @@ fn right( event: PromptEvent, ) -> anyhow::Result<()> { ensure!(args.len() <= 1, ":right takes at most 1 argument"); - align_text_impl(cx, args, event, |a, b| format!("{:>a$}", b)) + align_text_impl(cx, args, event, |text_width, text| { + format!("{:>text_width$}", text) + }) } fn buffer_close( From f4679232fe4f2e2974d28a58be4ddc825ea9432a Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Mon, 24 Feb 2025 01:00:21 +0000 Subject: [PATCH 3/9] docs: generate docs for `:center`, `:left` and `:right` --- book/src/generated/typable-cmd.md | 3 +++ helix-term/src/commands/typed.rs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 55820e08b..1a095592b 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -88,3 +88,6 @@ | `:move`, `:mv` | Move the current buffer and its corresponding file to a different path | | `:yank-diagnostic` | Yank diagnostic(s) under primary cursor to register, or clipboard by default | | `:read`, `:r` | Load a file into buffer | +| `:left` | Align text to the left, optionally pass a number overriding the current document's text width | +| `:center` | Center-align text, optionally pass a number overriding the current document's text width | +| `:right` | Align text to the right, optionally pass a number overriding the current document's text width | diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 3a83501f2..260932ba6 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -3335,21 +3335,21 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "left", aliases: &[], - doc: "Align text to the left", + doc: "Align text to the left, optionally pass a number overriding the current document's text width", fun: left, signature: CommandSignature::none(), }, TypableCommand { name: "center", aliases: &[], - doc: "Center-align text", + doc: "Center-align text, optionally pass a number overriding the current document's text width", fun: center, signature: CommandSignature::none(), }, TypableCommand { name: "right", aliases: &[], - doc: "Align text to the right", + doc: "Align text to the right, optionally pass a number overriding the current document's text width", fun: right, signature: CommandSignature::none(), }, From 0708c5c93d4954b716812f7e512fa46a7a6bb1bf Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Mon, 24 Feb 2025 01:27:58 +0000 Subject: [PATCH 4/9] fix: remove trailing whitespace on align line command --- helix-term/src/commands/typed.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 260932ba6..f4cf20905 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -238,10 +238,11 @@ fn align_text_impl( for line_idx in lines { let line = doc.text().line(line_idx); - let line = line.to_string(); + let old_line = line.to_string(); - let line = format(text_width, line.trim()); - let tendril = Tendril::from(line); + let aligned_line = format(text_width, old_line.trim()); + let aligned_line = aligned_line.trim_end(); + let tendril = Tendril::from(aligned_line); changes.push(( text.line_to_char(line_idx), From 7e77b15b53e9c1316477536ebd373171eff9bbf6 Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:50:44 +0000 Subject: [PATCH 5/9] fix: use Args struct for Args --- helix-term/src/commands/typed.rs | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 95d851ce1..928c396a1 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -206,7 +206,7 @@ fn buffer_gather_paths_impl(editor: &mut Editor, args: Args) -> Vec fn align_text_impl( cx: &mut compositor::Context, - args: &[Cow], + args: Args, event: PromptEvent, format: fn(usize, &str) -> String, ) -> anyhow::Result<()> { @@ -252,30 +252,19 @@ fn align_text_impl( Ok(()) } -fn left(cx: &mut compositor::Context, args: &[Cow], event: PromptEvent) -> anyhow::Result<()> { - ensure!(args.len() <= 1, ":left takes at most 1 argument"); +fn left(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> { align_text_impl(cx, args, event, |text_width, text| { format!("{:], - event: PromptEvent, -) -> anyhow::Result<()> { - ensure!(args.len() <= 1, ":center takes at most 1 argument"); +fn center(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> { align_text_impl(cx, args, event, |text_width, text| { format!("{:^text_width$}", text) }) } -fn right( - cx: &mut compositor::Context, - args: &[Cow], - event: PromptEvent, -) -> anyhow::Result<()> { - ensure!(args.len() <= 1, ":right takes at most 1 argument"); +fn right(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> { align_text_impl(cx, args, event, |text_width, text| { format!("{:>text_width$}", text) }) @@ -3575,21 +3564,33 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ aliases: &[], doc: "Align text to the left, optionally pass a number overriding the current document's text width", fun: left, - signature: CommandSignature::none(), + completer: CommandCompleter::none(), + signature: Signature { + positionals: (0, Some(0)), + ..Signature::DEFAULT + }, }, TypableCommand { name: "center", aliases: &[], doc: "Center-align text, optionally pass a number overriding the current document's text width", fun: center, - signature: CommandSignature::none(), + completer: CommandCompleter::none(), + signature: Signature { + positionals: (0, Some(1)), + ..Signature::DEFAULT + }, }, TypableCommand { name: "right", aliases: &[], doc: "Align text to the right, optionally pass a number overriding the current document's text width", fun: right, - signature: CommandSignature::none(), + completer: CommandCompleter::none(), + signature: Signature { + positionals: (0, Some(1)), + ..Signature::DEFAULT + }, }, ]; From d048c6ec2171f686350ca9f12a10f015804ecde3 Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:20:31 +0000 Subject: [PATCH 6/9] docs: item ordering --- book/src/generated/typable-cmd.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 4b378be8a..45eed674e 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -87,8 +87,8 @@ | `:move`, `:mv` | Move the current buffer and its corresponding file to a different path | | `:yank-diagnostic` | Yank diagnostic(s) under primary cursor to register, or clipboard by default | | `:read`, `:r` | Load a file into buffer | +| `:echo` | Prints the given arguments to the statusline. | +| `:noop` | Does nothing. | | `:left` | Align text to the left, optionally pass a number overriding the current document's text width | | `:center` | Center-align text, optionally pass a number overriding the current document's text width | | `:right` | Align text to the right, optionally pass a number overriding the current document's text width | -| `:echo` | Prints the given arguments to the statusline. | -| `:noop` | Does nothing. | From b9f60bdfaa73e0682e6fd1ab1b481bd9c1f385b0 Mon Sep 17 00:00:00 2001 From: Nik Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:35:05 +0000 Subject: [PATCH 7/9] fix: `:left` does not take an argument --- book/src/generated/typable-cmd.md | 2 +- helix-term/src/commands/typed.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 45eed674e..3388a0827 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -89,6 +89,6 @@ | `:read`, `:r` | Load a file into buffer | | `:echo` | Prints the given arguments to the statusline. | | `:noop` | Does nothing. | -| `:left` | Align text to the left, optionally pass a number overriding the current document's text width | +| `:left` | Align text to the left | | `:center` | Center-align text, optionally pass a number overriding the current document's text width | | `:right` | Align text to the right, optionally pass a number overriding the current document's text width | diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 928c396a1..b3ad18c91 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -3562,7 +3562,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "left", aliases: &[], - doc: "Align text to the left, optionally pass a number overriding the current document's text width", + doc: "Align text to the left", fun: left, completer: CommandCompleter::none(), signature: Signature { From f83044566adb177630420db9294dd3731edb824b Mon Sep 17 00:00:00 2001 From: Nik Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:02:04 +0100 Subject: [PATCH 8/9] feat: rename to `:align-text` --- book/src/generated/typable-cmd.md | 6 +++--- helix-term/src/commands/typed.rs | 36 ++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 3388a0827..5b89191b6 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -89,6 +89,6 @@ | `:read`, `:r` | Load a file into buffer | | `:echo` | Prints the given arguments to the statusline. | | `:noop` | Does nothing. | -| `:left` | Align text to the left | -| `:center` | Center-align text, optionally pass a number overriding the current document's text width | -| `:right` | Align text to the right, optionally pass a number overriding the current document's text width | +| `:align-text-left`, `:atl` | Align text to the left | +| `:align-text-center`, `:atc` | Center-align text, optionally pass a number overriding the current document's text width | +| `:align-text-right`, `:atr` | Align text to the right, optionally pass a number overriding the current document's text width | diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index b3ad18c91..668793e50 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -252,19 +252,31 @@ fn align_text_impl( Ok(()) } -fn left(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> { +fn align_text_left( + cx: &mut compositor::Context, + args: Args, + event: PromptEvent, +) -> anyhow::Result<()> { align_text_impl(cx, args, event, |text_width, text| { format!("{: anyhow::Result<()> { +fn align_text_center( + cx: &mut compositor::Context, + args: Args, + event: PromptEvent, +) -> anyhow::Result<()> { align_text_impl(cx, args, event, |text_width, text| { format!("{:^text_width$}", text) }) } -fn right(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> { +fn align_text_right( + cx: &mut compositor::Context, + args: Args, + event: PromptEvent, +) -> anyhow::Result<()> { align_text_impl(cx, args, event, |text_width, text| { format!("{:>text_width$}", text) }) @@ -3560,10 +3572,10 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ }, }, TypableCommand { - name: "left", - aliases: &[], + name: "align-text-left", + aliases: &["atl"], doc: "Align text to the left", - fun: left, + fun: align_text_left, completer: CommandCompleter::none(), signature: Signature { positionals: (0, Some(0)), @@ -3571,10 +3583,10 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ }, }, TypableCommand { - name: "center", - aliases: &[], + name: "align-text-center", + aliases: &["atc"], doc: "Center-align text, optionally pass a number overriding the current document's text width", - fun: center, + fun: align_text_center, completer: CommandCompleter::none(), signature: Signature { positionals: (0, Some(1)), @@ -3582,10 +3594,10 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ }, }, TypableCommand { - name: "right", - aliases: &[], + name: "align-text-right", + aliases: &["atr"], doc: "Align text to the right, optionally pass a number overriding the current document's text width", - fun: right, + fun: align_text_right, completer: CommandCompleter::none(), signature: Signature { positionals: (0, Some(1)), From 893f4c8ece7d9869b4f0e08b8e1483c9a1c7e65e Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Wed, 11 Jun 2025 12:42:46 +0100 Subject: [PATCH 9/9] test: add tests for align-text --- helix-term/tests/test/commands.rs | 1 + helix-term/tests/test/commands/align_text.rs | 65 ++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 helix-term/tests/test/commands/align_text.rs diff --git a/helix-term/tests/test/commands.rs b/helix-term/tests/test/commands.rs index 2af1a054f..64e8ab1cb 100644 --- a/helix-term/tests/test/commands.rs +++ b/helix-term/tests/test/commands.rs @@ -2,6 +2,7 @@ use helix_term::application::Application; use super::*; +mod align_text; mod insert; mod movement; mod write; diff --git a/helix-term/tests/test/commands/align_text.rs b/helix-term/tests/test/commands/align_text.rs new file mode 100644 index 000000000..795cae565 --- /dev/null +++ b/helix-term/tests/test/commands/align_text.rs @@ -0,0 +1,65 @@ +use super::*; + +const IN: &str = indoc! {" + #[pub fn docgen() -> Result<(), DynError> { + use crate::docgen::*; + write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?); + write(STATIC_COMMANDS_MD_OUTPUT, &static_commands()?); + write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?); + Ok(()) + }\n|]#"}; + +#[tokio::test(flavor = "multi_thread")] +async fn left() -> anyhow::Result<()> { + test(( + IN, + ":align-text-left", + indoc! {"\ + #[pub fn docgen() -> Result<(), DynError> { + use crate::docgen::*; + write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?); + write(STATIC_COMMANDS_MD_OUTPUT, &static_commands()?); + write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?); + Ok(()) + }\n|]#"}, + )) + .await?; + + Ok(()) +} +#[tokio::test(flavor = "multi_thread")] +async fn center() -> anyhow::Result<()> { + test(( + IN, + ":align-text-center", + indoc! {"\ + #[ pub fn docgen() -> Result<(), DynError> { + use crate::docgen::*; + write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?); + write(STATIC_COMMANDS_MD_OUTPUT, &static_commands()?); + write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?); + Ok(()) + }\n|]#"}, + )) + .await?; + + Ok(()) +} +#[tokio::test(flavor = "multi_thread")] +async fn right() -> anyhow::Result<()> { + test(( + IN, + ":align-text-right", + indoc! {"\ + #[ pub fn docgen() -> Result<(), DynError> { + use crate::docgen::*; + write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?); + write(STATIC_COMMANDS_MD_OUTPUT, &static_commands()?); + write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?); + Ok(()) + }\n|]#"}, + )) + .await?; + + Ok(()) +}