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)
{
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();

View File

@ -44,12 +44,21 @@ constexpr bool with_bit_ops(Meta::Type<PromptFlags>) { return true; }
using KeyCallback = std::function<void (Key, Context&)>;
class InputMode;
enum class InsertMode : unsigned;
enum class KeymapMode : char;
enum class CursorMode;
using PromptCompleter = std::function<Completions (const Context&, CompletionFlags,
StringView, ByteCount)>;
enum class InsertMode : unsigned
{
Insert,
Append,
Replace,
InsertAtLineBegin,
AppendAtLineEnd,
OpenLineBelow,
OpenLineAbove
};
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)
{
const char reg = params.reg ? params.reg : '"';
@ -690,22 +651,28 @@ void change(Context& context, NormalParams params)
enter_insert_mode<InsertMode::Replace>(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<InsertMode mode>
template<PasteMode mode>
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<InsertMode mode>
template<PasteMode mode>
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<Selection> 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<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)
{
return (mode & RegexMode::Forward) ?
@ -2235,12 +2243,12 @@ static constexpr HashMap<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend>
{ {'V'}, {"move view (locked)", view_commands<true>} },
{ {'y'}, {"yank selected text", yank} },
{ {'p'}, {"paste after selected text", repeated<paste<InsertMode::Append>>} },
{ {'P'}, {"paste before selected text", repeated<paste<InsertMode::Insert>>} },
{ {alt('p')}, {"paste every yanked selection after selected text", paste_all<InsertMode::Append>} },
{ {alt('P')}, {"paste every yanked selection before selected text", paste_all<InsertMode::Insert>} },
{ {'R'}, {"replace selected text with yanked text", paste<InsertMode::Replace>} },
{ {alt('R')}, {"replace selected text with every yanked text", paste_all<InsertMode::Replace>} },
{ {'p'}, {"paste after selected text", repeated<paste<PasteMode::Append>>} },
{ {'P'}, {"paste before selected text", repeated<paste<PasteMode::Insert>>} },
{ {alt('p')}, {"paste every yanked selection after selected text", paste_all<PasteMode::Append>} },
{ {alt('P')}, {"paste every yanked selection before selected text", paste_all<PasteMode::Insert>} },
{ {'R'}, {"replace selected text with yanked text", paste<PasteMode::Replace>} },
{ {alt('R')}, {"replace selected text with every yanked text", paste_all<PasteMode::Replace>} },
{ {'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<Key, NormalCmd, MemoryDomain::Undefined, KeymapBackend>
{ {':'}, {"enter command prompt", command} },
{ {'|'}, {"pipe each selection through filter and replace with output", pipe<true>} },
{ {alt('|')}, {"pipe each selection through command and ignore output", pipe<false>} },
{ {'!'}, {"insert command output", insert_output<InsertMode::Insert>} },
{ {alt('!')}, {"append command output", insert_output<InsertMode::Append>} },
{ {'!'}, {"insert command output", insert_output<PasteMode::Insert>} },
{ {alt('!')}, {"append command output", insert_output<PasteMode::Append>} },
{ {' '}, {"remove all selections except main", keep_selection} },
{ {alt(' ')}, {"remove main selection", remove_selection} },

View File

@ -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<Selection>& selections,
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)
{
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));
}

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 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 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 (size_t index, Selection& sel)>;
void for_each(ApplyFunc apply);
void insert(ConstArrayView<String> strings, InsertMode mode);
void replace(ConstArrayView<String> strings);
void erase();