SelectionList know its buffer and timestamp

This commit is contained in:
Maxime Coste 2014-05-12 23:25:15 +01:00
parent 7bc73b7ef9
commit ea3e92aa5e
17 changed files with 150 additions and 87 deletions

View File

@ -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)
{
InputHandler hook_handler(*this, { Selection{} });
InputHandler hook_handler({ *this, Selection{} });
m_hooks.run_hook(hook_name, param, hook_handler.context());
}

View File

@ -18,7 +18,7 @@ Client::Client(std::unique_ptr<UserInterface>&& ui,
EnvVarMap env_vars,
String name)
: 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)},
m_env_vars(env_vars)
{
@ -102,7 +102,7 @@ static void reload_buffer(Context& context, const String& filename)
if (not buf)
return;
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.print_status({ "'" + buf->display_name() + "' reloaded",
get_color("Information") });

View File

@ -94,18 +94,18 @@ WindowAndSelections ClientManager::get_free_window(Buffer& buffer)
w->forget_timestamp();
WindowAndSelections res = std::move(*it);
m_free_windows.erase(it.base()-1);
res.selections.update();
return res;
}
}
return WindowAndSelections{ std::unique_ptr<Window>{new Window{buffer}},
DynamicSelectionList{buffer,
{ Selection{ {}, {} } } } };
SelectionList{ buffer, Selection{} } };
}
void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
{
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)

View File

@ -12,7 +12,8 @@ struct client_removed{};
struct WindowAndSelections
{
std::unique_ptr<Window> window;
DynamicSelectionList selections;
SelectionList selections;
size_t timestamp;
};
class ClientManager : public Singleton<ClientManager>

View File

@ -131,7 +131,8 @@ void edit(const ParametersParser& parser, Context& context)
int column = param_count > 2 and not parser[2].empty() ?
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())
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)
{
Buffer& buffer = BufferManager::instance().get_buffer(name);
InputHandler input_handler{buffer, ( Selection{} )};
InputHandler input_handler{{ buffer, Selection{} }};
func(parser, input_handler.context());
}
return;
@ -980,7 +981,7 @@ void context_wrap(const ParametersParser& parser, Context& context, Func func)
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
// 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"))
{
DynamicSelectionList sels{real_context->buffer(), real_context->selections()};
SelectionList sels{real_context->selections()};
ScopedEdition edition{input_handler.context()};
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());
if (&sels.buffer() != &input_handler.context().buffer())
throw runtime_error("the buffer has changed while iterating on selections");
}
}
else

View File

@ -10,10 +10,10 @@ namespace Kakoune
Context::Context() = default;
Context::~Context() = default;
Context::Context(InputHandler& input_handler, Buffer& buffer,
SelectionList selections, String name)
Context::Context(InputHandler& input_handler, SelectionList selections,
String name)
: m_input_handler{&input_handler},
m_selections{{buffer, std::move(selections)}},
m_selections{std::move(selections)},
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),
end(m_jump_list));
m_jump_list.push_back({buffer(), jump});
m_jump_list.push_back(jump);
m_current_jump = m_jump_list.end();
}
@ -168,7 +168,7 @@ void Context::change_buffer(Buffer& buffer)
if (has_client())
client().change_buffer(buffer);
else
m_selections = DynamicSelectionList{ buffer };
m_selections = DynamicSelectionList{ { buffer, Selection{} } };
if (has_input_handler())
input_handler().reset_normal_mode();
}

View File

@ -26,7 +26,8 @@ class Context
{
public:
Context();
Context(InputHandler& input_handler, Buffer& buffer, SelectionList selections, String name = "");
Context(InputHandler& input_handler, SelectionList selections,
String name = "");
~Context();
Context(const Context&) = delete;

View File

@ -3,10 +3,9 @@
namespace Kakoune
{
DynamicSelectionList::DynamicSelectionList(Buffer& buffer,
SelectionList selections)
DynamicSelectionList::DynamicSelectionList(SelectionList selections)
: SelectionList(std::move(selections)),
BufferChangeListener_AutoRegister(buffer)
BufferChangeListener_AutoRegister(const_cast<Buffer&>(buffer()))
{
check_invariant();
}
@ -18,32 +17,16 @@ DynamicSelectionList& DynamicSelectionList::operator=(SelectionList selections)
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)
{
update_insert(begin, end, at_end);
set_timestamp(buffer.timestamp());
}
void DynamicSelectionList::on_erase(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end)
{
update_erase(begin, end, at_end);
set_timestamp(buffer.timestamp());
}
}

View File

@ -13,10 +13,11 @@ public:
using iterator = SelectionList::iterator;
using const_iterator = SelectionList::const_iterator;
DynamicSelectionList(Buffer& buffer, SelectionList selections = { Selection{} });
DynamicSelectionList(SelectionList selections);
DynamicSelectionList& operator=(SelectionList selections);
void check_invariant() const;
using SelectionList::buffer;
private:
void on_insert(const Buffer& buffer, ByteCoord begin, ByteCoord end, bool at_end) override;

View File

@ -875,9 +875,9 @@ void InputMode::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_context(*this, buffer, std::move(selections), std::move(name))
m_context(*this, std::move(selections), std::move(name))
{
}

View File

@ -35,7 +35,7 @@ enum class InsertMode : unsigned;
class InputHandler : public SafeCountable
{
public:
InputHandler(Buffer& buffer, SelectionList selections, String name = "");
InputHandler(SelectionList selections, String name = "");
~InputHandler();
// switch to insert mode

View File

@ -26,6 +26,7 @@ void erase(Buffer& buffer, SelectionList& selections)
for (auto& sel : selections)
{
erase(buffer, sel);
selections.update();
avoid_eol(buffer, sel);
}
selections.check_invariant();
@ -178,7 +179,7 @@ void select_coord(const Buffer& buffer, ByteCoord coord, SelectionList& selectio
{
coord = buffer.clamp(coord);
if (mode == SelectMode::Replace)
selections = SelectionList { coord };
selections = SelectionList{ buffer, coord };
else if (mode == SelectMode::Extend)
{
for (auto& sel : selections)
@ -598,7 +599,7 @@ void paste(Context& context, int)
template<typename T>
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,
[=](const String& str, PromptEvent event, Context& context) {
try
@ -738,7 +739,7 @@ void split_lines(Context& context, int)
{
auto& selections = context.selections();
auto& buffer = context.buffer();
SelectionList res;
SelectionList res(context.buffer());
for (auto& sel : selections)
{
if (sel.anchor().line == sel.cursor().line)
@ -760,7 +761,7 @@ void split_lines(Context& context, int)
void join_select_spaces(Context& context, int)
{
auto& buffer = context.buffer();
SelectionList selections;
SelectionList selections(buffer);
for (auto& sel : context.selections())
{
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)
{
DynamicSelectionList sels{context.buffer(), context.selections()};
auto restore_sels = on_scope_end([&]{ context.selections() = std::move(sels); });
SelectionList sels{context.selections()};
auto restore_sels = on_scope_end([&]{
sels.update();
context.selections() = std::move(sels);
});
join_select_spaces(context, param);
}
@ -796,7 +801,7 @@ void keep(Context& context, int)
if (ex.empty())
return;
const Buffer& buffer = context.buffer();
SelectionList keep;
SelectionList keep(buffer);
for (auto& sel : context.selections())
{
if (boost::regex_search(buffer.iterator_at(sel.min()),
@ -818,7 +823,7 @@ void keep_pipe(Context& context, int)
return;
const Buffer& buffer = context.buffer();
auto& shell_manager = ShellManager::instance();
SelectionList keep;
SelectionList keep(buffer);
for (auto& sel : context.selections())
{
int status = 0;
@ -839,7 +844,7 @@ void indent(Context& context, int)
String indent = indent_width == 0 ? "\t" : String{' ', indent_width};
auto& buffer = context.buffer();
SelectionList sels;
SelectionList sels(buffer);
for (auto& sel : context.selections())
{
for (auto line = sel.min().line; line < sel.max().line+1; ++line)
@ -864,7 +869,7 @@ void deindent(Context& context, int)
indent_width = tabstop;
auto& buffer = context.buffer();
SelectionList sels;
SelectionList sels(buffer);
for (auto& sel : context.selections())
{
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)
{
SelectionList ranges;
SelectionList ranges(buffer);
for (auto& change : buffer.changes_since(timestamp))
{
const ByteCoord& begin = change.begin;
@ -1264,6 +1269,7 @@ static SelectionList compute_modified_ranges(const Buffer& buffer, size_t timest
if (ranges.empty())
return ranges;
ranges.set_timestamp(buffer.timestamp());
ranges.set_main_index(ranges.size() - 1);
auto touches = [&](const Selection& lhs, const Selection& rhs) {

View File

@ -63,6 +63,7 @@ struct UpdateInsert
coord.column = end.column + coord.column - begin.column;
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);
if (not assume_different_line and coord <= end)
{
if (not at_end)
if (not at_end or begin == ByteCoord{0,0})
coord = begin;
else
coord = begin.column ? ByteCoord{begin.line, begin.column-1}
@ -94,11 +95,33 @@ struct UpdateErase
else
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)
{
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);
}
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
{
#ifdef KAK_DEBUG
auto& buffer = this->buffer();
kak_assert(size() > 0);
kak_assert(m_main < size());
for (size_t i = 0; i+1 < size(); ++ i)
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);
}
}

View File

@ -12,7 +12,7 @@ using CaptureList = std::vector<String>;
struct Selection
{
Selection() = default;
explicit Selection(ByteCoord pos) : Selection(pos,pos) {}
Selection(ByteCoord pos) : Selection(pos,pos) {}
Selection(ByteCoord anchor, ByteCoord cursor,
CaptureList captures = {})
: m_anchor{anchor}, m_cursor{cursor},
@ -57,13 +57,17 @@ static bool compare_selections(const Selection& lhs, const Selection& rhs)
struct SelectionList
{
SelectionList() = default;
SelectionList(ByteCoord c) : m_selections{Selection{c,c}} {}
SelectionList(Selection s) : m_selections{s} {}
SelectionList(const Buffer& buffer);
SelectionList(const Buffer& buffer, Selection 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_erase(ByteCoord begin, ByteCoord end, bool at_end);
void update();
void check_invariant() const;
const Selection& main() const { return (*this)[m_main]; }
@ -102,8 +106,8 @@ struct SelectionList
size_t size() const { return m_selections.size(); }
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_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 !((*this) == other); }
template<typename OverlapsFunc>
void merge_overlapping(OverlapsFunc overlaps)
@ -129,27 +133,19 @@ struct SelectionList
kak_assert(std::is_sorted(begin(), end(), compare_selections));
}
void sort_and_merge_overlapping()
{
if (size() == 1)
return;
void sort_and_merge_overlapping();
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);
}
const Buffer& buffer() const { return *m_buffer; }
size_t timestamp() const { return m_timestamp; }
void set_timestamp(size_t timestamp) { m_timestamp = timestamp; }
private:
size_t m_main = 0;
std::vector<Selection> m_selections;
safe_ptr<const Buffer> m_buffer;
size_t m_timestamp;
};
}

View File

@ -431,13 +431,13 @@ Selection trim_partial_lines(const Buffer& buffer, const Selection& selection)
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,
const Regex& regex)
{
SelectionList result;
SelectionList result(buffer);
for (auto& sel : selections)
{
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,
const Regex& regex)
{
SelectionList result;
SelectionList result(buffer);
for (auto& sel : selections)
{
auto begin = buffer.iterator_at(sel.min());

View File

@ -16,7 +16,7 @@ inline void clear_selections(const Buffer& buffer, SelectionList& selections)
avoid_eol(buffer, pos);
sel.anchor() = pos;
selections = SelectionList{ std::move(sel) };
selections = SelectionList{ buffer, std::move(sel) };
}
inline void flip_selections(SelectionList& selections)
@ -31,7 +31,7 @@ inline void keep_selection(SelectionList& selections, int index)
if (index < 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();
}

View File

@ -23,7 +23,7 @@ Window::Window(Buffer& buffer)
m_options(buffer.options()),
m_keymaps(buffer.keymaps())
{
InputHandler hook_handler{*m_buffer, { Selection{} } };
InputHandler hook_handler{{ *m_buffer, Selection{} }};
hook_handler.context().set_window(*this);
m_hooks.run_hook("WinCreate", buffer.name(), hook_handler.context());
m_options.register_watcher(*this);
@ -38,7 +38,7 @@ Window::Window(Buffer& buffer)
Window::~Window()
{
InputHandler hook_handler{*m_buffer, { Selection{} } };
InputHandler hook_handler{{ *m_buffer, Selection{} }};
hook_handler.context().set_window(*this);
m_hooks.run_hook("WinClose", buffer().name(), hook_handler.context());
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} });
display_buffer.compute_range();
InputHandler hook_handler{*m_buffer, { Selection{} } };
InputHandler hook_handler{{ *m_buffer, Selection{} } };
hook_handler.context().set_window(*this);
m_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)
{
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);
m_hooks.run_hook("WinSetOption", desc, hook_handler.context());