diff --git a/src/input_handler.cc b/src/input_handler.cc index 85904e7f..fbfadf81 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -1467,13 +1467,19 @@ private: void insert(ConstArrayView strings) { - context().selections().insert(strings, InsertMode::InsertCursor); + context().selections().for_each([strings, &buffer=context().buffer()] + (size_t index, Selection& sel) { + Kakoune::insert(buffer, sel, sel.cursor(), strings[std::min(strings.size()-1, index)]); + }); } void insert(Codepoint key) { String str{key}; - context().selections().insert(str, InsertMode::InsertCursor); + context().selections().for_each([&buffer=context().buffer(), &str] + (size_t index, Selection& sel) { + Kakoune::insert(buffer, sel, sel.cursor(), str); + }); context().hooks().run_hook(Hook::InsertChar, str, context()); } @@ -1551,10 +1557,6 @@ private: sel.set(pos); } break; - case InsertMode::InsertAtNextLineBegin: - case InsertMode::InsertCursor: - kak_assert(false); // invalid for interactive insert - break; } selections.check_invariant(); buffer.check_invariant(); diff --git a/src/input_handler.hh b/src/input_handler.hh index bd9c3dcb..5cb150b5 100644 --- a/src/input_handler.hh +++ b/src/input_handler.hh @@ -44,12 +44,21 @@ constexpr bool with_bit_ops(Meta::Type) { return true; } using KeyCallback = std::function; class InputMode; -enum class InsertMode : unsigned; enum class KeymapMode : char; enum class CursorMode; using PromptCompleter = std::function; +enum class InsertMode : unsigned +{ + Insert, + Append, + Replace, + InsertAtLineBegin, + AppendAtLineEnd, + OpenLineBelow, + OpenLineAbove +}; class InputHandler : public SafeCountable { diff --git a/src/normal.cc b/src/normal.cc index 396a2eef..af6a4459 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -619,45 +619,6 @@ void pipe(Context& context, NormalParams params) }); } -template -void insert_output(Context& context, NormalParams params) -{ - const char* prompt = mode == InsertMode::Insert ? "insert-output:" : "append-output:"; - String default_command = context.main_sel_register_value(params.reg ? params.reg : '|').str(); - - context.input_handler().prompt( - prompt, {}, default_command, context.faces()["Prompt"], - PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', - shell_complete, - [default_command](StringView cmdline, PromptEvent event, Context& context) - { - if (event != PromptEvent::Validate) - return; - - if (cmdline.empty()) - cmdline = default_command; - - if (cmdline.empty()) - return; - - ScopedEdition edition(context); - auto& selections = context.selections(); - auto& buffer = context.buffer(); - const size_t old_main = selections.main_index(); - - selections.for_each([&](size_t index, Selection& sel) { - selections.set_main_index(index); - auto [out, status] = ShellManager::instance().eval( - cmdline, context, content(context.buffer(), sel), - ShellManager::Flags::WaitForStdout); - - insert(buffer, sel, out, mode); - }); - - selections.set_main_index(old_main); - }); -} - void yank(Context& context, NormalParams params) { const char reg = params.reg ? params.reg : '"'; @@ -690,22 +651,28 @@ void change(Context& context, NormalParams params) enter_insert_mode(context, params); } -InsertMode adapt_for_linewise(InsertMode mode, bool linewise) +enum class PasteMode { - if (not linewise) - return mode; + Append, + Insert, + Replace +}; +BufferCoord paste_pos(Buffer& buffer, const Selection& sel, PasteMode mode, bool linewise) +{ switch (mode) { - case InsertMode::Append: return InsertMode::InsertAtNextLineBegin; - case InsertMode::Insert: return InsertMode::InsertAtLineBegin; - default: break; + case PasteMode::Append: + return linewise ? std::min(buffer.line_count(), sel.max().line+1) : buffer.char_next(sel.max()); + case PasteMode::Insert: + return linewise ? sel.min().line : sel.min(); + default: + kak_assert(false); + return {}; } - kak_assert(false); - return InsertMode::Insert; } -template +template void paste(Context& context, NormalParams params) { const char reg = params.reg ? params.reg : '"'; @@ -714,14 +681,18 @@ void paste(Context& context, NormalParams params) return not str.empty() and str.back() == '\n'; }); + auto& buffer = context.buffer(); ScopedEdition edition(context); - if (mode == InsertMode::Replace) - context.selections().replace(strings); - else - context.selections().insert(strings, adapt_for_linewise(mode, linewise)); + context.selections().for_each([&](size_t index, Selection& sel) { + auto& str = strings[std::min(strings.size()-1, index)]; + if (mode == PasteMode::Replace) + replace(buffer, sel, str); + else + insert(buffer, sel, paste_pos(buffer, sel, mode, linewise), str); + }); } -template +template void paste_all(Context& context, NormalParams params) { const char reg = params.reg ? params.reg : '"'; @@ -740,17 +711,15 @@ void paste_all(Context& context, NormalParams params) offsets.push_back(all.length()); } - InsertMode effective_mode = adapt_for_linewise(mode, linewise); - Buffer& buffer = context.buffer(); Vector result; auto& selections = context.selections(); { ScopedEdition edition(context); selections.for_each([&](size_t, const Selection& sel) { - auto range = (mode == InsertMode::Replace) ? + auto range = (mode == PasteMode::Replace) ? buffer.replace(sel.min(), buffer.char_next(sel.max()), all) - : buffer.insert(get_insert_pos(buffer, sel, effective_mode), all); + : buffer.insert(paste_pos(buffer, sel, mode, linewise), all); ByteCount pos_offset = 0; BufferCoord pos = range.begin; @@ -766,6 +735,45 @@ void paste_all(Context& context, NormalParams params) selections = std::move(result); } +template +void insert_output(Context& context, NormalParams params) +{ + const char* prompt = mode == PasteMode::Insert ? "insert-output:" : "append-output:"; + String default_command = context.main_sel_register_value(params.reg ? params.reg : '|').str(); + + context.input_handler().prompt( + prompt, {}, default_command, context.faces()["Prompt"], + PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', + shell_complete, + [default_command](StringView cmdline, PromptEvent event, Context& context) + { + if (event != PromptEvent::Validate) + return; + + if (cmdline.empty()) + cmdline = default_command; + + if (cmdline.empty()) + return; + + ScopedEdition edition(context); + auto& selections = context.selections(); + auto& buffer = context.buffer(); + const size_t old_main = selections.main_index(); + + selections.for_each([&](size_t index, Selection& sel) { + selections.set_main_index(index); + auto [out, status] = ShellManager::instance().eval( + cmdline, context, content(context.buffer(), sel), + ShellManager::Flags::WaitForStdout); + + insert(buffer, sel, paste_pos(buffer, sel, mode, false), out); + }); + + selections.set_main_index(old_main); + }); +} + constexpr RegexCompileFlags direction_flags(RegexMode mode) { return (mode & RegexMode::Forward) ? @@ -2235,12 +2243,12 @@ static constexpr HashMap { {'V'}, {"move view (locked)", view_commands} }, { {'y'}, {"yank selected text", yank} }, - { {'p'}, {"paste after selected text", repeated>} }, - { {'P'}, {"paste before selected text", repeated>} }, - { {alt('p')}, {"paste every yanked selection after selected text", paste_all} }, - { {alt('P')}, {"paste every yanked selection before selected text", paste_all} }, - { {'R'}, {"replace selected text with yanked text", paste} }, - { {alt('R')}, {"replace selected text with every yanked text", paste_all} }, + { {'p'}, {"paste after selected text", repeated>} }, + { {'P'}, {"paste before selected text", repeated>} }, + { {alt('p')}, {"paste every yanked selection after selected text", paste_all} }, + { {alt('P')}, {"paste every yanked selection before selected text", paste_all} }, + { {'R'}, {"replace selected text with yanked text", paste} }, + { {alt('R')}, {"replace selected text with every yanked text", paste_all} }, { {'s'}, {"select regex matches in selected text", select_regex} }, { {'S'}, {"split selected text on regex matches", split_regex} }, @@ -2255,8 +2263,8 @@ static constexpr HashMap { {':'}, {"enter command prompt", command} }, { {'|'}, {"pipe each selection through filter and replace with output", pipe} }, { {alt('|')}, {"pipe each selection through command and ignore output", pipe} }, - { {'!'}, {"insert command output", insert_output} }, - { {alt('!')}, {"append command output", insert_output} }, + { {'!'}, {"insert command output", insert_output} }, + { {alt('!')}, {"append command output", insert_output} }, { {' '}, {"remove all selections except main", keep_selection} }, { {alt(' ')}, {"remove main selection", remove_selection} }, diff --git a/src/selection.cc b/src/selection.cc index 27a40156..d408ddfd 100644 --- a/src/selection.cc +++ b/src/selection.cc @@ -351,29 +351,6 @@ void SelectionList::sort_and_merge_overlapping() merge_overlapping(); } -BufferCoord get_insert_pos(const Buffer& buffer, const Selection& sel, - InsertMode mode) -{ - switch (mode) - { - case InsertMode::Insert: - return sel.min(); - case InsertMode::InsertCursor: - return sel.cursor(); - case InsertMode::Append: - return buffer.char_next(sel.max()); - case InsertMode::InsertAtLineBegin: - return sel.min().line; - case InsertMode::AppendAtLineEnd: - return {sel.max().line, buffer[sel.max().line].length() - 1}; - case InsertMode::InsertAtNextLineBegin: - return std::min(buffer.line_count(), sel.max().line+1); - default: - kak_assert(false); - return {}; - } -} - static void fix_overflowing_selections(Vector& selections, const Buffer& buffer) { @@ -385,16 +362,6 @@ static void fix_overflowing_selections(Vector& selections, } } -void SelectionList::insert(ConstArrayView strings, InsertMode mode) -{ - if (strings.empty()) - return; - - for_each([&](size_t index, Selection& sel) { - Kakoune::insert(*m_buffer, sel, strings[std::min(strings.size()-1, index)], mode); - }); -} - void SelectionList::for_each(ApplyFunc func) { update(); @@ -432,9 +399,9 @@ void replace(Buffer& buffer, Selection& sel, StringView content) max = range.end > range.begin ? buffer.char_prev(range.end) : range.begin; } -void insert(Buffer& buffer, Selection& sel, StringView content, InsertMode mode) +void insert(Buffer& buffer, Selection& sel, BufferCoord pos, StringView content) { - auto range = buffer.insert(get_insert_pos(buffer, sel, mode), content); + auto range = buffer.insert(pos, content); sel.anchor() = buffer.clamp(update_insert(sel.anchor(), range.begin, range.end)); sel.cursor() = buffer.clamp(update_insert(sel.cursor(), range.begin, range.end)); } diff --git a/src/selection.hh b/src/selection.hh index 5d6e7427..1aebe41f 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -74,24 +74,8 @@ void sort_selections(Vector& selections, size_t& main); void merge_overlapping_selections(Vector& selections, size_t& main); void clamp_selections(Vector& sel, const Buffer& buffer); -enum class InsertMode : unsigned -{ - Insert, - InsertCursor, - Append, - Replace, - InsertAtLineBegin, - InsertAtNextLineBegin, - AppendAtLineEnd, - OpenLineBelow, - OpenLineAbove -}; - -BufferCoord get_insert_pos(const Buffer& buffer, const Selection& sel, InsertMode mode); - void replace(Buffer& buffer, Selection& sel, StringView content); -void insert(Buffer& buffer, Selection& sel, StringView content, InsertMode mode); - +void insert(Buffer& buffer, Selection& sel, BufferCoord pos, StringView content); struct SelectionList { @@ -154,7 +138,6 @@ struct SelectionList using ApplyFunc = FunctionRef; void for_each(ApplyFunc apply); - void insert(ConstArrayView strings, InsertMode mode); void replace(ConstArrayView strings); void erase();