|
|
|
@ -2775,24 +2775,87 @@ fn last_picker(cx: &mut Context) {
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// I inserts at the first nonwhitespace character of each line with a selection
|
|
|
|
|
/// Fallback position to use for [`insert_with_indent`].
|
|
|
|
|
enum IndentFallbackPos {
|
|
|
|
|
LineStart,
|
|
|
|
|
LineEnd,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `I` inserts at the first nonwhitespace character of each line with a selection.
|
|
|
|
|
// If the line is empty, automatically indent.
|
|
|
|
|
fn insert_at_line_start(cx: &mut Context) {
|
|
|
|
|
goto_first_nonwhitespace(cx);
|
|
|
|
|
enter_insert_mode(cx);
|
|
|
|
|
insert_with_indent(cx, IndentFallbackPos::LineStart);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A inserts at the end of each line with a selection
|
|
|
|
|
// `A` inserts at the end of each line with a selection.
|
|
|
|
|
// If the line is empty, automatically indent.
|
|
|
|
|
fn insert_at_line_end(cx: &mut Context) {
|
|
|
|
|
insert_with_indent(cx, IndentFallbackPos::LineEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Enter insert mode and auto-indent the current line if it is empty.
|
|
|
|
|
// If the line is not empty, move the cursor to the specified fallback position.
|
|
|
|
|
fn insert_with_indent(cx: &mut Context, cursor_fallback: IndentFallbackPos) {
|
|
|
|
|
enter_insert_mode(cx);
|
|
|
|
|
|
|
|
|
|
let (view, doc) = current!(cx.editor);
|
|
|
|
|
|
|
|
|
|
let selection = doc.selection(view.id).clone().transform(|range| {
|
|
|
|
|
let text = doc.text().slice(..);
|
|
|
|
|
let line = range.cursor_line(text);
|
|
|
|
|
let pos = line_end_char_index(&text, line);
|
|
|
|
|
Range::new(pos, pos)
|
|
|
|
|
let text = doc.text().slice(..);
|
|
|
|
|
let contents = doc.text();
|
|
|
|
|
let selection = doc.selection(view.id);
|
|
|
|
|
|
|
|
|
|
let language_config = doc.language_config();
|
|
|
|
|
let syntax = doc.syntax();
|
|
|
|
|
let tab_width = doc.tab_width();
|
|
|
|
|
|
|
|
|
|
let mut ranges = SmallVec::with_capacity(selection.len());
|
|
|
|
|
let mut offs = 0;
|
|
|
|
|
|
|
|
|
|
let mut transaction = Transaction::change_by_selection(contents, selection, |range| {
|
|
|
|
|
let cursor_line = range.cursor_line(text);
|
|
|
|
|
let cursor_line_start = text.line_to_char(cursor_line);
|
|
|
|
|
|
|
|
|
|
if line_end_char_index(&text, cursor_line) == cursor_line_start {
|
|
|
|
|
// line is empty => auto indent
|
|
|
|
|
let line_end_index = cursor_line_start;
|
|
|
|
|
|
|
|
|
|
let indent = indent::indent_for_newline(
|
|
|
|
|
language_config,
|
|
|
|
|
syntax,
|
|
|
|
|
&doc.indent_style,
|
|
|
|
|
tab_width,
|
|
|
|
|
text,
|
|
|
|
|
cursor_line,
|
|
|
|
|
line_end_index,
|
|
|
|
|
cursor_line,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// calculate new selection ranges
|
|
|
|
|
let pos = offs + cursor_line_start;
|
|
|
|
|
let indent_width = indent.chars().count();
|
|
|
|
|
ranges.push(Range::point(pos + indent_width));
|
|
|
|
|
offs += indent_width;
|
|
|
|
|
|
|
|
|
|
(line_end_index, line_end_index, Some(indent.into()))
|
|
|
|
|
} else {
|
|
|
|
|
// move cursor to the fallback position
|
|
|
|
|
let pos = match cursor_fallback {
|
|
|
|
|
IndentFallbackPos::LineStart => {
|
|
|
|
|
find_first_non_whitespace_char(text.line(cursor_line))
|
|
|
|
|
.map(|ws_offset| ws_offset + cursor_line_start)
|
|
|
|
|
.unwrap_or(cursor_line_start)
|
|
|
|
|
}
|
|
|
|
|
IndentFallbackPos::LineEnd => line_end_char_index(&text, cursor_line),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ranges.push(range.put_cursor(text, pos + offs, cx.editor.mode == Mode::Select));
|
|
|
|
|
|
|
|
|
|
(cursor_line_start, cursor_line_start, None)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
doc.set_selection(view.id, selection);
|
|
|
|
|
|
|
|
|
|
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
|
|
|
|
|
doc.apply(&transaction, view.id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Creates an LspCallback that waits for formatting changes to be computed. When they're done,
|
|
|
|
|