summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands.cc2
-rw-r--r--src/context.hh14
-rw-r--r--src/input_handler.cc31
-rw-r--r--src/normal.cc59
4 files changed, 91 insertions, 15 deletions
diff --git a/src/commands.cc b/src/commands.cc
index 6715fd05..f7d328e3 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -2009,6 +2009,7 @@ void context_wrap(const ParametersParser& parser, Context& context, StringView d
ScopedSetBool disable_history(c.history_disabled());
ScopedEdition edition{c};
+ ScopedSelectionEdition selection_edition{c};
if (parser.get_switch("itersel"))
{
@@ -2523,6 +2524,7 @@ const CommandDesc select_cmd = {
else if (parser.get_switch("display-column"))
column_type = ColumnType::DisplayColumn;
ColumnCount tabstop = context.options()["tabstop"].get<int>();
+ ScopedSelectionEdition selection_edition{context};
context.selections_write_only() = selection_list_from_strings(buffer, column_type, parser.positionals_from(0), timestamp, 0, tabstop);
}
};
diff --git a/src/context.hh b/src/context.hh
index 6a7480cc..a855d243 100644
--- a/src/context.hh
+++ b/src/context.hh
@@ -180,5 +180,19 @@ private:
SafePtr<Buffer> m_buffer;
};
+struct ScopedSelectionEdition
+{
+ ScopedSelectionEdition(Context& context)
+ : m_context{context},
+ m_buffer{context.has_buffer() ? &context.buffer() : nullptr} {}
+ ScopedSelectionEdition(ScopedSelectionEdition&& other) : m_context{other.m_context}, m_buffer{other.m_buffer}
+ { other.m_buffer = nullptr; }
+
+ ~ScopedSelectionEdition() {}
+private:
+ Context& m_context;
+ SafePtr<Buffer> m_buffer;
+};
+
}
#endif // context_hh_INCLUDED
diff --git a/src/input_handler.cc b/src/input_handler.cc
index 956c32d7..73ad276d 100644
--- a/src/input_handler.cc
+++ b/src/input_handler.cc
@@ -93,59 +93,67 @@ struct MouseHandler
Buffer& buffer = context.buffer();
BufferCoord cursor;
- auto& selections = context.selections();
constexpr auto modifiers = Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift | Key::Modifiers::MouseButtonMask;
switch ((key.modifiers & ~modifiers).value)
{
case Key::Modifiers::MousePress:
switch (key.mouse_button())
{
- case Key::MouseButton::Right:
- m_dragging = false;
+ case Key::MouseButton::Right: {
+ m_dragging.reset();
cursor = context.window().buffer_coord(key.coord());
+ ScopedSelectionEdition selection_edition{context};
+ auto& selections = context.selections();
if (key.modifiers & Key::Modifiers::Control)
selections = {{selections.begin()->anchor(), cursor}};
else
selections.main() = {selections.main().anchor(), cursor};
selections.sort_and_merge_overlapping();
return true;
+ }
- case Key::MouseButton::Left:
- m_dragging = true;
+ case Key::MouseButton::Left: {
+ m_dragging.reset(new ScopedSelectionEdition{context});
m_anchor = context.window().buffer_coord(key.coord());
if (not (key.modifiers & Key::Modifiers::Control))
context.selections_write_only() = { buffer, m_anchor};
else
{
+ auto& selections = context.selections();
size_t main = selections.size();
selections.push_back({m_anchor});
selections.set_main_index(main);
selections.sort_and_merge_overlapping();
}
return true;
+ }
default: return true;
}
- case Key::Modifiers::MouseRelease:
+ case Key::Modifiers::MouseRelease: {
if (not m_dragging)
return true;
- m_dragging = false;
+ auto& selections = context.selections();
cursor = context.window().buffer_coord(key.coord());
selections.main() = {buffer.clamp(m_anchor), cursor};
selections.sort_and_merge_overlapping();
+ m_dragging.reset();
return true;
+ }
- case Key::Modifiers::MousePos:
+ case Key::Modifiers::MousePos: {
if (not m_dragging)
return true;
cursor = context.window().buffer_coord(key.coord());
+ auto& selections = context.selections();
selections.main() = {buffer.clamp(m_anchor), cursor};
selections.sort_and_merge_overlapping();
return true;
+ }
case Key::Modifiers::Scroll:
- scroll_window(context, static_cast<int32_t>(key.key), m_dragging);
+ scroll_window(context, static_cast<int32_t>(key.key), (bool)m_dragging);
return true;
default: return false;
@@ -153,7 +161,7 @@ struct MouseHandler
}
private:
- bool m_dragging = false;
+ std::unique_ptr<ScopedSelectionEdition> m_dragging;
BufferCoord m_anchor;
};
@@ -1199,6 +1207,7 @@ public:
Insert(InputHandler& input_handler, InsertMode mode, int count)
: InputMode(input_handler),
m_edition(context()),
+ m_selection_edition(context()),
m_completer(context()),
m_restore_cursor(mode == InsertMode::Append),
m_auto_complete{context().options()["autocomplete"].get<AutoComplete>() & AutoComplete::Insert},
@@ -1549,6 +1558,7 @@ private:
}
ScopedEdition m_edition;
+ ScopedSelectionEdition m_selection_edition;
InsertCompleter m_completer;
const bool m_restore_cursor;
bool m_auto_complete;
@@ -1806,6 +1816,7 @@ void scroll_window(Context& context, LineCount offset, bool mouse_dragging)
win_pos.line = clamp(win_pos.line + offset, 0_line, line_count-1);
+ ScopedSelectionEdition selection_edition{context};
SelectionList& selections = context.selections();
Selection& main_selection = selections.main();
const BufferCoord anchor = main_selection.anchor();
diff --git a/src/normal.cc b/src/normal.cc
index 0a559b47..4873703a 100644
--- a/src/normal.cc
+++ b/src/normal.cc
@@ -74,6 +74,7 @@ UnitTest test_merge_selection{[] {
template<SelectMode mode, typename T>
void select(Context& context, T func)
{
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
if (mode == SelectMode::Append)
{
@@ -143,6 +144,7 @@ template<SelectMode mode = SelectMode::Replace>
void select_coord(Context& context, BufferCoord coord)
{
Buffer& buffer = context.buffer();
+ ScopedSelectionEdition selection_edition{context};
SelectionList& selections = context.selections();
coord = buffer.clamp(coord);
if (mode == SelectMode::Replace)
@@ -422,6 +424,7 @@ void replace_with_char(Context& context, NormalParams)
if (not cp or key == Key::Escape)
return;
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
Buffer& buffer = context.buffer();
context.selections().for_each([&](size_t index, Selection& sel) {
CharCount count = char_length(buffer, sel);
@@ -442,6 +445,7 @@ void for_each_codepoint(Context& context, NormalParams)
using Utf8It = utf8::iterator<BufferIterator>;
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
Buffer& buffer = context.buffer();
context.selections().for_each([&](size_t index, Selection& sel) {
@@ -555,7 +559,8 @@ void pipe(Context& context, NormalParams params)
prompt, {}, default_command, context.faces()["Prompt"],
PromptFlags::DropHistoryEntriesWithBlankPrefix, '|',
shell_complete,
- [default_command](StringView cmdline, PromptEvent event, Context& context)
+ [default_command, selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
+ (StringView cmdline, PromptEvent event, Context& context)
{
if (event != PromptEvent::Validate)
return;
@@ -637,6 +642,7 @@ void erase_selections(Context& context, NormalParams params)
RegisterManager::instance()[reg].set(context, context.selections_content());
}
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
context.selections().erase();
}
@@ -683,6 +689,7 @@ void paste(Context& context, NormalParams params)
auto& buffer = context.buffer();
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
context.selections().for_each([&](size_t index, Selection& sel) {
auto& str = strings[std::min(strings.size()-1, index)];
auto& min = sel.min();
@@ -719,6 +726,7 @@ void paste_all(Context& context, NormalParams params)
Buffer& buffer = context.buffer();
Vector<Selection> result;
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
{
ScopedEdition edition(context);
@@ -751,7 +759,8 @@ void insert_output(Context& context, NormalParams params)
prompt, {}, default_command, context.faces()["Prompt"],
PromptFlags::DropHistoryEntriesWithBlankPrefix, '|',
shell_complete,
- [default_command](StringView cmdline, PromptEvent event, Context& context)
+ [default_command, selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
+ (StringView cmdline, PromptEvent event, Context& context)
{
if (event != PromptEvent::Validate)
return;
@@ -827,7 +836,8 @@ void regex_prompt(Context& context, String prompt, char reg, T func)
[&](auto&& m) { candidates.push_back(m.candidate().str()); return true; });
return {(int)(word.begin() - regex.begin()), pos, std::move(candidates) };
},
- [=, func=T(std::move(func))](StringView str, PromptEvent event, Context& context) mutable {
+ [=, func=T(std::move(func)), selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
+ (StringView str, PromptEvent event, Context& context) mutable {
try
{
if (event != PromptEvent::Change and context.has_client())
@@ -947,6 +957,7 @@ void search_next(Context& context, NormalParams params)
if (not str.empty())
{
Regex regex{str, direction_flags(regex_mode)};
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
bool main_wrapped = false;
do {
@@ -1048,6 +1059,7 @@ void split_regex(Context& context, NormalParams params)
void split_lines(Context& context, NormalParams params)
{
const LineCount count{params.count == 0 ? 1 : params.count};
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
auto& buffer = context.buffer();
Vector<Selection> res;
@@ -1074,6 +1086,7 @@ void split_lines(Context& context, NormalParams params)
void select_boundaries(Context& context, NormalParams)
{
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
Vector<Selection> res;
for (auto& sel : selections)
@@ -1089,6 +1102,7 @@ void join_lines_select_spaces(Context& context, NormalParams)
{
auto& buffer = context.buffer();
Vector<Selection> selections;
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
{
const LineCount min_line = sel.min().line;
@@ -1112,6 +1126,7 @@ void join_lines_select_spaces(Context& context, NormalParams)
void join_lines(Context& context, NormalParams params)
{
+ ScopedSelectionEdition selection_edition{context};
SelectionList sels{context.selections()};
auto restore_sels = on_scope_end([&]{
sels.update();
@@ -1129,7 +1144,8 @@ void keep(Context& context, NormalParams params)
const char reg = to_lower(params.reg ? params.reg : '/');
regex_prompt(context, prompt.str(), reg,
- [reg, saved_reg = RegisterManager::instance()[reg].save(context)]
+ [reg, saved_reg = RegisterManager::instance()[reg].save(context),
+ selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
(const Regex& regex, PromptEvent event, Context& context) {
RegisterManager::instance()[reg].restore(context, saved_reg);
if (event == PromptEvent::Abort)
@@ -1166,7 +1182,8 @@ void keep_pipe(Context& context, NormalParams params)
context.input_handler().prompt(
"keep pipe:", {}, default_command, context.faces()["Prompt"],
PromptFlags::DropHistoryEntriesWithBlankPrefix, '|', shell_complete,
- [default_command](StringView cmdline, PromptEvent event, Context& context) {
+ [default_command, selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
+ (StringView cmdline, PromptEvent event, Context& context) {
if (event != PromptEvent::Validate)
return;
@@ -1212,6 +1229,7 @@ void indent(Context& context, NormalParams params)
ScopedEdition edition(context);
auto& buffer = context.buffer();
LineCount last_line = 0;
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
{
for (auto line = std::max(last_line, sel.min().line); line < sel.max().line+1; ++line)
@@ -1236,6 +1254,7 @@ void deindent(Context& context, NormalParams params)
auto& buffer = context.buffer();
LineCount last_line = 0;
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
{
for (auto line = std::max(sel.min().line, last_line);
@@ -1416,6 +1435,7 @@ void scroll(Context& context, NormalParams params)
template<Direction direction>
void copy_selections_on_next_lines(Context& context, NormalParams params)
{
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
auto& buffer = context.buffer();
const ColumnCount tabstop = context.options()["tabstop"].get<int>();
@@ -1469,6 +1489,7 @@ template<Direction direction>
void rotate_selections(Context& context, NormalParams params)
{
const int count = params.count ? params.count : 1;
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
const int index = selections.main_index();
const int num = selections.size();
@@ -1486,6 +1507,7 @@ void rotate_selections_content(Context& context, NormalParams params)
if (group == 0 or group > (int)strings.size())
group = (int)strings.size();
count = count % group;
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
auto main = strings.begin() + selections.main_index();
for (auto it = strings.begin(); it != strings.end(); )
@@ -1570,6 +1592,7 @@ void replay_macro(Context& context, NormalParams params)
auto keys = parse_keys(reg_val[0]);
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
do
{
for (auto& key : keys)
@@ -1587,6 +1610,7 @@ void jump(Context& context, NormalParams params)
Buffer* oldbuf = &context.buffer();
Buffer& buffer = const_cast<Buffer&>(jump.buffer());
+ ScopedSelectionEdition selection_edition{context};
if (&buffer != oldbuf)
context.change_buffer(buffer);
context.selections_write_only() = jump;
@@ -1601,6 +1625,7 @@ void push_selections(Context& context, NormalParams)
void align(Context& context, NormalParams)
{
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
auto& buffer = context.buffer();
const ColumnCount tabstop = context.options()["tabstop"].get<int>();
@@ -1654,6 +1679,7 @@ void copy_indent(Context& context, NormalParams params)
{
int selection = params.count;
auto& buffer = context.buffer();
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
Vector<LineCount> lines;
for (auto sel : selections)
@@ -1694,6 +1720,7 @@ void tabs_to_spaces(Context& context, NormalParams params)
const ColumnCount tabstop = params.count == 0 ? opt_tabstop : params.count;
Vector<Selection> tabs;
Vector<String> spaces;
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
{
for (auto it = buffer.iterator_at(sel.min()),
@@ -1718,6 +1745,7 @@ void spaces_to_tabs(Context& context, NormalParams params)
const ColumnCount opt_tabstop = context.options()["tabstop"].get<int>();
const ColumnCount tabstop = params.count == 0 ? opt_tabstop : params.count;
Vector<Selection> spaces;
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
{
for (auto it = buffer.iterator_at(sel.min()),
@@ -1751,6 +1779,7 @@ void spaces_to_tabs(Context& context, NormalParams params)
void trim_selections(Context& context, NormalParams)
{
auto& buffer = context.buffer();
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
Vector<int> to_remove;
@@ -1874,6 +1903,7 @@ void combine_selections(Context& context, SelectionList list, Func func, StringV
return;
const auto op = key_to_combine_op(key);
+ ScopedSelectionEdition selection_edition{context};
auto& sels = context.selections();
list.update();
if (op == CombineOp::Append)
@@ -1927,7 +1957,10 @@ void save_selections(Context& context, NormalParams params)
};
if (combine and not empty)
+ {
+ ScopedSelectionEdition selection_edition{context};
combine_selections(context, read_selections_from_register(reg, context), save_to_reg, "combine selections to register");
+ }
else
save_to_reg(context, context.selections());
}
@@ -1938,6 +1971,8 @@ void restore_selections(Context& context, NormalParams params)
const char reg = to_lower(params.reg ? params.reg : '^');
auto selections = read_selections_from_register(reg, context);
+ ScopedSelectionEdition selection_edition{context};
+
auto set_selections = [reg](Context& context, SelectionList sels) {
auto size = sels.size();
context.selections_write_only() = std::move(sels);
@@ -2021,6 +2056,7 @@ void exec_user_mappings(Context& context, NormalParams params)
InputHandler::ScopedForceNormal force_normal{context.input_handler(), params};
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
for (auto& key : mapping.keys)
context.input_handler().handle_key(key);
}, "user mapping",
@@ -2033,6 +2069,7 @@ void add_empty_line(Context& context, NormalParams params)
int count = std::max(params.count, 1);
String new_lines{'\n', CharCount{count}};
auto& buffer = context.buffer();
+ ScopedSelectionEdition selection_edition{context};
auto& sels = context.selections();
ScopedEdition edition{context};
for (int i = 0; i < sels.size(); ++i)
@@ -2051,6 +2088,7 @@ public:
void operator() (Context& context, NormalParams params)
{
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
do { m_func(context, {0, params.reg}); } while(--params.count > 0);
}
private:
@@ -2061,6 +2099,7 @@ template<void (*func)(Context&, NormalParams)>
void repeated(Context& context, NormalParams params)
{
ScopedEdition edition(context);
+ ScopedSelectionEdition selection_edition{context};
do { func(context, {0, params.reg}); } while(--params.count > 0);
}
@@ -2070,6 +2109,7 @@ void move_cursor(Context& context, NormalParams params)
kak_assert(mode == SelectMode::Replace or mode == SelectMode::Extend);
const Type offset{direction * std::max(params.count,1)};
const ColumnCount tabstop = context.options()["tabstop"].get<int>();
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
for (auto& sel : selections)
{
@@ -2083,11 +2123,13 @@ void move_cursor(Context& context, NormalParams params)
void select_whole_buffer(Context& context, NormalParams)
{
auto& buffer = context.buffer();
+ ScopedSelectionEdition selection_edition{context};
context.selections_write_only() = SelectionList{buffer, {{0,0}, {buffer.back_coord(), max_column}}};
}
void keep_selection(Context& context, NormalParams p)
{
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
const int index = p.count ? p.count-1 : selections.main_index();
if (index >= selections.size())
@@ -2099,6 +2141,7 @@ void keep_selection(Context& context, NormalParams p)
void remove_selection(Context& context, NormalParams p)
{
+ ScopedSelectionEdition selection_edition{context};
auto& selections = context.selections();
const int index = p.count ? p.count-1 : selections.main_index();
if (index >= selections.size())
@@ -2112,12 +2155,14 @@ void remove_selection(Context& context, NormalParams p)
void clear_selections(Context& context, NormalParams)
{
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
sel.anchor() = sel.cursor();
}
void flip_selections(Context& context, NormalParams)
{
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
{
const BufferCoord tmp = sel.anchor();
@@ -2129,6 +2174,7 @@ void flip_selections(Context& context, NormalParams)
void ensure_forward(Context& context, NormalParams)
{
+ ScopedSelectionEdition selection_edition{context};
for (auto& sel : context.selections())
{
const BufferCoord min = sel.min(), max = sel.max();
@@ -2140,18 +2186,21 @@ void ensure_forward(Context& context, NormalParams)
void merge_consecutive(Context& context, NormalParams params)
{
+ ScopedSelectionEdition selection_edition{context};
ensure_forward(context, params);
context.selections().merge_consecutive();
}
void merge_overlapping(Context& context, NormalParams params)
{
+ ScopedSelectionEdition selection_edition{context};
ensure_forward(context, params);
context.selections().merge_overlapping();
}
void duplicate_selections(Context& context, NormalParams params)
{
+ ScopedSelectionEdition selection_edition{context};
SelectionList& sels = context.selections();
Vector<Selection> new_sels;
const int count = params.count ? params.count : 2;