aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob2021-11-23 14:08:05 +0000
committerGitHub2021-11-23 14:08:05 +0000
commit21143e8d22c13ead8c1835063acb518aa5a42822 (patch)
tree25dadbba9b63809edb00d8dff42eb02e14377d41
parentf24e5a3c414363c6847fd7e806b434f8a6772ef9 (diff)
Align selections via & (#1101)
* align lines * remove log statement * use selections to align * fix a clippy issue * only accept 1,2,3 as user count * Update helix-term/src/commands.rs Co-authored-by: Ivan Tham <pickfire@riseup.net> * return if user count is not correct * add doc Co-authored-by: Ivan Tham <pickfire@riseup.net>
-rw-r--r--book/src/keymap.md1
-rw-r--r--helix-term/src/commands.rs71
-rw-r--r--helix-term/src/keymap.rs2
3 files changed, 73 insertions, 1 deletions
diff --git a/book/src/keymap.md b/book/src/keymap.md
index c88ed767..fbe77267 100644
--- a/book/src/keymap.md
+++ b/book/src/keymap.md
@@ -92,6 +92,7 @@
| `s` | Select all regex matches inside selections | `select_regex` |
| `S` | Split selection into subselections on regex matches | `split_selection` |
| `Alt-s` | Split selection on newlines | `split_selection_on_newline` |
+| `&` | Align selection in columns | `align_selections` |
| `_` | Trim whitespace from the selection | `trim_selections` |
| `;` | Collapse selection onto a single cursor | `collapse_selection` |
| `Alt-;` | Flip selection cursor and anchor | `flip_selections` |
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index fde505fd..a7179c30 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -314,6 +314,7 @@ impl Command {
join_selections, "Join lines inside selection",
keep_selections, "Keep selections matching regex",
remove_selections, "Remove selections matching regex",
+ align_selections, "Align selections in column",
keep_primary_selection, "Keep primary selection",
remove_primary_selection, "Remove primary selection",
completion, "Invoke completion popup",
@@ -657,6 +658,76 @@ fn trim_selections(cx: &mut Context) {
};
}
+// align text in selection
+fn align_selections(cx: &mut Context) {
+ let align_style = cx.count();
+ if align_style > 3 {
+ cx.editor.set_error(
+ "align only accept 1,2,3 as count to set left/center/right align".to_string(),
+ );
+ return;
+ }
+
+ let (view, doc) = current!(cx.editor);
+ let text = doc.text().slice(..);
+ let selection = doc.selection(view.id);
+ let mut column_widths = vec![];
+ let mut last_line = text.len_lines();
+ let mut column = 0;
+ // first of all, we need compute all column's width, let use max width of the selections in a column
+ for sel in selection {
+ let (l1, l2) = sel.line_range(text);
+ if l1 != l2 {
+ cx.editor
+ .set_error("align cannot work with multi line selections".to_string());
+ return;
+ }
+ // if the selection is not in the same line with last selection, we set the column to 0
+ column = if l1 != last_line { 0 } else { column + 1 };
+ last_line = l1;
+
+ if column < column_widths.len() {
+ if sel.to() - sel.from() > column_widths[column] {
+ column_widths[column] = sel.to() - sel.from();
+ }
+ } else {
+ // a new column, current selection width is the temp width of the column
+ column_widths.push(sel.to() - sel.from());
+ }
+ }
+ last_line = text.len_lines();
+ // once we get the with of each column, we transform each selection with to it's column width based on the align style
+ let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
+ let l = range.cursor_line(text);
+ column = if l != last_line { 0 } else { column + 1 };
+ last_line = l;
+
+ (
+ range.from(),
+ range.to(),
+ Some(
+ align_fragment_to_width(&range.fragment(text), column_widths[column], align_style)
+ .into(),
+ ),
+ )
+ });
+
+ doc.apply(&transaction, view.id);
+ doc.append_changes_to_history(view.id);
+}
+
+fn align_fragment_to_width(fragment: &str, width: usize, align_style: usize) -> String {
+ let trimed = fragment.trim_matches(|c| c == ' ');
+ let mut s = " ".repeat(width - trimed.chars().count());
+ match align_style {
+ 1 => s.insert_str(0, trimed), // left align
+ 2 => s.insert_str(s.len() / 2, trimed), // center align
+ 3 => s.push_str(trimed), // right align
+ n => unimplemented!("{}", n),
+ }
+ s
+}
+
fn goto_window(cx: &mut Context, align: Align) {
let (view, doc) = current!(cx.editor);
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index bf3b594e..7f6d0c6b 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -604,7 +604,7 @@ impl Default for Keymaps {
// "q" => record_macro,
// "Q" => replay_macro,
- // & align selections
+ "&" => align_selections,
"_" => trim_selections,
"(" => rotate_selections_backward,