on master help sign in
refactor align_selection using kakoune logic
1c1aee7Bob Qi
helix-term/src/commands.rs+36−38
@@ -772,18 +772,11 @@ fn trim_selections(cx: &mut Context) {
772
772
773// align text in selection
773// align text in selection
774fn align_selections(cx: &mut Context) {
774fn align_selections(cx: &mut Context) {
775− let align_style = cx.count();
776− if align_style > 3 {
777− cx.editor
778− .set_error("align only accept 1,2,3 as count to set left/center/right align");
779− return;
780− }
781−
782 let (view, doc) = current!(cx.editor);
775 let (view, doc) = current!(cx.editor);
783 let text = doc.text().slice(..);
776 let text = doc.text().slice(..);
784 let selection = doc.selection(view.id);
777 let selection = doc.selection(view.id);
785− let mut column_widths = vec![];
778+ let mut column_widths: Vec<Vec<_>> = vec![];
786− let mut last_line = text.len_lines();
779+ let mut last_line = text.len_lines() + 1;
787 let mut column = 0;
780 let mut column = 0;
788 // first of all, we need compute all column's width, let use max width of the selections in a column
781 // first of all, we need compute all column's width, let use max width of the selections in a column
789 for sel in selection {
782 for sel in selection {
@@ -797,47 +790,52 @@ fn align_selections(cx: &mut Context) {
797 column = if l1 != last_line { 0 } else { column + 1 };
790 column = if l1 != last_line { 0 } else { column + 1 };
798 last_line = l1;
791 last_line = l1;
799
792
793+ // save all max line cursor of selections
794+ let cursor = sel.to() - text.line_to_char(l1);
800 if column < column_widths.len() {
795 if column < column_widths.len() {
801− if sel.to() - sel.from() > column_widths[column] {
802− column_widths[column] = sel.to() - sel.from();
796+ column_widths[column].push((l1, cursor));
803− }
804 } else {
797 } else {
805− // a new column, current selection width is the temp width of the column
798+ column_widths.push(vec![(l1, cursor)]);
806− column_widths.push(sel.to() - sel.from());
799+ }
800+ }
801+ let mut max_curs = vec![];
802+ for i in 0..column_widths.len() {
803+ // compute max cursor of current column
804+ let max_cur = column_widths[i]
805+ .iter()
806+ .fold(0, |max, &(_, cur)| max.max(cur));
807+ max_curs.push(max_cur);
808+
809+ let mut pads = std::collections::HashMap::new();
810+ for item in column_widths[i].iter_mut() {
811+ pads.insert(item.0, max_cur - item.1);
812+ }
813+ // add pad to right columns
814+ for col in column_widths[i + 1..].iter_mut() {
815+ for item in col.iter_mut() {
816+ item.1 += pads.get(&item.0).unwrap_or(&0);
817+ }
807 }
818 }
808 }
819 }
820+
809 last_line = text.len_lines();
821 last_line = text.len_lines();
810− // once we get the with of each column, we transform each selection with to it's column width based on the align style
811 let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
822 let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
812− let l = range.cursor_line(text);
823+ let line = range.cursor_line(text);
813− column = if l != last_line { 0 } else { column + 1 };
824+ column = if line != last_line { 0 } else { column + 1 };
814− last_line = l;
825+ last_line = line;
826+ let max_cur = max_curs[column];
827+ let new_cur = column_widths[column]
828+ .iter()
829+ .fold(max_cur, |c, (l, cur)| if *l == line { *cur } else { c });
830+ let mut new_str = " ".repeat(max_cur - new_cur);
831+ new_str.push_str(&range.fragment(text));
815
832
816− (
817− range.from(),
833+ (range.from(), range.to(), Some(new_str.into()))
818− range.to(),
819− Some(
820− align_fragment_to_width(&range.fragment(text), column_widths[column], align_style)
821− .into(),
822− ),
823− )
824 });
834 });
825
835
826 doc.apply(&transaction, view.id);
836 doc.apply(&transaction, view.id);
827}
837}
828
838
829−fn align_fragment_to_width(fragment: &str, width: usize, align_style: usize) -> String {
830− let trimed = fragment.trim_matches(|c| c == ' ');
831− let mut s = " ".repeat(width - trimed.chars().count());
832− match align_style {
833− 1 => s.insert_str(0, trimed), // left align
834− 2 => s.insert_str(s.len() / 2, trimed), // center align
835− 3 => s.push_str(trimed), // right align
836− n => unimplemented!("{}", n),
837− }
838− s
839−}
840−
841fn goto_window(cx: &mut Context, align: Align) {
839fn goto_window(cx: &mut Context, align: Align) {
842 let count = cx.count() - 1;
840 let count = cx.count() - 1;
843 let (view, doc) = current!(cx.editor);
841 let (view, doc) = current!(cx.editor);