SelectionList know its buffer and timestamp
This commit is contained in:
parent
7bc73b7ef9
commit
ea3e92aa5e
|
@ -706,7 +706,7 @@ void Buffer::on_option_changed(const Option& option)
|
||||||
|
|
||||||
void Buffer::run_hook_in_own_context(const String& hook_name, const String& param)
|
void Buffer::run_hook_in_own_context(const String& hook_name, const String& param)
|
||||||
{
|
{
|
||||||
InputHandler hook_handler(*this, { Selection{} });
|
InputHandler hook_handler({ *this, Selection{} });
|
||||||
m_hooks.run_hook(hook_name, param, hook_handler.context());
|
m_hooks.run_hook(hook_name, param, hook_handler.context());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ Client::Client(std::unique_ptr<UserInterface>&& ui,
|
||||||
EnvVarMap env_vars,
|
EnvVarMap env_vars,
|
||||||
String name)
|
String name)
|
||||||
: m_ui{std::move(ui)}, m_window{std::move(window)},
|
: m_ui{std::move(ui)}, m_window{std::move(window)},
|
||||||
m_input_handler{m_window->buffer(), std::move(selections),
|
m_input_handler{std::move(selections),
|
||||||
std::move(name)},
|
std::move(name)},
|
||||||
m_env_vars(env_vars)
|
m_env_vars(env_vars)
|
||||||
{
|
{
|
||||||
|
@ -102,7 +102,7 @@ static void reload_buffer(Context& context, const String& filename)
|
||||||
if (not buf)
|
if (not buf)
|
||||||
return;
|
return;
|
||||||
context.change_buffer(*buf);
|
context.change_buffer(*buf);
|
||||||
context.selections() = SelectionList{buf->clamp(cursor_pos)};
|
context.selections() = SelectionList{ *buf, buf->clamp(cursor_pos)};
|
||||||
context.window().set_position(view_pos);
|
context.window().set_position(view_pos);
|
||||||
context.print_status({ "'" + buf->display_name() + "' reloaded",
|
context.print_status({ "'" + buf->display_name() + "' reloaded",
|
||||||
get_color("Information") });
|
get_color("Information") });
|
||||||
|
|
|
@ -94,18 +94,18 @@ WindowAndSelections ClientManager::get_free_window(Buffer& buffer)
|
||||||
w->forget_timestamp();
|
w->forget_timestamp();
|
||||||
WindowAndSelections res = std::move(*it);
|
WindowAndSelections res = std::move(*it);
|
||||||
m_free_windows.erase(it.base()-1);
|
m_free_windows.erase(it.base()-1);
|
||||||
|
res.selections.update();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return WindowAndSelections{ std::unique_ptr<Window>{new Window{buffer}},
|
return WindowAndSelections{ std::unique_ptr<Window>{new Window{buffer}},
|
||||||
DynamicSelectionList{buffer,
|
SelectionList{ buffer, Selection{} } };
|
||||||
{ Selection{ {}, {} } } } };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
|
void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
|
||||||
{
|
{
|
||||||
Buffer& buffer = window->buffer();
|
Buffer& buffer = window->buffer();
|
||||||
m_free_windows.push_back({ std::move(window), DynamicSelectionList{ buffer, std::move(selections) } });
|
m_free_windows.push_back({ std::move(window), SelectionList{ std::move(selections) }, buffer.timestamp() });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
|
void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
|
||||||
|
|
|
@ -12,7 +12,8 @@ struct client_removed{};
|
||||||
struct WindowAndSelections
|
struct WindowAndSelections
|
||||||
{
|
{
|
||||||
std::unique_ptr<Window> window;
|
std::unique_ptr<Window> window;
|
||||||
DynamicSelectionList selections;
|
SelectionList selections;
|
||||||
|
size_t timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClientManager : public Singleton<ClientManager>
|
class ClientManager : public Singleton<ClientManager>
|
||||||
|
|
|
@ -131,7 +131,8 @@ void edit(const ParametersParser& parser, Context& context)
|
||||||
int column = param_count > 2 and not parser[2].empty() ?
|
int column = param_count > 2 and not parser[2].empty() ?
|
||||||
std::max(0, str_to_int(parser[2]) - 1) : 0;
|
std::max(0, str_to_int(parser[2]) - 1) : 0;
|
||||||
|
|
||||||
context.selections() = context.buffer().clamp({ line, column });
|
auto& buffer = context.buffer();
|
||||||
|
context.selections() = { buffer, buffer.clamp({ line, column }) };
|
||||||
if (context.has_window())
|
if (context.has_window())
|
||||||
context.window().center_line(context.selections().main().cursor().line);
|
context.window().center_line(context.selections().main().cursor().line);
|
||||||
}
|
}
|
||||||
|
@ -962,7 +963,7 @@ void context_wrap(const ParametersParser& parser, Context& context, Func func)
|
||||||
for (auto& name : names)
|
for (auto& name : names)
|
||||||
{
|
{
|
||||||
Buffer& buffer = BufferManager::instance().get_buffer(name);
|
Buffer& buffer = BufferManager::instance().get_buffer(name);
|
||||||
InputHandler input_handler{buffer, ( Selection{} )};
|
InputHandler input_handler{{ buffer, Selection{} }};
|
||||||
func(parser, input_handler.context());
|
func(parser, input_handler.context());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -980,7 +981,7 @@ void context_wrap(const ParametersParser& parser, Context& context, Func func)
|
||||||
|
|
||||||
if (parser.has_option("draft"))
|
if (parser.has_option("draft"))
|
||||||
{
|
{
|
||||||
InputHandler input_handler(real_context->buffer(), real_context->selections(), real_context->name());
|
InputHandler input_handler(real_context->selections(), real_context->name());
|
||||||
|
|
||||||
// We do not want this draft context to commit undo groups if the real one is
|
// We do not want this draft context to commit undo groups if the real one is
|
||||||
// going to commit the whole thing later
|
// going to commit the whole thing later
|
||||||
|
@ -989,12 +990,17 @@ void context_wrap(const ParametersParser& parser, Context& context, Func func)
|
||||||
|
|
||||||
if (parser.has_option("itersel"))
|
if (parser.has_option("itersel"))
|
||||||
{
|
{
|
||||||
DynamicSelectionList sels{real_context->buffer(), real_context->selections()};
|
SelectionList sels{real_context->selections()};
|
||||||
ScopedEdition edition{input_handler.context()};
|
ScopedEdition edition{input_handler.context()};
|
||||||
for (auto& sel : sels)
|
for (auto& sel : sels)
|
||||||
{
|
{
|
||||||
input_handler.context().selections() = sel;
|
input_handler.context().selections() = SelectionList{ sels.buffer(), sel, sels.timestamp() };
|
||||||
|
input_handler.context().selections().update();
|
||||||
|
|
||||||
func(parser, input_handler.context());
|
func(parser, input_handler.context());
|
||||||
|
|
||||||
|
if (&sels.buffer() != &input_handler.context().buffer())
|
||||||
|
throw runtime_error("the buffer has changed while iterating on selections");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -10,10 +10,10 @@ namespace Kakoune
|
||||||
Context::Context() = default;
|
Context::Context() = default;
|
||||||
Context::~Context() = default;
|
Context::~Context() = default;
|
||||||
|
|
||||||
Context::Context(InputHandler& input_handler, Buffer& buffer,
|
Context::Context(InputHandler& input_handler, SelectionList selections,
|
||||||
SelectionList selections, String name)
|
String name)
|
||||||
: m_input_handler{&input_handler},
|
: m_input_handler{&input_handler},
|
||||||
m_selections{{buffer, std::move(selections)}},
|
m_selections{std::move(selections)},
|
||||||
m_name(std::move(name))
|
m_name(std::move(name))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ void Context::push_jump()
|
||||||
}
|
}
|
||||||
m_jump_list.erase(std::remove(begin(m_jump_list), end(m_jump_list), jump),
|
m_jump_list.erase(std::remove(begin(m_jump_list), end(m_jump_list), jump),
|
||||||
end(m_jump_list));
|
end(m_jump_list));
|
||||||
m_jump_list.push_back({buffer(), jump});
|
m_jump_list.push_back(jump);
|
||||||
m_current_jump = m_jump_list.end();
|
m_current_jump = m_jump_list.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ void Context::change_buffer(Buffer& buffer)
|
||||||
if (has_client())
|
if (has_client())
|
||||||
client().change_buffer(buffer);
|
client().change_buffer(buffer);
|
||||||
else
|
else
|
||||||
m_selections = DynamicSelectionList{ buffer };
|
m_selections = DynamicSelectionList{ { buffer, Selection{} } };
|
||||||
if (has_input_handler())
|
if (has_input_handler())
|
||||||
input_handler().reset_normal_mode();
|
input_handler().reset_normal_mode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@ class Context
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Context();
|
Context();
|
||||||
Context(InputHandler& input_handler, Buffer& buffer, SelectionList selections, String name = "");
|
Context(InputHandler& input_handler, SelectionList selections,
|
||||||
|
String name = "");
|
||||||
~Context();
|
~Context();
|
||||||
|
|
||||||
Context(const Context&) = delete;
|
Context(const Context&) = delete;
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
DynamicSelectionList::DynamicSelectionList(Buffer& buffer,
|
DynamicSelectionList::DynamicSelectionList(SelectionList selections)
|
||||||
SelectionList selections)
|
|
||||||
: SelectionList(std::move(selections)),
|
: SelectionList(std::move(selections)),
|
||||||
BufferChangeListener_AutoRegister(buffer)
|
BufferChangeListener_AutoRegister(const_cast<Buffer&>(buffer()))
|
||||||
{
|
{
|
||||||
check_invariant();
|
check_invariant();
|
||||||
}
|
}
|
||||||
|
@ -18,32 +17,16 @@ DynamicSelectionList& DynamicSelectionList::operator=(SelectionList selections)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicSelectionList::check_invariant() const
|
|
||||||
{
|
|
||||||
#ifdef KAK_DEBUG
|
|
||||||
SelectionList::check_invariant();
|
|
||||||
const Buffer& buffer = registry();
|
|
||||||
for (size_t i = 0; i < size(); ++i)
|
|
||||||
{
|
|
||||||
auto& sel = (*this)[i];
|
|
||||||
kak_assert(buffer.is_valid(sel.anchor()));
|
|
||||||
kak_assert(buffer.is_valid(sel.cursor()));
|
|
||||||
kak_assert(not buffer.is_end(sel.anchor()));
|
|
||||||
kak_assert(not buffer.is_end(sel.cursor()));
|
|
||||||
kak_assert(utf8::is_character_start(buffer.iterator_at(sel.anchor())));
|
|
||||||
kak_assert(utf8::is_character_start(buffer.iterator_at(sel.cursor())));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void DynamicSelectionList::on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
void DynamicSelectionList::on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
{
|
{
|
||||||
update_insert(begin, end, at_end);
|
update_insert(begin, end, at_end);
|
||||||
|
set_timestamp(buffer.timestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicSelectionList::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
void DynamicSelectionList::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
{
|
{
|
||||||
update_erase(begin, end, at_end);
|
update_erase(begin, end, at_end);
|
||||||
|
set_timestamp(buffer.timestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,11 @@ public:
|
||||||
using iterator = SelectionList::iterator;
|
using iterator = SelectionList::iterator;
|
||||||
using const_iterator = SelectionList::const_iterator;
|
using const_iterator = SelectionList::const_iterator;
|
||||||
|
|
||||||
DynamicSelectionList(Buffer& buffer, SelectionList selections = { Selection{} });
|
DynamicSelectionList(SelectionList selections);
|
||||||
|
|
||||||
DynamicSelectionList& operator=(SelectionList selections);
|
DynamicSelectionList& operator=(SelectionList selections);
|
||||||
void check_invariant() const;
|
|
||||||
|
using SelectionList::buffer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) override;
|
void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) override;
|
||||||
|
|
|
@ -875,9 +875,9 @@ void InputMode::reset_normal_mode()
|
||||||
m_input_handler.reset_normal_mode();
|
m_input_handler.reset_normal_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
InputHandler::InputHandler(Buffer& buffer, SelectionList selections, String name)
|
InputHandler::InputHandler(SelectionList selections, String name)
|
||||||
: m_mode(new InputModes::Normal(*this)),
|
: m_mode(new InputModes::Normal(*this)),
|
||||||
m_context(*this, buffer, std::move(selections), std::move(name))
|
m_context(*this, std::move(selections), std::move(name))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ enum class InsertMode : unsigned;
|
||||||
class InputHandler : public SafeCountable
|
class InputHandler : public SafeCountable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InputHandler(Buffer& buffer, SelectionList selections, String name = "");
|
InputHandler(SelectionList selections, String name = "");
|
||||||
~InputHandler();
|
~InputHandler();
|
||||||
|
|
||||||
// switch to insert mode
|
// switch to insert mode
|
||||||
|
|
|
@ -26,6 +26,7 @@ void erase(Buffer& buffer, SelectionList& selections)
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
erase(buffer, sel);
|
erase(buffer, sel);
|
||||||
|
selections.update();
|
||||||
avoid_eol(buffer, sel);
|
avoid_eol(buffer, sel);
|
||||||
}
|
}
|
||||||
selections.check_invariant();
|
selections.check_invariant();
|
||||||
|
@ -178,7 +179,7 @@ void select_coord(const Buffer& buffer, ByteCoord coord, SelectionList& selectio
|
||||||
{
|
{
|
||||||
coord = buffer.clamp(coord);
|
coord = buffer.clamp(coord);
|
||||||
if (mode == SelectMode::Replace)
|
if (mode == SelectMode::Replace)
|
||||||
selections = SelectionList { coord };
|
selections = SelectionList{ buffer, coord };
|
||||||
else if (mode == SelectMode::Extend)
|
else if (mode == SelectMode::Extend)
|
||||||
{
|
{
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
|
@ -598,7 +599,7 @@ void paste(Context& context, int)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void regex_prompt(Context& context, const String prompt, T func)
|
void regex_prompt(Context& context, const String prompt, T func)
|
||||||
{
|
{
|
||||||
DynamicSelectionList selections{context.buffer(), context.selections()};
|
DynamicSelectionList selections{context.selections()};
|
||||||
context.input_handler().prompt(prompt, "", get_color("Prompt"), complete_nothing,
|
context.input_handler().prompt(prompt, "", get_color("Prompt"), complete_nothing,
|
||||||
[=](const String& str, PromptEvent event, Context& context) {
|
[=](const String& str, PromptEvent event, Context& context) {
|
||||||
try
|
try
|
||||||
|
@ -738,7 +739,7 @@ void split_lines(Context& context, int)
|
||||||
{
|
{
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList res;
|
SelectionList res(context.buffer());
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
if (sel.anchor().line == sel.cursor().line)
|
if (sel.anchor().line == sel.cursor().line)
|
||||||
|
@ -760,7 +761,7 @@ void split_lines(Context& context, int)
|
||||||
void join_select_spaces(Context& context, int)
|
void join_select_spaces(Context& context, int)
|
||||||
{
|
{
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList selections;
|
SelectionList selections(buffer);
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
for (LineCount line = sel.min().line; line <= sel.max().line; ++line)
|
for (LineCount line = sel.min().line; line <= sel.max().line; ++line)
|
||||||
|
@ -783,8 +784,12 @@ void join_select_spaces(Context& context, int)
|
||||||
|
|
||||||
void join(Context& context, int param)
|
void join(Context& context, int param)
|
||||||
{
|
{
|
||||||
DynamicSelectionList sels{context.buffer(), context.selections()};
|
SelectionList sels{context.selections()};
|
||||||
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
|
auto restore_sels = on_scope_end([&]{
|
||||||
|
sels.update();
|
||||||
|
context.selections() = std::move(sels);
|
||||||
|
});
|
||||||
|
|
||||||
join_select_spaces(context, param);
|
join_select_spaces(context, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -796,7 +801,7 @@ void keep(Context& context, int)
|
||||||
if (ex.empty())
|
if (ex.empty())
|
||||||
return;
|
return;
|
||||||
const Buffer& buffer = context.buffer();
|
const Buffer& buffer = context.buffer();
|
||||||
SelectionList keep;
|
SelectionList keep(buffer);
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
if (boost::regex_search(buffer.iterator_at(sel.min()),
|
if (boost::regex_search(buffer.iterator_at(sel.min()),
|
||||||
|
@ -818,7 +823,7 @@ void keep_pipe(Context& context, int)
|
||||||
return;
|
return;
|
||||||
const Buffer& buffer = context.buffer();
|
const Buffer& buffer = context.buffer();
|
||||||
auto& shell_manager = ShellManager::instance();
|
auto& shell_manager = ShellManager::instance();
|
||||||
SelectionList keep;
|
SelectionList keep(buffer);
|
||||||
for (auto& sel : context.selections())
|
for (auto& sel : context.selections())
|
||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
@ -839,7 +844,7 @@ void indent(Context& context, int)
|
||||||
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
|
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
|
||||||
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList sels;
|
SelectionList sels(buffer);
|
||||||
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)
|
||||||
|
@ -864,7 +869,7 @@ void deindent(Context& context, int)
|
||||||
indent_width = tabstop;
|
indent_width = tabstop;
|
||||||
|
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
SelectionList sels;
|
SelectionList sels(buffer);
|
||||||
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)
|
||||||
|
@ -1235,7 +1240,7 @@ void spaces_to_tabs(Context& context, int ts)
|
||||||
|
|
||||||
static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timestamp)
|
static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timestamp)
|
||||||
{
|
{
|
||||||
SelectionList ranges;
|
SelectionList ranges(buffer);
|
||||||
for (auto& change : buffer.changes_since(timestamp))
|
for (auto& change : buffer.changes_since(timestamp))
|
||||||
{
|
{
|
||||||
const ByteCoord& begin = change.begin;
|
const ByteCoord& begin = change.begin;
|
||||||
|
@ -1264,6 +1269,7 @@ static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timest
|
||||||
if (ranges.empty())
|
if (ranges.empty())
|
||||||
return ranges;
|
return ranges;
|
||||||
|
|
||||||
|
ranges.set_timestamp(buffer.timestamp());
|
||||||
ranges.set_main_index(ranges.size() - 1);
|
ranges.set_main_index(ranges.size() - 1);
|
||||||
|
|
||||||
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
auto touches = [&](const Selection& lhs, const Selection& rhs) {
|
||||||
|
|
|
@ -63,6 +63,7 @@ struct UpdateInsert
|
||||||
coord.column = end.column + coord.column - begin.column;
|
coord.column = end.column + coord.column - begin.column;
|
||||||
|
|
||||||
coord.line += end.line - begin.line;
|
coord.line += end.line - begin.line;
|
||||||
|
kak_assert(coord.line >= 0 and coord.column >= 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ struct UpdateErase
|
||||||
kak_assert(end.line < coord.line);
|
kak_assert(end.line < coord.line);
|
||||||
if (not assume_different_line and coord <= end)
|
if (not assume_different_line and coord <= end)
|
||||||
{
|
{
|
||||||
if (not at_end)
|
if (not at_end or begin == ByteCoord{0,0})
|
||||||
coord = begin;
|
coord = begin;
|
||||||
else
|
else
|
||||||
coord = begin.column ? ByteCoord{begin.line, begin.column-1}
|
coord = begin.column ? ByteCoord{begin.line, begin.column-1}
|
||||||
|
@ -94,11 +95,33 @@ struct UpdateErase
|
||||||
else
|
else
|
||||||
coord.line -= end.line - begin.line;
|
coord.line -= end.line - begin.line;
|
||||||
}
|
}
|
||||||
|
kak_assert(coord.line >= 0 and coord.column >= 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectionList::SelectionList(const Buffer& buffer)
|
||||||
|
: m_buffer(&buffer), m_timestamp(buffer.timestamp())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionList::SelectionList(const Buffer& buffer, Selection s, size_t timestamp)
|
||||||
|
: m_buffer(&buffer), m_selections({ s }), m_timestamp(timestamp)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SelectionList::SelectionList(const Buffer& buffer, Selection s)
|
||||||
|
: SelectionList(buffer, s, buffer.timestamp())
|
||||||
|
{}
|
||||||
|
|
||||||
|
SelectionList::SelectionList(const Buffer& buffer, std::vector<Selection> s, size_t timestamp)
|
||||||
|
: m_buffer(&buffer), m_selections(std::move(s)), m_timestamp(timestamp)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SelectionList::SelectionList(const Buffer& buffer, std::vector<Selection> s)
|
||||||
|
: SelectionList(buffer, std::move(s), buffer.timestamp())
|
||||||
|
{}
|
||||||
|
|
||||||
void SelectionList::update_insert(ByteCoord begin, ByteCoord end, bool at_end)
|
void SelectionList::update_insert(ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
{
|
{
|
||||||
on_buffer_change<UpdateInsert>(*this, begin, end, at_end, begin.line);
|
on_buffer_change<UpdateInsert>(*this, begin, end, at_end, begin.line);
|
||||||
|
@ -109,12 +132,58 @@ void SelectionList::update_erase(ByteCoord begin, ByteCoord end, bool at_end)
|
||||||
on_buffer_change<UpdateErase>(*this, begin, end, at_end, end.line);
|
on_buffer_change<UpdateErase>(*this, begin, end, at_end, end.line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SelectionList::update()
|
||||||
|
{
|
||||||
|
for (auto& change : m_buffer->changes_since(m_timestamp))
|
||||||
|
{
|
||||||
|
if (change.type == Buffer::Change::Insert)
|
||||||
|
update_insert(change.begin, change.end, change.at_end);
|
||||||
|
else
|
||||||
|
update_erase(change.begin, change.end, change.at_end);
|
||||||
|
}
|
||||||
|
m_timestamp = m_buffer->timestamp();
|
||||||
|
|
||||||
|
check_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
void SelectionList::check_invariant() const
|
void SelectionList::check_invariant() const
|
||||||
{
|
{
|
||||||
|
#ifdef KAK_DEBUG
|
||||||
|
auto& buffer = this->buffer();
|
||||||
kak_assert(size() > 0);
|
kak_assert(size() > 0);
|
||||||
kak_assert(m_main < size());
|
kak_assert(m_main < size());
|
||||||
for (size_t i = 0; i+1 < size(); ++ i)
|
for (size_t i = 0; i+1 < size(); ++ i)
|
||||||
kak_assert((*this)[i].min() <= (*this)[i+1].min());
|
kak_assert((*this)[i].min() <= (*this)[i+1].min());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size(); ++i)
|
||||||
|
{
|
||||||
|
auto& sel = (*this)[i];
|
||||||
|
kak_assert(buffer.is_valid(sel.anchor()));
|
||||||
|
kak_assert(buffer.is_valid(sel.cursor()));
|
||||||
|
kak_assert(not buffer.is_end(sel.anchor()));
|
||||||
|
kak_assert(not buffer.is_end(sel.cursor()));
|
||||||
|
kak_assert(utf8::is_character_start(buffer.iterator_at(sel.anchor())));
|
||||||
|
kak_assert(utf8::is_character_start(buffer.iterator_at(sel.cursor())));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelectionList::sort_and_merge_overlapping()
|
||||||
|
{
|
||||||
|
if (size() == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto& main = this->main();
|
||||||
|
const auto main_begin = main.min();
|
||||||
|
m_main = std::count_if(begin(), end(), [&](const Selection& sel) {
|
||||||
|
auto begin = sel.min();
|
||||||
|
if (begin == main_begin)
|
||||||
|
return &sel < &main;
|
||||||
|
else
|
||||||
|
return begin < main_begin;
|
||||||
|
});
|
||||||
|
std::stable_sort(begin(), end(), compare_selections);
|
||||||
|
merge_overlapping(overlaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ using CaptureList = std::vector<String>;
|
||||||
struct Selection
|
struct Selection
|
||||||
{
|
{
|
||||||
Selection() = default;
|
Selection() = default;
|
||||||
explicit Selection(ByteCoord pos) : Selection(pos,pos) {}
|
Selection(ByteCoord pos) : Selection(pos,pos) {}
|
||||||
Selection(ByteCoord anchor, ByteCoord cursor,
|
Selection(ByteCoord anchor, ByteCoord cursor,
|
||||||
CaptureList captures = {})
|
CaptureList captures = {})
|
||||||
: m_anchor{anchor}, m_cursor{cursor},
|
: m_anchor{anchor}, m_cursor{cursor},
|
||||||
|
@ -57,13 +57,17 @@ static bool compare_selections(const Selection& lhs, const Selection& rhs)
|
||||||
|
|
||||||
struct SelectionList
|
struct SelectionList
|
||||||
{
|
{
|
||||||
SelectionList() = default;
|
SelectionList(const Buffer& buffer);
|
||||||
SelectionList(ByteCoord c) : m_selections{Selection{c,c}} {}
|
SelectionList(const Buffer& buffer, Selection s);
|
||||||
SelectionList(Selection s) : m_selections{s} {}
|
SelectionList(const Buffer& buffer, Selection s, size_t timestamp);
|
||||||
|
SelectionList(const Buffer& buffer, std::vector<Selection> s);
|
||||||
|
SelectionList(const Buffer& buffer, std::vector<Selection> s, size_t timestamp);
|
||||||
|
|
||||||
void update_insert(ByteCoord begin, ByteCoord end, bool at_end);
|
void update_insert(ByteCoord begin, ByteCoord end, bool at_end);
|
||||||
void update_erase(ByteCoord begin, ByteCoord end, bool at_end);
|
void update_erase(ByteCoord begin, ByteCoord end, bool at_end);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
void check_invariant() const;
|
void check_invariant() const;
|
||||||
|
|
||||||
const Selection& main() const { return (*this)[m_main]; }
|
const Selection& main() const { return (*this)[m_main]; }
|
||||||
|
@ -102,8 +106,8 @@ struct SelectionList
|
||||||
size_t size() const { return m_selections.size(); }
|
size_t size() const { return m_selections.size(); }
|
||||||
bool empty() const { return m_selections.empty(); }
|
bool empty() const { return m_selections.empty(); }
|
||||||
|
|
||||||
bool operator==(const SelectionList& other) const { return m_selections == other.m_selections; }
|
bool operator==(const SelectionList& other) const { return m_buffer == other.m_buffer and m_selections == other.m_selections; }
|
||||||
bool operator!=(const SelectionList& other) const { return m_selections != other.m_selections; }
|
bool operator!=(const SelectionList& other) const { return !((*this) == other); }
|
||||||
|
|
||||||
template<typename OverlapsFunc>
|
template<typename OverlapsFunc>
|
||||||
void merge_overlapping(OverlapsFunc overlaps)
|
void merge_overlapping(OverlapsFunc overlaps)
|
||||||
|
@ -129,27 +133,19 @@ struct SelectionList
|
||||||
kak_assert(std::is_sorted(begin(), end(), compare_selections));
|
kak_assert(std::is_sorted(begin(), end(), compare_selections));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sort_and_merge_overlapping()
|
void sort_and_merge_overlapping();
|
||||||
{
|
|
||||||
if (size() == 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto& main = this->main();
|
const Buffer& buffer() const { return *m_buffer; }
|
||||||
const auto main_begin = main.min();
|
|
||||||
m_main = std::count_if(begin(), end(), [&](const Selection& sel) {
|
size_t timestamp() const { return m_timestamp; }
|
||||||
auto begin = sel.min();
|
void set_timestamp(size_t timestamp) { m_timestamp = timestamp; }
|
||||||
if (begin == main_begin)
|
|
||||||
return &sel < &main;
|
|
||||||
else
|
|
||||||
return begin < main_begin;
|
|
||||||
});
|
|
||||||
std::stable_sort(begin(), end(), compare_selections);
|
|
||||||
merge_overlapping(overlaps);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_main = 0;
|
size_t m_main = 0;
|
||||||
std::vector<Selection> m_selections;
|
std::vector<Selection> m_selections;
|
||||||
|
|
||||||
|
safe_ptr<const Buffer> m_buffer;
|
||||||
|
size_t m_timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,13 +431,13 @@ Selection trim_partial_lines(const Buffer& buffer, const Selection& selection)
|
||||||
|
|
||||||
void select_whole_buffer(const Buffer& buffer, SelectionList& selections)
|
void select_whole_buffer(const Buffer& buffer, SelectionList& selections)
|
||||||
{
|
{
|
||||||
selections = SelectionList{ Selection({0,0}, buffer.back_coord()) };
|
selections = SelectionList{ buffer, Selection({0,0}, buffer.back_coord()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void select_all_matches(const Buffer& buffer, SelectionList& selections,
|
void select_all_matches(const Buffer& buffer, SelectionList& selections,
|
||||||
const Regex& regex)
|
const Regex& regex)
|
||||||
{
|
{
|
||||||
SelectionList result;
|
SelectionList result(buffer);
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
|
auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
|
||||||
|
@ -470,7 +470,7 @@ void select_all_matches(const Buffer& buffer, SelectionList& selections,
|
||||||
void split_selections(const Buffer& buffer, SelectionList& selections,
|
void split_selections(const Buffer& buffer, SelectionList& selections,
|
||||||
const Regex& regex)
|
const Regex& regex)
|
||||||
{
|
{
|
||||||
SelectionList result;
|
SelectionList result(buffer);
|
||||||
for (auto& sel : selections)
|
for (auto& sel : selections)
|
||||||
{
|
{
|
||||||
auto begin = buffer.iterator_at(sel.min());
|
auto begin = buffer.iterator_at(sel.min());
|
||||||
|
|
|
@ -16,7 +16,7 @@ inline void clear_selections(const Buffer& buffer, SelectionList& selections)
|
||||||
avoid_eol(buffer, pos);
|
avoid_eol(buffer, pos);
|
||||||
sel.anchor() = pos;
|
sel.anchor() = pos;
|
||||||
|
|
||||||
selections = SelectionList{ std::move(sel) };
|
selections = SelectionList{ buffer, std::move(sel) };
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void flip_selections(SelectionList& selections)
|
inline void flip_selections(SelectionList& selections)
|
||||||
|
@ -31,7 +31,7 @@ inline void keep_selection(SelectionList& selections, int index)
|
||||||
if (index < selections.size())
|
if (index < selections.size())
|
||||||
{
|
{
|
||||||
size_t real_index = (index + selections.main_index() + 1) % selections.size();
|
size_t real_index = (index + selections.main_index() + 1) % selections.size();
|
||||||
selections = SelectionList{ std::move(selections[real_index]) };
|
selections = SelectionList{ selections.buffer(), std::move(selections[real_index]) };
|
||||||
}
|
}
|
||||||
selections.check_invariant();
|
selections.check_invariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ Window::Window(Buffer& buffer)
|
||||||
m_options(buffer.options()),
|
m_options(buffer.options()),
|
||||||
m_keymaps(buffer.keymaps())
|
m_keymaps(buffer.keymaps())
|
||||||
{
|
{
|
||||||
InputHandler hook_handler{*m_buffer, { Selection{} } };
|
InputHandler hook_handler{{ *m_buffer, Selection{} }};
|
||||||
hook_handler.context().set_window(*this);
|
hook_handler.context().set_window(*this);
|
||||||
m_hooks.run_hook("WinCreate", buffer.name(), hook_handler.context());
|
m_hooks.run_hook("WinCreate", buffer.name(), hook_handler.context());
|
||||||
m_options.register_watcher(*this);
|
m_options.register_watcher(*this);
|
||||||
|
@ -38,7 +38,7 @@ Window::Window(Buffer& buffer)
|
||||||
|
|
||||||
Window::~Window()
|
Window::~Window()
|
||||||
{
|
{
|
||||||
InputHandler hook_handler{*m_buffer, { Selection{} } };
|
InputHandler hook_handler{{ *m_buffer, Selection{} }};
|
||||||
hook_handler.context().set_window(*this);
|
hook_handler.context().set_window(*this);
|
||||||
m_hooks.run_hook("WinClose", buffer().name(), hook_handler.context());
|
m_hooks.run_hook("WinClose", buffer().name(), hook_handler.context());
|
||||||
m_options.unregister_watcher(*this);
|
m_options.unregister_watcher(*this);
|
||||||
|
@ -262,7 +262,7 @@ ByteCoord Window::offset_coord(ByteCoord coord, LineCount offset)
|
||||||
lines.emplace_back(AtomList{ {buffer(), line, line+1} });
|
lines.emplace_back(AtomList{ {buffer(), line, line+1} });
|
||||||
display_buffer.compute_range();
|
display_buffer.compute_range();
|
||||||
|
|
||||||
InputHandler hook_handler{*m_buffer, { Selection{} } };
|
InputHandler hook_handler{{ *m_buffer, Selection{} } };
|
||||||
hook_handler.context().set_window(*this);
|
hook_handler.context().set_window(*this);
|
||||||
m_highlighters(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer);
|
m_highlighters(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer);
|
||||||
m_builtin_highlighters(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer);
|
m_builtin_highlighters(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer);
|
||||||
|
@ -274,7 +274,7 @@ ByteCoord Window::offset_coord(ByteCoord coord, LineCount offset)
|
||||||
void Window::on_option_changed(const Option& option)
|
void Window::on_option_changed(const Option& option)
|
||||||
{
|
{
|
||||||
String desc = option.name() + "=" + option.get_as_string();
|
String desc = option.name() + "=" + option.get_as_string();
|
||||||
InputHandler hook_handler{*m_buffer, { Selection{} } };
|
InputHandler hook_handler{{ *m_buffer, Selection{} }};
|
||||||
hook_handler.context().set_window(*this);
|
hook_handler.context().set_window(*this);
|
||||||
m_hooks.run_hook("WinSetOption", desc, hook_handler.context());
|
m_hooks.run_hook("WinSetOption", desc, hook_handler.context());
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user