on master help sign in
commands: Implement join_selections.
commit2c9b02039bac81cb32309bd0d4e2b08191356b9c authorBlaž Hrastnik <blaz@mxxn.io> dateFri 26 Feb 2021, 17:21 (6 years ago) parent6336c1d
3 files changed+64−5
helix-core/src/state.rs+4−2 helix-term/src/commands.rs+45 helix-term/src/keymap.rs+15−3
helix-core/src/state.rs+4−2
@@ -292,7 +292,8 @@ fn categorize(ch: char) -> Category {
292292 }
293293}
294294
295−fn skip_over_next<F>(slice: RopeSlice, pos: &mut usize, fun: F)
295+#[inline]
296+pub fn skip_over_next<F>(slice: RopeSlice, pos: &mut usize, fun: F)
296297where
297298 F: Fn(char) -> bool,
298299{
@@ -306,7 +307,8 @@ where
306307 }
307308}
308309
309−fn skip_over_prev<F>(slice: RopeSlice, pos: &mut usize, fun: F)
310+#[inline]
311+pub fn skip_over_prev<F>(slice: RopeSlice, pos: &mut usize, fun: F)
310312where
311313 F: Fn(char) -> bool,
312314{
helix-term/src/commands.rs+45
@@ -308,6 +308,7 @@ pub fn select_regex(cx: &mut Context) {
308308 let prompt = ui::regex_prompt(cx, "select:".to_string(), |doc, regex| {
309309 let text = doc.text().slice(..);
310310 // TODO: if select on matches returns empty range, we need to abort
311+ // if regex empty or no matches, return
311312 let selection =
312313 selection::select_on_matches(text, doc.selection(), &regex).expect("no matches");
313314 doc.set_selection(selection);
@@ -929,6 +930,50 @@ pub fn format_selections(cx: &mut Context) {
929930 doc.append_changes_to_history();
930931}
931932
933+pub fn join_selections(cx: &mut Context) {
934+ use helix_core::state::skip_over_next;
935+ let doc = cx.doc();
936+ let text = doc.text();
937+ let slice = doc.text().slice(..);
938+
939+ let mut changes = Vec::new();
940+ let fragment = Tendril::from(" ");
941+
942+ for selection in doc.selection().ranges() {
943+ let start = text.char_to_line(selection.from());
944+ let mut end = text.char_to_line(selection.to());
945+ if start == end {
946+ end += 1
947+ }
948+ let lines = start..end;
949+
950+ changes.reserve(lines.len());
951+
952+ for line in lines {
953+ let mut start = text.line_to_char(line + 1).saturating_sub(1);
954+ let mut end = start + 1;
955+ skip_over_next(slice, &mut end, |ch| matches!(ch, ' ' | '\t'));
956+
957+ // need to skip from start, not end
958+ let change = (start, end, Some(fragment.clone()));
959+ changes.push(change);
960+ }
961+ }
962+
963+ changes.sort_unstable_by_key(|(from, _to, _text)| *from);
964+ changes.dedup();
965+
966+ // TODO: joining multiple empty lines should be replaced by a single space.
967+ // need to merge change ranges that touch
968+
969+ let transaction = Transaction::change(&doc.state, changes.into_iter());
970+ // TODO: select inserted spaces
971+ // .with_selection(selection);
972+
973+ doc.apply(&transaction);
974+ doc.append_changes_to_history();
975+}
976+
932977//
933978
934979pub fn save(cx: &mut Context) {
helix-term/src/keymap.rs+15−3
@@ -138,28 +138,35 @@ pub fn default() -> Keymaps {
138138 vec![key!('h')] => commands::move_char_left as Command,
139139 vec![key!('j')] => commands::move_line_down,
140140 vec![key!('k')] => commands::move_line_up,
141+ vec![key!('l')] => commands::move_char_right,
142+
141143 vec![key!('0')] => commands::move_line_start,
142144 vec![key!('$')] => commands::move_line_end,
143− vec![key!('l')] => commands::move_char_right,
145+
144146 vec![shift!('H')] => commands::extend_char_left,
145147 vec![shift!('J')] => commands::extend_line_down,
146148 vec![shift!('K')] => commands::extend_line_up,
147149 vec![shift!('L')] => commands::extend_char_right,
150+
148151 vec![key!('w')] => commands::move_next_word_start,
149152 vec![shift!('W')] => commands::extend_next_word_start,
150153 vec![key!('b')] => commands::move_prev_word_start,
151154 vec![shift!('B')] => commands::extend_prev_word_start,
152155 vec![key!('e')] => commands::move_next_word_end,
153156 vec![key!('E')] => commands::extend_next_word_end,
154− // TODO: E
157+
155158 vec![key!('g')] => commands::goto_mode,
159+ vec![key!(':')] => commands::command_mode,
160+
156161 vec![key!('i')] => commands::insert_mode,
157162 vec![shift!('I')] => commands::prepend_to_line,
158163 vec![key!('a')] => commands::append_mode,
159164 vec![shift!('A')] => commands::append_to_line,
160165 vec![key!('o')] => commands::open_below,
166+
161167 vec![key!('d')] => commands::delete_selection,
162168 vec![key!('c')] => commands::change_selection,
169+
163170 vec![key!('s')] => commands::select_regex,
164171 vec![alt!('s')] => commands::split_selection_on_newline,
165172 vec![shift!('S')] => commands::split_selection,
@@ -167,19 +174,24 @@ pub fn default() -> Keymaps {
167174 vec![alt!(';')] => commands::flip_selections,
168175 vec![key!('%')] => commands::select_all,
169176 vec![key!('x')] => commands::select_line,
177+
170178 // TODO: figure out what key to use
171179 vec![key!('[')] => commands::expand_selection,
180+
172181 vec![key!('/')] => commands::search,
173182 vec![key!('n')] => commands::search_next,
174183 vec![key!('*')] => commands::search_selection,
184+
175185 vec![key!('u')] => commands::undo,
176186 vec![shift!('U')] => commands::redo,
187+
177188 vec![key!('y')] => commands::yank,
178189 vec![key!('p')] => commands::paste,
190+
179191 vec![key!('>')] => commands::indent,
180192 vec![key!('<')] => commands::unindent,
181193 vec![key!('=')] => commands::format_selections,
182− vec![key!(':')] => commands::command_mode,
194+ vec![ctrl!('j')] => commands::join_selections,
183195 vec![Key {
184196 code: KeyCode::Esc,
185197 modifiers: Modifiers::NONE