Move insert and erase to normal.cc, and move edition management to context
This commit is contained in:
parent
e369b60258
commit
7267b8281f
|
@ -109,7 +109,7 @@ void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
|
||||||
if (&client->context().buffer() != &buffer)
|
if (&client->context().buffer() != &buffer)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (client->context().editor().is_editing())
|
if (client->context().is_editing())
|
||||||
throw runtime_error("client '" + client->context().name() + "' is inserting in '" +
|
throw runtime_error("client '" + client->context().name() + "' is inserting in '" +
|
||||||
buffer.display_name() + '\'');
|
buffer.display_name() + '\'');
|
||||||
|
|
||||||
|
|
|
@ -573,6 +573,11 @@ void context_wrap(CommandParameters params, Context& context, Func func)
|
||||||
DynamicSelectionList sels{editor.buffer(), editor.selections()};
|
DynamicSelectionList sels{editor.buffer(), editor.selections()};
|
||||||
auto restore_sels = on_scope_end([&]{ editor.selections() = std::move(sels); });
|
auto restore_sels = on_scope_end([&]{ editor.selections() = std::move(sels); });
|
||||||
|
|
||||||
|
// We do not want this draft context to commit undo groups if the real one is
|
||||||
|
// going to commit the whole thing later
|
||||||
|
if (real_context->is_editing())
|
||||||
|
input_handler.context().disable_undo_handling();
|
||||||
|
|
||||||
if (parser.has_option("itersel"))
|
if (parser.has_option("itersel"))
|
||||||
{
|
{
|
||||||
for (auto& sel : sels)
|
for (auto& sel : sels)
|
||||||
|
@ -804,7 +809,7 @@ void exec_keys(const KeyList& keys, Context& context)
|
||||||
RegisterRestorer quote('"', context);
|
RegisterRestorer quote('"', context);
|
||||||
RegisterRestorer slash('/', context);
|
RegisterRestorer slash('/', context);
|
||||||
|
|
||||||
scoped_edition edition(context.editor());
|
ScopedEdition edition(context);
|
||||||
|
|
||||||
for (auto& key : keys)
|
for (auto& key : keys)
|
||||||
context.input_handler().handle_key(key);
|
context.input_handler().handle_key(key);
|
||||||
|
|
|
@ -187,4 +187,18 @@ const SelectionList& Context::selections() const
|
||||||
return editor().selections();
|
return editor().selections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Context::begin_edition()
|
||||||
|
{
|
||||||
|
++m_edition_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::end_edition()
|
||||||
|
{
|
||||||
|
kak_assert(m_edition_level > 0);
|
||||||
|
if (m_edition_level == 1)
|
||||||
|
buffer().commit_undo_group();
|
||||||
|
|
||||||
|
--m_edition_level;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,15 @@ public:
|
||||||
const String& name() const { return m_name; }
|
const String& name() const { return m_name; }
|
||||||
void set_name(String name) { m_name = std::move(name); }
|
void set_name(String name) { m_name = std::move(name); }
|
||||||
|
|
||||||
|
bool is_editing() const { return m_edition_level!= 0; }
|
||||||
|
void disable_undo_handling() { ++m_edition_level; }
|
||||||
private:
|
private:
|
||||||
|
void begin_edition();
|
||||||
|
void end_edition();
|
||||||
|
int m_edition_level = 0;
|
||||||
|
|
||||||
|
friend struct ScopedEdition;
|
||||||
|
|
||||||
safe_ptr<Editor> m_editor;
|
safe_ptr<Editor> m_editor;
|
||||||
safe_ptr<InputHandler> m_input_handler;
|
safe_ptr<InputHandler> m_input_handler;
|
||||||
safe_ptr<Client> m_client;
|
safe_ptr<Client> m_client;
|
||||||
|
@ -82,5 +90,19 @@ private:
|
||||||
JumpList::iterator m_current_jump = m_jump_list.begin();
|
JumpList::iterator m_current_jump = m_jump_list.begin();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ScopedEdition
|
||||||
|
{
|
||||||
|
ScopedEdition(Context& context)
|
||||||
|
: m_context(context)
|
||||||
|
{ m_context.begin_edition(); }
|
||||||
|
|
||||||
|
~ScopedEdition()
|
||||||
|
{ m_context.end_edition(); }
|
||||||
|
|
||||||
|
Context& context() const { return m_context; }
|
||||||
|
private:
|
||||||
|
Context& m_context;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif // context_hh_INCLUDED
|
#endif // context_hh_INCLUDED
|
||||||
|
|
|
@ -13,92 +13,9 @@ namespace Kakoune
|
||||||
|
|
||||||
Editor::Editor(Buffer& buffer)
|
Editor::Editor(Buffer& buffer)
|
||||||
: m_buffer(&buffer),
|
: m_buffer(&buffer),
|
||||||
m_edition_level(0),
|
|
||||||
m_selections(buffer, {BufferCoord{}})
|
m_selections(buffer, {BufferCoord{}})
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void Editor::erase()
|
|
||||||
{
|
|
||||||
scoped_edition edition(*this);
|
|
||||||
for (auto& sel : m_selections)
|
|
||||||
{
|
|
||||||
Kakoune::erase(*m_buffer, sel);
|
|
||||||
avoid_eol(*m_buffer, sel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BufferIterator prepare_insert(Buffer& buffer, const Selection& sel,
|
|
||||||
InsertMode mode)
|
|
||||||
{
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case InsertMode::Insert:
|
|
||||||
return buffer.iterator_at(sel.min());
|
|
||||||
case InsertMode::Replace:
|
|
||||||
return Kakoune::erase(buffer, sel);
|
|
||||||
case InsertMode::Append:
|
|
||||||
{
|
|
||||||
// special case for end of lines, append to current line instead
|
|
||||||
auto pos = buffer.iterator_at(sel.max());
|
|
||||||
return *pos == '\n' ? pos : utf8::next(pos);
|
|
||||||
}
|
|
||||||
case InsertMode::InsertAtLineBegin:
|
|
||||||
return buffer.iterator_at(sel.min().line);
|
|
||||||
case InsertMode::AppendAtLineEnd:
|
|
||||||
return buffer.iterator_at({sel.max().line, buffer[sel.max().line].length() - 1});
|
|
||||||
case InsertMode::InsertAtNextLineBegin:
|
|
||||||
return buffer.iterator_at(sel.max().line+1);
|
|
||||||
case InsertMode::OpenLineBelow:
|
|
||||||
return buffer.insert(buffer.iterator_at(sel.max().line + 1), "\n");
|
|
||||||
case InsertMode::OpenLineAbove:
|
|
||||||
return buffer.insert(buffer.iterator_at(sel.min().line), "\n");
|
|
||||||
}
|
|
||||||
kak_assert(false);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Editor::insert(const String& str, InsertMode mode)
|
|
||||||
{
|
|
||||||
scoped_edition edition(*this);
|
|
||||||
|
|
||||||
for (auto& sel : m_selections)
|
|
||||||
{
|
|
||||||
auto pos = prepare_insert(*m_buffer, sel, mode);
|
|
||||||
pos = m_buffer->insert(pos, str);
|
|
||||||
if (mode == InsertMode::Replace and pos != m_buffer->end())
|
|
||||||
{
|
|
||||||
sel.first() = pos.coord();
|
|
||||||
sel.last() = str.empty() ?
|
|
||||||
pos.coord() : (pos + str.byte_count_to(str.char_length() - 1)).coord();
|
|
||||||
}
|
|
||||||
avoid_eol(*m_buffer, sel);
|
|
||||||
}
|
|
||||||
check_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Editor::insert(memoryview<String> strings, InsertMode mode)
|
|
||||||
{
|
|
||||||
scoped_edition edition(*this);
|
|
||||||
if (strings.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < selections().size(); ++i)
|
|
||||||
{
|
|
||||||
auto& sel = m_selections[i];
|
|
||||||
auto pos = prepare_insert(*m_buffer, sel, mode);
|
|
||||||
const String& str = strings[std::min(i, strings.size()-1)];
|
|
||||||
pos = m_buffer->insert(pos, str);
|
|
||||||
if (mode == InsertMode::Replace and pos != m_buffer->end())
|
|
||||||
{
|
|
||||||
sel.first() = pos.coord();
|
|
||||||
sel.last() = (str.empty() ?
|
|
||||||
pos : pos + str.byte_count_to(str.char_length() - 1)).coord();
|
|
||||||
}
|
|
||||||
avoid_eol(*m_buffer, sel);
|
|
||||||
}
|
|
||||||
check_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<String> Editor::selections_content() const
|
std::vector<String> Editor::selections_content() const
|
||||||
{
|
{
|
||||||
std::vector<String> contents;
|
std::vector<String> contents;
|
||||||
|
@ -182,18 +99,4 @@ void Editor::check_invariant() const
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::begin_edition()
|
|
||||||
{
|
|
||||||
++m_edition_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Editor::end_edition()
|
|
||||||
{
|
|
||||||
kak_assert(m_edition_level > 0);
|
|
||||||
if (m_edition_level == 1)
|
|
||||||
m_buffer->commit_undo_group();
|
|
||||||
|
|
||||||
--m_edition_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,13 +38,6 @@ public:
|
||||||
|
|
||||||
Buffer& buffer() const { return *m_buffer; }
|
Buffer& buffer() const { return *m_buffer; }
|
||||||
|
|
||||||
void erase();
|
|
||||||
|
|
||||||
void insert(const String& string,
|
|
||||||
InsertMode mode = InsertMode::Insert);
|
|
||||||
void insert(memoryview<String> strings,
|
|
||||||
InsertMode mode = InsertMode::Insert);
|
|
||||||
|
|
||||||
const SelectionList& selections() const { return m_selections; }
|
const SelectionList& selections() const { return m_selections; }
|
||||||
SelectionList& selections() { return m_selections; }
|
SelectionList& selections() { return m_selections; }
|
||||||
std::vector<String> selections_content() const;
|
std::vector<String> selections_content() const;
|
||||||
|
@ -52,14 +45,9 @@ public:
|
||||||
bool undo();
|
bool undo();
|
||||||
bool redo();
|
bool redo();
|
||||||
|
|
||||||
bool is_editing() const { return m_edition_level!= 0; }
|
|
||||||
private:
|
private:
|
||||||
friend struct scoped_edition;
|
friend struct scoped_edition;
|
||||||
friend class InputModes::Insert;
|
friend class InputModes::Insert;
|
||||||
void begin_edition();
|
|
||||||
void end_edition();
|
|
||||||
|
|
||||||
int m_edition_level;
|
|
||||||
|
|
||||||
void check_invariant() const;
|
void check_invariant() const;
|
||||||
|
|
||||||
|
@ -67,20 +55,6 @@ private:
|
||||||
DynamicSelectionList m_selections;
|
DynamicSelectionList m_selections;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct scoped_edition
|
|
||||||
{
|
|
||||||
scoped_edition(Editor& editor)
|
|
||||||
: m_editor(editor)
|
|
||||||
{ m_editor.begin_edition(); }
|
|
||||||
|
|
||||||
~scoped_edition()
|
|
||||||
{ m_editor.end_edition(); }
|
|
||||||
|
|
||||||
Editor& editor() const { return m_editor; }
|
|
||||||
private:
|
|
||||||
Editor& m_editor;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // editor_hh_INCLUDED
|
#endif // editor_hh_INCLUDED
|
||||||
|
|
|
@ -873,7 +873,7 @@ public:
|
||||||
Insert(InputHandler& input_handler, InsertMode mode)
|
Insert(InputHandler& input_handler, InsertMode mode)
|
||||||
: InputMode(input_handler),
|
: InputMode(input_handler),
|
||||||
m_insert_mode(mode),
|
m_insert_mode(mode),
|
||||||
m_edition(context().editor()),
|
m_edition(context()),
|
||||||
m_completer(context()),
|
m_completer(context()),
|
||||||
m_idle_timer{Clock::now() + idle_timeout,
|
m_idle_timer{Clock::now() + idle_timeout,
|
||||||
[this](Timer& timer) {
|
[this](Timer& timer) {
|
||||||
|
@ -982,8 +982,8 @@ public:
|
||||||
private:
|
private:
|
||||||
void erase() const
|
void erase() const
|
||||||
{
|
{
|
||||||
auto& buffer = m_edition.editor().buffer();
|
auto& buffer = context().buffer();
|
||||||
for (auto& sel : m_edition.editor().selections())
|
for (auto& sel : context().selections())
|
||||||
{
|
{
|
||||||
if (sel.last() == BufferCoord{0,0})
|
if (sel.last() == BufferCoord{0,0})
|
||||||
continue;
|
continue;
|
||||||
|
@ -1007,8 +1007,8 @@ private:
|
||||||
|
|
||||||
void insert(memoryview<String> strings)
|
void insert(memoryview<String> strings)
|
||||||
{
|
{
|
||||||
auto& buffer = m_edition.editor().buffer();
|
auto& buffer = context().buffer();
|
||||||
auto& selections = m_edition.editor().selections();
|
auto& selections = context().selections();
|
||||||
for (size_t i = 0; i < selections.size(); ++i)
|
for (size_t i = 0; i < selections.size(); ++i)
|
||||||
{
|
{
|
||||||
size_t index = std::min(i, strings.size()-1);
|
size_t index = std::min(i, strings.size()-1);
|
||||||
|
@ -1020,18 +1020,18 @@ private:
|
||||||
void insert(Codepoint key)
|
void insert(Codepoint key)
|
||||||
{
|
{
|
||||||
auto str = codepoint_to_str(key);
|
auto str = codepoint_to_str(key);
|
||||||
auto& buffer = m_edition.editor().buffer();
|
auto& buffer = context().buffer();
|
||||||
for (auto& sel : m_edition.editor().selections())
|
for (auto& sel : context().selections())
|
||||||
buffer.insert(buffer.iterator_at(sel.last()), str);
|
buffer.insert(buffer.iterator_at(sel.last()), str);
|
||||||
context().hooks().run_hook("InsertChar", str, context());
|
context().hooks().run_hook("InsertChar", str, context());
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepare(InsertMode mode)
|
void prepare(InsertMode mode)
|
||||||
{
|
{
|
||||||
Editor& editor = m_edition.editor();
|
SelectionList& selections = context().selections();
|
||||||
Buffer& buffer = editor.buffer();
|
Buffer& buffer = context().buffer();
|
||||||
|
|
||||||
for (auto& sel : editor.m_selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
BufferCoord first, last;
|
BufferCoord first, last;
|
||||||
switch (mode)
|
switch (mode)
|
||||||
|
@ -1087,7 +1087,7 @@ private:
|
||||||
insert('\n');
|
insert('\n');
|
||||||
if (mode == InsertMode::OpenLineAbove)
|
if (mode == InsertMode::OpenLineAbove)
|
||||||
{
|
{
|
||||||
for (auto& sel : editor.m_selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
// special case, the --first line above did nothing, so we need to compensate now
|
// special case, the --first line above did nothing, so we need to compensate now
|
||||||
if (sel.first() == buffer.char_next({0,0}))
|
if (sel.first() == buffer.char_next({0,0}))
|
||||||
|
@ -1095,24 +1095,25 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editor.m_selections.sort_and_merge_overlapping();
|
selections.sort_and_merge_overlapping();
|
||||||
editor.check_invariant();
|
selections.check_invariant();
|
||||||
|
buffer.check_invariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_replaced() override
|
void on_replaced() override
|
||||||
{
|
{
|
||||||
for (auto& sel : m_edition.editor().m_selections)
|
for (auto& sel : context().selections())
|
||||||
{
|
{
|
||||||
if (m_insert_mode == InsertMode::Append and sel.last().column > 0)
|
if (m_insert_mode == InsertMode::Append and sel.last().column > 0)
|
||||||
sel.last() = m_edition.editor().buffer().char_prev(sel.last());
|
sel.last() = context().buffer().char_prev(sel.last());
|
||||||
avoid_eol(m_edition.editor().buffer(), sel);
|
avoid_eol(context().buffer(), sel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Mode { Default, Complete, InsertReg };
|
enum class Mode { Default, Complete, InsertReg };
|
||||||
Mode m_mode = Mode::Default;
|
Mode m_mode = Mode::Default;
|
||||||
InsertMode m_insert_mode;
|
InsertMode m_insert_mode;
|
||||||
scoped_edition m_edition;
|
ScopedEdition m_edition;
|
||||||
BufferCompleter m_completer;
|
BufferCompleter m_completer;
|
||||||
Timer m_idle_timer;
|
Timer m_idle_timer;
|
||||||
};
|
};
|
||||||
|
|
199
src/normal.cc
199
src/normal.cc
|
@ -20,6 +20,89 @@
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void erase(Buffer& buffer, SelectionList& selections)
|
||||||
|
{
|
||||||
|
for (auto& sel : selections)
|
||||||
|
{
|
||||||
|
erase(buffer, sel);
|
||||||
|
avoid_eol(buffer, sel);
|
||||||
|
}
|
||||||
|
selections.check_invariant();
|
||||||
|
buffer.check_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<InsertMode mode>
|
||||||
|
BufferIterator prepare_insert(Buffer& buffer, const Selection& sel)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case InsertMode::Insert:
|
||||||
|
return buffer.iterator_at(sel.min());
|
||||||
|
case InsertMode::Replace:
|
||||||
|
return Kakoune::erase(buffer, sel);
|
||||||
|
case InsertMode::Append:
|
||||||
|
{
|
||||||
|
// special case for end of lines, append to current line instead
|
||||||
|
auto pos = buffer.iterator_at(sel.max());
|
||||||
|
return *pos == '\n' ? pos : utf8::next(pos);
|
||||||
|
}
|
||||||
|
case InsertMode::InsertAtLineBegin:
|
||||||
|
return buffer.iterator_at(sel.min().line);
|
||||||
|
case InsertMode::AppendAtLineEnd:
|
||||||
|
return buffer.iterator_at({sel.max().line, buffer[sel.max().line].length() - 1});
|
||||||
|
case InsertMode::InsertAtNextLineBegin:
|
||||||
|
return buffer.iterator_at(sel.max().line+1);
|
||||||
|
case InsertMode::OpenLineBelow:
|
||||||
|
return buffer.insert(buffer.iterator_at(sel.max().line + 1), "\n");
|
||||||
|
case InsertMode::OpenLineAbove:
|
||||||
|
return buffer.insert(buffer.iterator_at(sel.min().line), "\n");
|
||||||
|
}
|
||||||
|
kak_assert(false);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<InsertMode mode>
|
||||||
|
void insert(Buffer& buffer, SelectionList& selections, const String& str)
|
||||||
|
{
|
||||||
|
for (auto& sel : selections)
|
||||||
|
{
|
||||||
|
auto pos = prepare_insert<mode>(buffer, sel);
|
||||||
|
pos = buffer.insert(pos, str);
|
||||||
|
if (mode == InsertMode::Replace and pos != buffer.end())
|
||||||
|
{
|
||||||
|
sel.first() = pos.coord();
|
||||||
|
sel.last() = str.empty() ?
|
||||||
|
pos.coord() : (pos + str.byte_count_to(str.char_length() - 1)).coord();
|
||||||
|
}
|
||||||
|
avoid_eol(buffer, sel);
|
||||||
|
}
|
||||||
|
selections.check_invariant();
|
||||||
|
buffer.check_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<InsertMode mode>
|
||||||
|
void insert(Buffer& buffer, SelectionList& selections, memoryview<String> strings)
|
||||||
|
{
|
||||||
|
if (strings.empty())
|
||||||
|
return;
|
||||||
|
for (size_t i = 0; i < selections.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& sel = selections[i];
|
||||||
|
auto pos = prepare_insert<mode>(buffer, sel);
|
||||||
|
const String& str = strings[std::min(i, strings.size()-1)];
|
||||||
|
pos = buffer.insert(pos, str);
|
||||||
|
if (mode == InsertMode::Replace and pos != buffer.end())
|
||||||
|
{
|
||||||
|
sel.first() = pos.coord();
|
||||||
|
sel.last() = (str.empty() ?
|
||||||
|
pos : pos + str.byte_count_to(str.char_length() - 1)).coord();
|
||||||
|
}
|
||||||
|
avoid_eol(buffer, sel);
|
||||||
|
}
|
||||||
|
selections.check_invariant();
|
||||||
|
buffer.check_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
enum class SelectMode
|
enum class SelectMode
|
||||||
|
@ -98,12 +181,12 @@ void select_coord(BufferCoord coord, SelectionList& selections)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<InsertMode mode>
|
template<InsertMode mode>
|
||||||
void insert(Context& context, int)
|
void enter_insert_mode(Context& context, int)
|
||||||
{
|
{
|
||||||
context.input_handler().insert(mode);
|
context.input_handler().insert(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void repeat_insert(Context& context, int)
|
void repeat_last_insert(Context& context, int)
|
||||||
{
|
{
|
||||||
context.input_handler().repeat_last_insert();
|
context.input_handler().repeat_last_insert();
|
||||||
}
|
}
|
||||||
|
@ -289,11 +372,11 @@ void replace_with_char(Context& context, int)
|
||||||
on_next_key_with_autoinfo(context, [](Key key, Context& context) {
|
on_next_key_with_autoinfo(context, [](Key key, Context& context) {
|
||||||
if (not isprint(key.key))
|
if (not isprint(key.key))
|
||||||
return;
|
return;
|
||||||
Editor& editor = context.editor();
|
ScopedEdition edition(context);
|
||||||
SelectionList sels = context.selections();
|
Buffer& buffer = context.buffer();
|
||||||
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
|
SelectionList selections = context.selections();
|
||||||
select_all_matches(context.buffer(), context.selections(), Regex{"."});
|
select_all_matches(buffer, selections, Regex{"."});
|
||||||
editor.insert(codepoint_to_str(key.key), InsertMode::Replace);
|
insert<InsertMode::Replace>(buffer, selections, codepoint_to_str(key.key));
|
||||||
}, "replace with char", "enter char to replace with\n");
|
}, "replace with char", "enter char to replace with\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +392,7 @@ Codepoint swap_case(Codepoint cp)
|
||||||
template<Codepoint (*func)(Codepoint)>
|
template<Codepoint (*func)(Codepoint)>
|
||||||
void for_each_char(Context& context, int)
|
void for_each_char(Context& context, int)
|
||||||
{
|
{
|
||||||
|
ScopedEdition edition(context);
|
||||||
Editor& editor = context.editor();
|
Editor& editor = context.editor();
|
||||||
std::vector<String> sels = editor.selections_content();
|
std::vector<String> sels = editor.selections_content();
|
||||||
for (auto& sel : sels)
|
for (auto& sel : sels)
|
||||||
|
@ -316,7 +400,7 @@ void for_each_char(Context& context, int)
|
||||||
for (auto& c : sel)
|
for (auto& c : sel)
|
||||||
c = func(c);
|
c = func(c);
|
||||||
}
|
}
|
||||||
editor.insert(sels, InsertMode::Replace);
|
insert<InsertMode::Replace>(context.buffer(), context.selections(), sels);
|
||||||
}
|
}
|
||||||
|
|
||||||
void command(Context& context, int)
|
void command(Context& context, int)
|
||||||
|
@ -350,11 +434,12 @@ void pipe(Context& context, int)
|
||||||
if (real_cmd.empty())
|
if (real_cmd.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Editor& editor = context.editor();
|
Buffer& buffer = context.buffer();
|
||||||
|
SelectionList& selections = context.selections();
|
||||||
std::vector<String> strings;
|
std::vector<String> strings;
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
auto str = content(context.buffer(), sel);
|
auto str = content(buffer, sel);
|
||||||
bool insert_eol = str.back() != '\n';
|
bool insert_eol = str.back() != '\n';
|
||||||
if (insert_eol)
|
if (insert_eol)
|
||||||
str += '\n';
|
str += '\n';
|
||||||
|
@ -364,7 +449,8 @@ void pipe(Context& context, int)
|
||||||
str = str.substr(0, str.length()-1);
|
str = str.substr(0, str.length()-1);
|
||||||
strings.push_back(str);
|
strings.push_back(str);
|
||||||
}
|
}
|
||||||
editor.insert(strings, InsertMode::Replace);
|
ScopedEdition edition(context);
|
||||||
|
insert<InsertMode::Replace>(buffer, selections, strings);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,43 +591,43 @@ void cat_yank(Context& context, int)
|
||||||
void erase_selections(Context& context, int)
|
void erase_selections(Context& context, int)
|
||||||
{
|
{
|
||||||
RegisterManager::instance()['"'] = context.editor().selections_content();
|
RegisterManager::instance()['"'] = context.editor().selections_content();
|
||||||
context.editor().erase();
|
ScopedEdition edition(context);
|
||||||
|
erase(context.buffer(), context.selections());
|
||||||
}
|
}
|
||||||
|
|
||||||
void change(Context& context, int param)
|
void change(Context& context, int param)
|
||||||
{
|
{
|
||||||
RegisterManager::instance()['"'] = context.editor().selections_content();
|
RegisterManager::instance()['"'] = context.editor().selections_content();
|
||||||
insert<InsertMode::Replace>(context, param);
|
enter_insert_mode<InsertMode::Replace>(context, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
static InsertMode adapt_for_linewise(InsertMode mode)
|
constexpr InsertMode adapt_for_linewise(InsertMode mode)
|
||||||
{
|
{
|
||||||
if (mode == InsertMode::Append)
|
return ((mode == InsertMode::Append) ?
|
||||||
return InsertMode::InsertAtNextLineBegin;
|
InsertMode::InsertAtNextLineBegin :
|
||||||
if (mode == InsertMode::Insert)
|
((mode == InsertMode::Insert) ?
|
||||||
return InsertMode::InsertAtLineBegin;
|
InsertMode::InsertAtLineBegin :
|
||||||
if (mode == InsertMode::Replace)
|
((mode == InsertMode::Replace) ?
|
||||||
return InsertMode::Replace;
|
InsertMode::Replace : InsertMode::Insert)));
|
||||||
|
|
||||||
kak_assert(false);
|
|
||||||
return InsertMode::Insert;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<InsertMode insert_mode>
|
template<InsertMode mode>
|
||||||
void paste(Context& context, int)
|
void paste(Context& context, int)
|
||||||
{
|
{
|
||||||
Editor& editor = context.editor();
|
|
||||||
auto strings = RegisterManager::instance()['"'].values(context);
|
auto strings = RegisterManager::instance()['"'].values(context);
|
||||||
InsertMode mode = insert_mode;
|
bool linewise = false;
|
||||||
for (auto& str : strings)
|
for (auto& str : strings)
|
||||||
{
|
{
|
||||||
if (not str.empty() and str.back() == '\n')
|
if (not str.empty() and str.back() == '\n')
|
||||||
{
|
{
|
||||||
mode = adapt_for_linewise(mode);
|
linewise = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editor.insert(strings, mode);
|
if (linewise)
|
||||||
|
insert<adapt_for_linewise(mode)>(context.buffer(), context.selections(), strings);
|
||||||
|
else
|
||||||
|
insert<mode>(context.buffer(), context.selections(), strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -619,17 +705,17 @@ void join_select_spaces(Context& context, int)
|
||||||
{
|
{
|
||||||
select(select_whole_lines)(context, 0);
|
select(select_whole_lines)(context, 0);
|
||||||
select<SelectMode::Extend>(select_to_eol)(context, 0);
|
select<SelectMode::Extend>(select_to_eol)(context, 0);
|
||||||
auto& editor = context.editor();
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
select_all_matches(buffer, context.selections(), Regex{"(\n\\h*)+"});
|
select_all_matches(buffer, selections, Regex{"(\n\\h*)+"});
|
||||||
// remove last end of line if selected
|
// remove last end of line if selected
|
||||||
kak_assert(std::is_sorted(selections.begin(), selections.end(),
|
kak_assert(std::is_sorted(selections.begin(), selections.end(),
|
||||||
[](const Selection& lhs, const Selection& rhs)
|
[](const Selection& lhs, const Selection& rhs)
|
||||||
{ return lhs.min() < rhs.min(); }));
|
{ return lhs.min() < rhs.min(); }));
|
||||||
if (not selections.empty() and selections.back().max() == buffer.back_coord())
|
if (not selections.empty() and selections.back().max() == buffer.back_coord())
|
||||||
selections.pop_back();
|
selections.pop_back();
|
||||||
editor.insert(" ", InsertMode::Replace);
|
ScopedEdition edition(context);
|
||||||
|
insert<InsertMode::Replace>(buffer, selections, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
void join(Context& context, int param)
|
void join(Context& context, int param)
|
||||||
|
@ -666,21 +752,18 @@ void indent(Context& context, int)
|
||||||
CharCount indent_width = context.options()["indentwidth"].get<int>();
|
CharCount indent_width = context.options()["indentwidth"].get<int>();
|
||||||
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
|
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
|
||||||
|
|
||||||
auto& editor = context.editor();
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
DynamicSelectionList sels{context.buffer(), context.selections()};
|
SelectionList sels;
|
||||||
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
|
|
||||||
SelectionList res;
|
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
||||||
{
|
{
|
||||||
if (indent_empty or buffer[line].length() > 1)
|
if (indent_empty or buffer[line].length() > 1)
|
||||||
res.emplace_back(line, line);
|
sels.emplace_back(line, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.selections() = std::move(res);
|
ScopedEdition edition(context);
|
||||||
editor.insert(indent, InsertMode::Insert);
|
insert<InsertMode::Insert>(buffer, sels, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool deindent_incomplete = true>
|
template<bool deindent_incomplete = true>
|
||||||
|
@ -691,12 +774,8 @@ void deindent(Context& context, int)
|
||||||
if (indent_width == 0)
|
if (indent_width == 0)
|
||||||
indent_width = tabstop;
|
indent_width = tabstop;
|
||||||
|
|
||||||
auto& editor = context.editor();
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
DynamicSelectionList sels{context.buffer(), context.selections()};
|
SelectionList sels;
|
||||||
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
|
|
||||||
|
|
||||||
SelectionList res;
|
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
||||||
|
@ -713,19 +792,19 @@ void deindent(Context& context, int)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (deindent_incomplete and width != 0)
|
if (deindent_incomplete and width != 0)
|
||||||
res.emplace_back(line, BufferCoord{line, column-1});
|
sels.emplace_back(line, BufferCoord{line, column-1});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (width == indent_width)
|
if (width == indent_width)
|
||||||
{
|
{
|
||||||
res.emplace_back(line, BufferCoord{line, column});
|
sels.emplace_back(line, BufferCoord{line, column});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.selections() = std::move(res);
|
ScopedEdition edition(context);
|
||||||
editor.erase();
|
erase(context.buffer(), sels);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<ObjectFlags flags, SelectMode mode = SelectMode::Replace>
|
template<ObjectFlags flags, SelectMode mode = SelectMode::Replace>
|
||||||
|
@ -827,8 +906,9 @@ void rotate_selections_content(Context& context, int count)
|
||||||
auto strings = editor.selections_content();
|
auto strings = editor.selections_content();
|
||||||
count = count % strings.size();
|
count = count % strings.size();
|
||||||
std::rotate(strings.begin(), strings.end()-count, strings.end());
|
std::rotate(strings.begin(), strings.end()-count, strings.end());
|
||||||
editor.insert(strings, InsertMode::Replace);
|
|
||||||
context.selections().rotate_main(count);
|
context.selections().rotate_main(count);
|
||||||
|
ScopedEdition edition(context);
|
||||||
|
insert<InsertMode::Replace>(context.buffer(), context.selections(), strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SelectFlags
|
enum class SelectFlags
|
||||||
|
@ -885,7 +965,7 @@ void replay_macro(Context& context, int count)
|
||||||
auto stop = on_scope_end([&]{ running_macros.erase(key.key); });
|
auto stop = on_scope_end([&]{ running_macros.erase(key.key); });
|
||||||
|
|
||||||
auto keys = parse_keys(reg_val[0]);
|
auto keys = parse_keys(reg_val[0]);
|
||||||
scoped_edition edition(context.editor());
|
ScopedEdition edition(context);
|
||||||
do { exec_keys(keys, context); } while (--count > 0);
|
do { exec_keys(keys, context); } while (--count > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -983,7 +1063,6 @@ void align(Context& context, int)
|
||||||
|
|
||||||
void align_indent(Context& context, int selection)
|
void align_indent(Context& context, int selection)
|
||||||
{
|
{
|
||||||
auto& editor = context.editor();
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
std::vector<LineCount> lines;
|
std::vector<LineCount> lines;
|
||||||
|
@ -1003,7 +1082,7 @@ void align_indent(Context& context, int selection)
|
||||||
++it;
|
++it;
|
||||||
const String indent{line.begin(), it};
|
const String indent{line.begin(), it};
|
||||||
|
|
||||||
scoped_edition edition{editor};
|
ScopedEdition edition{context};
|
||||||
for (auto& l : lines)
|
for (auto& l : lines)
|
||||||
{
|
{
|
||||||
auto& line = buffer[l];
|
auto& line = buffer[l];
|
||||||
|
@ -1023,7 +1102,7 @@ public:
|
||||||
|
|
||||||
void operator() (Context& context, int count)
|
void operator() (Context& context, int count)
|
||||||
{
|
{
|
||||||
scoped_edition edition(context.editor());
|
ScopedEdition edition(context);
|
||||||
do { m_func(context, 0); } while(--count > 0);
|
do { m_func(context, 0); } while(--count > 0);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
@ -1076,12 +1155,12 @@ KeyMap keymap =
|
||||||
|
|
||||||
{ 'd', erase_selections },
|
{ 'd', erase_selections },
|
||||||
{ 'c', change },
|
{ 'c', change },
|
||||||
{ 'i', insert<InsertMode::Insert> },
|
{ 'i', enter_insert_mode<InsertMode::Insert> },
|
||||||
{ 'I', insert<InsertMode::InsertAtLineBegin> },
|
{ 'I', enter_insert_mode<InsertMode::InsertAtLineBegin> },
|
||||||
{ 'a', insert<InsertMode::Append> },
|
{ 'a', enter_insert_mode<InsertMode::Append> },
|
||||||
{ 'A', insert<InsertMode::AppendAtLineEnd> },
|
{ 'A', enter_insert_mode<InsertMode::AppendAtLineEnd> },
|
||||||
{ 'o', insert<InsertMode::OpenLineBelow> },
|
{ 'o', enter_insert_mode<InsertMode::OpenLineBelow> },
|
||||||
{ 'O', insert<InsertMode::OpenLineAbove> },
|
{ 'O', enter_insert_mode<InsertMode::OpenLineAbove> },
|
||||||
{ 'r', replace_with_char },
|
{ 'r', replace_with_char },
|
||||||
|
|
||||||
{ 'g', goto_commands<SelectMode::Replace> },
|
{ 'g', goto_commands<SelectMode::Replace> },
|
||||||
|
@ -1099,7 +1178,7 @@ KeyMap keymap =
|
||||||
{ 'S', split_regex },
|
{ 'S', split_regex },
|
||||||
{ alt('s'), split_lines },
|
{ alt('s'), split_lines },
|
||||||
|
|
||||||
{ '.', repeat_insert },
|
{ '.', repeat_last_insert },
|
||||||
|
|
||||||
{ '%', [](Context& context, int) { select_whole_buffer(context.buffer(), context.selections()); } },
|
{ '%', [](Context& context, int) { select_whole_buffer(context.buffer(), context.selections()); } },
|
||||||
|
|
||||||
|
|
|
@ -68,30 +68,6 @@ void test_undo_group_optimizer()
|
||||||
kak_assert(lines[i] == buffer[LineCount((int)i)]);
|
kak_assert(lines[i] == buffer[LineCount((int)i)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_editor()
|
|
||||||
{
|
|
||||||
using namespace std::placeholders;
|
|
||||||
Buffer buffer("test", Buffer::Flags::None, { "test\n", "\n", "youpi\n" });
|
|
||||||
Editor editor(buffer);
|
|
||||||
|
|
||||||
{
|
|
||||||
scoped_edition edition{editor};
|
|
||||||
select_whole_buffer(buffer, editor.selections());
|
|
||||||
select_all_matches(buffer, editor.selections(), Regex{"\\n\\h*"});
|
|
||||||
for (auto& sel : editor.selections())
|
|
||||||
{
|
|
||||||
kak_assert(buffer.byte_at(sel.min()) == '\n');
|
|
||||||
erase(buffer, sel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
editor.undo();
|
|
||||||
|
|
||||||
Selection sel{ 2_line, buffer.back_coord() };
|
|
||||||
editor.selections() = SelectionList{sel};
|
|
||||||
editor.insert("",InsertMode::Replace);
|
|
||||||
kak_assert(not buffer.is_end(editor.selections().main().first()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_utf8()
|
void test_utf8()
|
||||||
{
|
{
|
||||||
String str = "maïs mélange bientôt";
|
String str = "maïs mélange bientôt";
|
||||||
|
@ -146,5 +122,4 @@ void run_unit_tests()
|
||||||
test_keys();
|
test_keys();
|
||||||
test_buffer();
|
test_buffer();
|
||||||
test_undo_group_optimizer();
|
test_undo_group_optimizer();
|
||||||
test_editor();
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user