Split InsertMode into InsertMode and PasteMode

They are quite different use cases, and this allow moving InsertMode
to input_handler.hh which is what uses it.

This also cleans up the code as we can get rid of get_insert_pos and
rely more on SelectionList::for_each.
This commit is contained in:
Maxime Coste 2021-10-10 10:28:34 +11:00
parent b609adc84c
commit 689553c2e9
5 changed files with 94 additions and 125 deletions

View File

@ -1467,13 +1467,19 @@ private:
void insert(ConstArrayView<String> strings) void insert(ConstArrayView<String> 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) void insert(Codepoint key)
{ {
String str{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()); context().hooks().run_hook(Hook::InsertChar, str, context());
} }
@ -1551,10 +1557,6 @@ private:
sel.set(pos); sel.set(pos);
} }
break; break;
case InsertMode::InsertAtNextLineBegin:
case InsertMode::InsertCursor:
kak_assert(false); // invalid for interactive insert
break;
} }
selections.check_invariant(); selections.check_invariant();
buffer.check_invariant(); buffer.check_invariant();

View File

@ -44,12 +44,21 @@ constexpr bool with_bit_ops(Meta::Type<PromptFlags>) { return true; }
using KeyCallback = std::function<void (Key, Context&)>; using KeyCallback = std::function<void (Key, Context&)>;
class InputMode; class InputMode;
enum class InsertMode : unsigned;
enum class KeymapMode : char; enum class KeymapMode : char;
enum class CursorMode; enum class CursorMode;
using PromptCompleter = std::function<Completions (const Context&, CompletionFlags, using PromptCompleter = std::function<Completions (const Context&, CompletionFlags,
StringView, ByteCount)>; StringView, ByteCount)>;
enum class InsertMode : unsigned
{
Insert,
Append,
Replace,
InsertAtLineBegin,
AppendAtLineEnd,
OpenLineBelow,
OpenLineAbove
};
class InputHandler : public SafeCountable class InputHandler : public SafeCountable
{ {

View File

@ -619,45 +619,6 @@ void pipe(Context& context, NormalParams params)
}); });
} }
template<InsertMode mode>
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) void yank(Context& context, NormalParams params)
{ {
const char reg = params.reg ? params.reg : '"'; const char reg = params.reg ? params.reg : '"';
@ -690,22 +651,28 @@ void change(Context& context, NormalParams params)
enter_insert_mode<InsertMode::Replace>(context, params); enter_insert_mode<InsertMode::Replace>(context, params);
} }
InsertMode adapt_for_linewise(InsertMode mode, bool linewise) enum class PasteMode
{ {
if (not linewise) Append,
return mode; Insert,
Replace
};
BufferCoord paste_pos(Buffer& buffer, const Selection& sel, PasteMode mode, bool linewise)
{
switch (mode) switch (mode)
{ {
case InsertMode::Append: return InsertMode::InsertAtNextLineBegin; case PasteMode::Append:
case InsertMode::Insert: return InsertMode::InsertAtLineBegin; return linewise ? std::min(buffer.line_count(), sel.max().line+1) : buffer.char_next(sel.max());
default: break; case PasteMode::Insert:
return linewise ? sel.min().line : sel.min();
default:
kak_assert(false);
return {};
} }
kak_assert(false);
return InsertMode::Insert;
} }
template<InsertMode mode> template<PasteMode mode>
void paste(Context& context, NormalParams params) void paste(Context& context, NormalParams params)
{ {
const char reg = params.reg ? params.reg : '"'; 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'; return not str.empty() and str.back() == '\n';
}); });
auto& buffer = context.buffer();
ScopedEdition edition(context); ScopedEdition edition(context);
if (mode == InsertMode::Replace) context.selections().for_each([&](size_t index, Selection& sel) {
context.selections().replace(strings); auto& str = strings[std::min(strings.size()-1, index)];
else if (mode == PasteMode::Replace)
context.selections().insert(strings, adapt_for_linewise(mode, linewise)); replace(buffer, sel, str);
else
insert(buffer, sel, paste_pos(buffer, sel, mode, linewise), str);
});
} }
template<InsertMode mode> template<PasteMode mode>
void paste_all(Context& context, NormalParams params) void paste_all(Context& context, NormalParams params)
{ {
const char reg = params.reg ? params.reg : '"'; const char reg = params.reg ? params.reg : '"';
@ -740,17 +711,15 @@ void paste_all(Context& context, NormalParams params)
offsets.push_back(all.length()); offsets.push_back(all.length());
} }
InsertMode effective_mode = adapt_for_linewise(mode, linewise);
Buffer& buffer = context.buffer(); Buffer& buffer = context.buffer();
Vector<Selection> result; Vector<Selection> result;
auto& selections = context.selections(); auto& selections = context.selections();
{ {
ScopedEdition edition(context); ScopedEdition edition(context);
selections.for_each([&](size_t, const Selection& sel) { 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.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; ByteCount pos_offset = 0;
BufferCoord pos = range.begin; BufferCoord pos = range.begin;
@ -766,6 +735,45 @@ void paste_all(Context& context, NormalParams params)
selections = std::move(result); selections = std::move(result);
} }
template<PasteMode mode>
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) constexpr RegexCompileFlags direction_flags(RegexMode mode)
{ {
return (mode & RegexMode::Forward) ? return (mode & RegexMode::Forward) ?
@ -2235,12 +2243,12 @@ static constexpr HashMap<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend>
{ {'V'}, {"move view (locked)", view_commands<true>} }, { {'V'}, {"move view (locked)", view_commands<true>} },
{ {'y'}, {"yank selected text", yank} }, { {'y'}, {"yank selected text", yank} },
{ {'p'}, {"paste after selected text", repeated<paste<InsertMode::Append>>} }, { {'p'}, {"paste after selected text", repeated<paste<PasteMode::Append>>} },
{ {'P'}, {"paste before selected text", repeated<paste<InsertMode::Insert>>} }, { {'P'}, {"paste before selected text", repeated<paste<PasteMode::Insert>>} },
{ {alt('p')}, {"paste every yanked selection after selected text", paste_all<InsertMode::Append>} }, { {alt('p')}, {"paste every yanked selection after selected text", paste_all<PasteMode::Append>} },
{ {alt('P')}, {"paste every yanked selection before selected text", paste_all<InsertMode::Insert>} }, { {alt('P')}, {"paste every yanked selection before selected text", paste_all<PasteMode::Insert>} },
{ {'R'}, {"replace selected text with yanked text", paste<InsertMode::Replace>} }, { {'R'}, {"replace selected text with yanked text", paste<PasteMode::Replace>} },
{ {alt('R')}, {"replace selected text with every yanked text", paste_all<InsertMode::Replace>} }, { {alt('R')}, {"replace selected text with every yanked text", paste_all<PasteMode::Replace>} },
{ {'s'}, {"select regex matches in selected text", select_regex} }, { {'s'}, {"select regex matches in selected text", select_regex} },
{ {'S'}, {"split selected text on regex matches", split_regex} }, { {'S'}, {"split selected text on regex matches", split_regex} },
@ -2255,8 +2263,8 @@ static constexpr HashMap<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend>
{ {':'}, {"enter command prompt", command} }, { {':'}, {"enter command prompt", command} },
{ {'|'}, {"pipe each selection through filter and replace with output", pipe<true>} }, { {'|'}, {"pipe each selection through filter and replace with output", pipe<true>} },
{ {alt('|')}, {"pipe each selection through command and ignore output", pipe<false>} }, { {alt('|')}, {"pipe each selection through command and ignore output", pipe<false>} },
{ {'!'}, {"insert command output", insert_output<InsertMode::Insert>} }, { {'!'}, {"insert command output", insert_output<PasteMode::Insert>} },
{ {alt('!')}, {"append command output", insert_output<InsertMode::Append>} }, { {alt('!')}, {"append command output", insert_output<PasteMode::Append>} },
{ {' '}, {"remove all selections except main", keep_selection} }, { {' '}, {"remove all selections except main", keep_selection} },
{ {alt(' ')}, {"remove main selection", remove_selection} }, { {alt(' ')}, {"remove main selection", remove_selection} },

View File

@ -351,29 +351,6 @@ void SelectionList::sort_and_merge_overlapping()
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<Selection>& selections, static void fix_overflowing_selections(Vector<Selection>& selections,
const Buffer& buffer) const Buffer& buffer)
{ {
@ -385,16 +362,6 @@ static void fix_overflowing_selections(Vector<Selection>& selections,
} }
} }
void SelectionList::insert(ConstArrayView<String> 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) void SelectionList::for_each(ApplyFunc func)
{ {
update(); 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; 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.anchor() = buffer.clamp(update_insert(sel.anchor(), range.begin, range.end));
sel.cursor() = buffer.clamp(update_insert(sel.cursor(), range.begin, range.end)); sel.cursor() = buffer.clamp(update_insert(sel.cursor(), range.begin, range.end));
} }

View File

@ -74,24 +74,8 @@ void sort_selections(Vector<Selection>& selections, size_t& main);
void merge_overlapping_selections(Vector<Selection>& selections, size_t& main); void merge_overlapping_selections(Vector<Selection>& selections, size_t& main);
void clamp_selections(Vector<Selection>& sel, const Buffer& buffer); void clamp_selections(Vector<Selection>& 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 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 struct SelectionList
{ {
@ -154,7 +138,6 @@ struct SelectionList
using ApplyFunc = FunctionRef<void (size_t index, Selection& sel)>; using ApplyFunc = FunctionRef<void (size_t index, Selection& sel)>;
void for_each(ApplyFunc apply); void for_each(ApplyFunc apply);
void insert(ConstArrayView<String> strings, InsertMode mode);
void replace(ConstArrayView<String> strings); void replace(ConstArrayView<String> strings);
void erase(); void erase();