diff --git a/README.asciidoc b/README.asciidoc index f33fa2ac..bb158dbf 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -339,6 +339,7 @@ object you want. * _W_: select the whole WORD * _s_: select the sentence * _p_: select the paragraph + * _␣_: select the whitespaces * _i_: select the current indentation block * _n_: select the number diff --git a/src/highlighters.cc b/src/highlighters.cc index 9738bc12..a6092ed4 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -547,6 +547,11 @@ void highlight_selections(const Context& context, HighlightFlags flags, DisplayB ColorPair sel_colors = get_color(primary ? "PrimarySelection" : "SecondarySelection"); highlight_range(display_buffer, begin, end, false, [&](DisplayAtom& atom) { atom.colors = sel_colors; }); + } + for (size_t i = 0; i < context.selections().size(); ++i) + { + auto& sel = context.selections()[i]; + const bool primary = (i == context.selections().main_index()); ColorPair cur_colors = get_color(primary ? "PrimaryCursor" : "SecondaryCursor"); highlight_range(display_buffer, sel.cursor(), buffer.char_next(sel.cursor()), false, [&](DisplayAtom& atom) { atom.colors = cur_colors; }); diff --git a/src/input_handler.cc b/src/input_handler.cc index d96a8f10..b5b6a847 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -842,7 +842,8 @@ private: kak_assert(false); // invalid for interactive insert break; } - selections.sort_and_merge_overlapping(); + if (mode != InsertMode::Append) + selections.sort_and_merge_overlapping(); selections.check_invariant(); buffer.check_invariant(); } diff --git a/src/ncurses.cc b/src/ncurses.cc index 78e96746..539e8fca 100644 --- a/src/ncurses.cc +++ b/src/ncurses.cc @@ -211,7 +211,7 @@ using Utf8Policy = utf8::InvalidBytePolicy::Pass; using Utf8Iterator = utf8::utf8_iterator; void addutf8str(WINDOW* win, Utf8Iterator begin, Utf8Iterator end) { - waddstr(win, std::string(begin.base(), end.base()).c_str()); + waddstr(win, StringView(begin.base(), end.base()).zstr()); } static CharCoord window_size(WINDOW* win) diff --git a/src/normal.cc b/src/normal.cc index 0e4498c5..be670397 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -839,6 +839,7 @@ void select_object(Context& context, int param) { 'W', select_word }, { 's', select_sentence }, { 'p', select_paragraph }, + { ' ', select_whitespaces }, { 'i', select_indent }, { 'n', select_number }, }; @@ -880,6 +881,7 @@ void select_object(Context& context, int param) "W: WORD \n" "s: sentence \n" "p: paragraph \n" + "␣: whitespaces \n" "i: indent \n"); } @@ -1105,23 +1107,24 @@ void tabs_to_spaces(Context& context, int ts) auto& buffer = context.buffer(); const CharCount opt_tabstop = context.options()["tabstop"].get(); const CharCount tabstop = ts == 0 ? opt_tabstop : ts; + std::vector tabs; + std::vector spaces; for (auto& sel : context.selections()) { for (auto it = buffer.iterator_at(sel.min()), - end = buffer.iterator_at(sel.max())+1; it != end;) + end = buffer.iterator_at(sel.max())+1; it != end; ++it) { if (*it == '\t') { CharCount col = get_column(buffer, opt_tabstop, it.coord()); CharCount end_col = (col / tabstop + 1) * tabstop; - it = buffer.erase(it, it+1); - it = buffer.insert(it, String{ ' ', end_col - col }) + (int)(end_col - col); - end = buffer.iterator_at(sel.max())+1; + tabs.push_back({ it.coord() }); + spaces.push_back(String{ ' ', end_col - col }); } - else - ++it; } } + if (not tabs.empty()) + SelectionList{ buffer, std::move(tabs) }.insert(spaces, InsertMode::Replace); } void spaces_to_tabs(Context& context, int ts) @@ -1129,6 +1132,7 @@ void spaces_to_tabs(Context& context, int ts) auto& buffer = context.buffer(); const CharCount opt_tabstop = context.options()["tabstop"].get(); const CharCount tabstop = ts == 0 ? opt_tabstop : ts; + std::vector spaces; for (auto& sel : context.selections()) { for (auto it = buffer.iterator_at(sel.min()), @@ -1144,20 +1148,16 @@ void spaces_to_tabs(Context& context, int ts) ++spaces_end; ++col; } - if ((col % tabstop) == 0) - { - it = buffer.erase(spaces_beg, spaces_end); - it = buffer.insert(it, "\t") + 1; - end = buffer.iterator_at(sel.max())+1; - } - else - it = spaces_end; + spaces.push_back({spaces_beg.coord(), (spaces_end-1).coord()}); + it = spaces_end; } else ++it; } } + if (not spaces.empty()) + SelectionList{ buffer, std::move(spaces) }.insert("\t"_str, InsertMode::Replace); } void undo(Context& context, int) diff --git a/src/selectors.cc b/src/selectors.cc index 78200ba0..838f1de1 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -375,6 +375,35 @@ static CharCount get_indent(const String& str, int tabstop) return indent; } +Selection select_whitespaces(const Buffer& buffer, const Selection& selection, ObjectFlags flags) +{ + auto is_whitespace = [&](char c) { + return c == ' ' or c == '\t' or + (not (flags & ObjectFlags::Inner) and c == '\n'); + }; + BufferIterator first = buffer.iterator_at(selection.cursor()); + BufferIterator last = first; + if (flags & ObjectFlags::ToBegin) + { + if (is_whitespace(*first)) + { + skip_while_reverse(first, buffer.begin(), is_whitespace); + if (not is_whitespace(*first)) + ++first; + } + } + if (flags & ObjectFlags::ToEnd) + { + if (is_whitespace(*last)) + { + skip_while(last, buffer.end(), is_whitespace); + --last; + } + } + return (flags & ObjectFlags::ToEnd) ? utf8_range(first, last) + : utf8_range(last, first); +} + static bool is_only_whitespaces(const String& str) { auto it = str.begin(); diff --git a/src/selectors.hh b/src/selectors.hh index 7e86a738..d9e5f770 100644 --- a/src/selectors.hh +++ b/src/selectors.hh @@ -212,6 +212,10 @@ Selection select_paragraph(const Buffer& buffer, const Selection& selection, ObjectFlags flags); +Selection select_whitespaces(const Buffer& buffer, + const Selection& selection, + ObjectFlags flags); + Selection select_indent(const Buffer& buffer, const Selection& selection, ObjectFlags flags);