@ -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 ) ;