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)
|
||||
continue;
|
||||
|
||||
if (client->context().editor().is_editing())
|
||||
if (client->context().is_editing())
|
||||
throw runtime_error("client '" + client->context().name() + "' is inserting in '" +
|
||||
buffer.display_name() + '\'');
|
||||
|
||||
|
|
|
@ -573,6 +573,11 @@ void context_wrap(CommandParameters params, Context& context, Func func)
|
|||
DynamicSelectionList sels{editor.buffer(), editor.selections()};
|
||||
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"))
|
||||
{
|
||||
for (auto& sel : sels)
|
||||
|
@ -804,7 +809,7 @@ void exec_keys(const KeyList& keys, Context& context)
|
|||
RegisterRestorer quote('"', context);
|
||||
RegisterRestorer slash('/', context);
|
||||
|
||||
scoped_edition edition(context.editor());
|
||||
ScopedEdition edition(context);
|
||||
|
||||
for (auto& key : keys)
|
||||
context.input_handler().handle_key(key);
|
||||
|
|
|
@ -187,4 +187,18 @@ const SelectionList& Context::selections() const
|
|||
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; }
|
||||
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:
|
||||
void begin_edition();
|
||||
void end_edition();
|
||||
int m_edition_level = 0;
|
||||
|
||||
friend struct ScopedEdition;
|
||||
|
||||
safe_ptr<Editor> m_editor;
|
||||
safe_ptr<InputHandler> m_input_handler;
|
||||
safe_ptr<Client> m_client;
|
||||
|
@ -82,5 +90,19 @@ private:
|
|||
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
|
||||
|
|
|
@ -13,92 +13,9 @@ namespace Kakoune
|
|||
|
||||
Editor::Editor(Buffer& buffer)
|
||||
: m_buffer(&buffer),
|
||||
m_edition_level(0),
|
||||
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> contents;
|
||||
|
@ -182,18 +99,4 @@ void Editor::check_invariant() const
|
|||
#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; }
|
||||
|
||||
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; }
|
||||
SelectionList& selections() { return m_selections; }
|
||||
std::vector<String> selections_content() const;
|
||||
|
@ -52,14 +45,9 @@ public:
|
|||
bool undo();
|
||||
bool redo();
|
||||
|
||||
bool is_editing() const { return m_edition_level!= 0; }
|
||||
private:
|
||||
friend struct scoped_edition;
|
||||
friend class InputModes::Insert;
|
||||
void begin_edition();
|
||||
void end_edition();
|
||||
|
||||
int m_edition_level;
|
||||
|
||||
void check_invariant() const;
|
||||
|
||||
|
@ -67,20 +55,6 @@ private:
|
|||
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
|
||||
|
|
|
@ -873,7 +873,7 @@ public:
|
|||
Insert(InputHandler& input_handler, InsertMode mode)
|
||||
: InputMode(input_handler),
|
||||
m_insert_mode(mode),
|
||||
m_edition(context().editor()),
|
||||
m_edition(context()),
|
||||
m_completer(context()),
|
||||
m_idle_timer{Clock::now() + idle_timeout,
|
||||
[this](Timer& timer) {
|
||||
|
@ -982,8 +982,8 @@ public:
|
|||
private:
|
||||
void erase() const
|
||||
{
|
||||
auto& buffer = m_edition.editor().buffer();
|
||||
for (auto& sel : m_edition.editor().selections())
|
||||
auto& buffer = context().buffer();
|
||||
for (auto& sel : context().selections())
|
||||
{
|
||||
if (sel.last() == BufferCoord{0,0})
|
||||
continue;
|
||||
|
@ -1007,8 +1007,8 @@ private:
|
|||
|
||||
void insert(memoryview<String> strings)
|
||||
{
|
||||
auto& buffer = m_edition.editor().buffer();
|
||||
auto& selections = m_edition.editor().selections();
|
||||
auto& buffer = context().buffer();
|
||||
auto& selections = context().selections();
|
||||
for (size_t i = 0; i < selections.size(); ++i)
|
||||
{
|
||||
size_t index = std::min(i, strings.size()-1);
|
||||
|
@ -1020,18 +1020,18 @@ private:
|
|||
void insert(Codepoint key)
|
||||
{
|
||||
auto str = codepoint_to_str(key);
|
||||
auto& buffer = m_edition.editor().buffer();
|
||||
for (auto& sel : m_edition.editor().selections())
|
||||
auto& buffer = context().buffer();
|
||||
for (auto& sel : context().selections())
|
||||
buffer.insert(buffer.iterator_at(sel.last()), str);
|
||||
context().hooks().run_hook("InsertChar", str, context());
|
||||
}
|
||||
|
||||
void prepare(InsertMode mode)
|
||||
{
|
||||
Editor& editor = m_edition.editor();
|
||||
Buffer& buffer = editor.buffer();
|
||||
SelectionList& selections = context().selections();
|
||||
Buffer& buffer = context().buffer();
|
||||
|
||||
for (auto& sel : editor.m_selections)
|
||||
for (auto& sel : selections)
|
||||
{
|
||||
BufferCoord first, last;
|
||||
switch (mode)
|
||||
|
@ -1087,7 +1087,7 @@ private:
|
|||
insert('\n');
|
||||
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
|
||||
if (sel.first() == buffer.char_next({0,0}))
|
||||
|
@ -1095,24 +1095,25 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
editor.m_selections.sort_and_merge_overlapping();
|
||||
editor.check_invariant();
|
||||
selections.sort_and_merge_overlapping();
|
||||
selections.check_invariant();
|
||||
buffer.check_invariant();
|
||||
}
|
||||
|
||||
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)
|
||||
sel.last() = m_edition.editor().buffer().char_prev(sel.last());
|
||||
avoid_eol(m_edition.editor().buffer(), sel);
|
||||
sel.last() = context().buffer().char_prev(sel.last());
|
||||
avoid_eol(context().buffer(), sel);
|
||||
}
|
||||
}
|
||||
|
||||
enum class Mode { Default, Complete, InsertReg };
|
||||
Mode m_mode = Mode::Default;
|
||||
InsertMode m_insert_mode;
|
||||
scoped_edition m_edition;
|
||||
ScopedEdition m_edition;
|
||||
BufferCompleter m_completer;
|
||||
Timer m_idle_timer;
|
||||
};
|
||||
|
|
199
src/normal.cc
199
src/normal.cc
|
@ -20,6 +20,89 @@
|
|||
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;
|
||||
|
||||
enum class SelectMode
|
||||
|
@ -98,12 +181,12 @@ void select_coord(BufferCoord coord, SelectionList& selections)
|
|||
}
|
||||
|
||||
template<InsertMode mode>
|
||||
void insert(Context& context, int)
|
||||
void enter_insert_mode(Context& context, int)
|
||||
{
|
||||
context.input_handler().insert(mode);
|
||||
}
|
||||
|
||||
void repeat_insert(Context& context, int)
|
||||
void repeat_last_insert(Context& context, int)
|
||||
{
|
||||
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) {
|
||||
if (not isprint(key.key))
|
||||
return;
|
||||
Editor& editor = context.editor();
|
||||
SelectionList sels = context.selections();
|
||||
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
|
||||
select_all_matches(context.buffer(), context.selections(), Regex{"."});
|
||||
editor.insert(codepoint_to_str(key.key), InsertMode::Replace);
|
||||
ScopedEdition edition(context);
|
||||
Buffer& buffer = context.buffer();
|
||||
SelectionList selections = context.selections();
|
||||
select_all_matches(buffer, selections, Regex{"."});
|
||||
insert<InsertMode::Replace>(buffer, selections, codepoint_to_str(key.key));
|
||||
}, "replace with char", "enter char to replace with\n");
|
||||
}
|
||||
|
||||
|
@ -309,6 +392,7 @@ Codepoint swap_case(Codepoint cp)
|
|||
template<Codepoint (*func)(Codepoint)>
|
||||
void for_each_char(Context& context, int)
|
||||
{
|
||||
ScopedEdition edition(context);
|
||||
Editor& editor = context.editor();
|
||||
std::vector<String> sels = editor.selections_content();
|
||||
for (auto& sel : sels)
|
||||
|
@ -316,7 +400,7 @@ void for_each_char(Context& context, int)
|
|||
for (auto& c : sel)
|
||||
c = func(c);
|
||||
}
|
||||
editor.insert(sels, InsertMode::Replace);
|
||||
insert<InsertMode::Replace>(context.buffer(), context.selections(), sels);
|
||||
}
|
||||
|
||||
void command(Context& context, int)
|
||||
|
@ -350,11 +434,12 @@ void pipe(Context& context, int)
|
|||
if (real_cmd.empty())
|
||||
return;
|
||||
|
||||
Editor& editor = context.editor();
|
||||
Buffer& buffer = context.buffer();
|
||||
SelectionList& selections = context.selections();
|
||||
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';
|
||||
if (insert_eol)
|
||||
str += '\n';
|
||||
|
@ -364,7 +449,8 @@ void pipe(Context& context, int)
|
|||
str = str.substr(0, str.length()-1);
|
||||
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)
|
||||
{
|
||||
RegisterManager::instance()['"'] = context.editor().selections_content();
|
||||
context.editor().erase();
|
||||
ScopedEdition edition(context);
|
||||
erase(context.buffer(), context.selections());
|
||||
}
|
||||
|
||||
void change(Context& context, int param)
|
||||
{
|
||||
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 InsertMode::InsertAtNextLineBegin;
|
||||
if (mode == InsertMode::Insert)
|
||||
return InsertMode::InsertAtLineBegin;
|
||||
if (mode == InsertMode::Replace)
|
||||
return InsertMode::Replace;
|
||||
|
||||
kak_assert(false);
|
||||
return InsertMode::Insert;
|
||||
return ((mode == InsertMode::Append) ?
|
||||
InsertMode::InsertAtNextLineBegin :
|
||||
((mode == InsertMode::Insert) ?
|
||||
InsertMode::InsertAtLineBegin :
|
||||
((mode == InsertMode::Replace) ?
|
||||
InsertMode::Replace : InsertMode::Insert)));
|
||||
}
|
||||
|
||||
template<InsertMode insert_mode>
|
||||
template<InsertMode mode>
|
||||
void paste(Context& context, int)
|
||||
{
|
||||
Editor& editor = context.editor();
|
||||
auto strings = RegisterManager::instance()['"'].values(context);
|
||||
InsertMode mode = insert_mode;
|
||||
bool linewise = false;
|
||||
for (auto& str : strings)
|
||||
{
|
||||
if (not str.empty() and str.back() == '\n')
|
||||
{
|
||||
mode = adapt_for_linewise(mode);
|
||||
linewise = true;
|
||||
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>
|
||||
|
@ -619,17 +705,17 @@ void join_select_spaces(Context& context, int)
|
|||
{
|
||||
select(select_whole_lines)(context, 0);
|
||||
select<SelectMode::Extend>(select_to_eol)(context, 0);
|
||||
auto& editor = context.editor();
|
||||
auto& buffer = context.buffer();
|
||||
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
|
||||
kak_assert(std::is_sorted(selections.begin(), selections.end(),
|
||||
[](const Selection& lhs, const Selection& rhs)
|
||||
{ return lhs.min() < rhs.min(); }));
|
||||
if (not selections.empty() and selections.back().max() == buffer.back_coord())
|
||||
selections.pop_back();
|
||||
editor.insert(" ", InsertMode::Replace);
|
||||
ScopedEdition edition(context);
|
||||
insert<InsertMode::Replace>(buffer, selections, " ");
|
||||
}
|
||||
|
||||
void join(Context& context, int param)
|
||||
|
@ -666,21 +752,18 @@ void indent(Context& context, int)
|
|||
CharCount indent_width = context.options()["indentwidth"].get<int>();
|
||||
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
|
||||
|
||||
auto& editor = context.editor();
|
||||
auto& buffer = context.buffer();
|
||||
DynamicSelectionList sels{context.buffer(), context.selections()};
|
||||
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
|
||||
SelectionList res;
|
||||
SelectionList sels;
|
||||
for (auto& sel : context.selections())
|
||||
{
|
||||
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
||||
{
|
||||
if (indent_empty or buffer[line].length() > 1)
|
||||
res.emplace_back(line, line);
|
||||
sels.emplace_back(line, line);
|
||||
}
|
||||
}
|
||||
context.selections() = std::move(res);
|
||||
editor.insert(indent, InsertMode::Insert);
|
||||
ScopedEdition edition(context);
|
||||
insert<InsertMode::Insert>(buffer, sels, indent);
|
||||
}
|
||||
|
||||
template<bool deindent_incomplete = true>
|
||||
|
@ -691,12 +774,8 @@ void deindent(Context& context, int)
|
|||
if (indent_width == 0)
|
||||
indent_width = tabstop;
|
||||
|
||||
auto& editor = context.editor();
|
||||
auto& buffer = context.buffer();
|
||||
DynamicSelectionList sels{context.buffer(), context.selections()};
|
||||
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
|
||||
|
||||
SelectionList res;
|
||||
SelectionList sels;
|
||||
for (auto& sel : context.selections())
|
||||
{
|
||||
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
|
||||
|
@ -713,19 +792,19 @@ void deindent(Context& context, int)
|
|||
else
|
||||
{
|
||||
if (deindent_incomplete and width != 0)
|
||||
res.emplace_back(line, BufferCoord{line, column-1});
|
||||
sels.emplace_back(line, BufferCoord{line, column-1});
|
||||
break;
|
||||
}
|
||||
if (width == indent_width)
|
||||
{
|
||||
res.emplace_back(line, BufferCoord{line, column});
|
||||
sels.emplace_back(line, BufferCoord{line, column});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
context.selections() = std::move(res);
|
||||
editor.erase();
|
||||
ScopedEdition edition(context);
|
||||
erase(context.buffer(), sels);
|
||||
}
|
||||
|
||||
template<ObjectFlags flags, SelectMode mode = SelectMode::Replace>
|
||||
|
@ -827,8 +906,9 @@ void rotate_selections_content(Context& context, int count)
|
|||
auto strings = editor.selections_content();
|
||||
count = count % strings.size();
|
||||
std::rotate(strings.begin(), strings.end()-count, strings.end());
|
||||
editor.insert(strings, InsertMode::Replace);
|
||||
context.selections().rotate_main(count);
|
||||
ScopedEdition edition(context);
|
||||
insert<InsertMode::Replace>(context.buffer(), context.selections(), strings);
|
||||
}
|
||||
|
||||
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 keys = parse_keys(reg_val[0]);
|
||||
scoped_edition edition(context.editor());
|
||||
ScopedEdition edition(context);
|
||||
do { exec_keys(keys, context); } while (--count > 0);
|
||||
}
|
||||
}
|
||||
|
@ -983,7 +1063,6 @@ void align(Context& context, int)
|
|||
|
||||
void align_indent(Context& context, int selection)
|
||||
{
|
||||
auto& editor = context.editor();
|
||||
auto& buffer = context.buffer();
|
||||
auto& selections = context.selections();
|
||||
std::vector<LineCount> lines;
|
||||
|
@ -1003,7 +1082,7 @@ void align_indent(Context& context, int selection)
|
|||
++it;
|
||||
const String indent{line.begin(), it};
|
||||
|
||||
scoped_edition edition{editor};
|
||||
ScopedEdition edition{context};
|
||||
for (auto& l : lines)
|
||||
{
|
||||
auto& line = buffer[l];
|
||||
|
@ -1023,7 +1102,7 @@ public:
|
|||
|
||||
void operator() (Context& context, int count)
|
||||
{
|
||||
scoped_edition edition(context.editor());
|
||||
ScopedEdition edition(context);
|
||||
do { m_func(context, 0); } while(--count > 0);
|
||||
}
|
||||
private:
|
||||
|
@ -1076,12 +1155,12 @@ KeyMap keymap =
|
|||
|
||||
{ 'd', erase_selections },
|
||||
{ 'c', change },
|
||||
{ 'i', insert<InsertMode::Insert> },
|
||||
{ 'I', insert<InsertMode::InsertAtLineBegin> },
|
||||
{ 'a', insert<InsertMode::Append> },
|
||||
{ 'A', insert<InsertMode::AppendAtLineEnd> },
|
||||
{ 'o', insert<InsertMode::OpenLineBelow> },
|
||||
{ 'O', insert<InsertMode::OpenLineAbove> },
|
||||
{ 'i', enter_insert_mode<InsertMode::Insert> },
|
||||
{ 'I', enter_insert_mode<InsertMode::InsertAtLineBegin> },
|
||||
{ 'a', enter_insert_mode<InsertMode::Append> },
|
||||
{ 'A', enter_insert_mode<InsertMode::AppendAtLineEnd> },
|
||||
{ 'o', enter_insert_mode<InsertMode::OpenLineBelow> },
|
||||
{ 'O', enter_insert_mode<InsertMode::OpenLineAbove> },
|
||||
{ 'r', replace_with_char },
|
||||
|
||||
{ 'g', goto_commands<SelectMode::Replace> },
|
||||
|
@ -1099,7 +1178,7 @@ KeyMap keymap =
|
|||
{ 'S', split_regex },
|
||||
{ alt('s'), split_lines },
|
||||
|
||||
{ '.', repeat_insert },
|
||||
{ '.', repeat_last_insert },
|
||||
|
||||
{ '%', [](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)]);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
String str = "maïs mélange bientôt";
|
||||
|
@ -146,5 +122,4 @@ void run_unit_tests()
|
|||
test_keys();
|
||||
test_buffer();
|
||||
test_undo_group_optimizer();
|
||||
test_editor();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user