diff --git a/doc/pages/changelog.asciidoc b/doc/pages/changelog.asciidoc index 2f092421..f60b9ce2 100644 --- a/doc/pages/changelog.asciidoc +++ b/doc/pages/changelog.asciidoc @@ -3,6 +3,11 @@ This changelog contains major and/or breaking changes to Kakoune between released versions. +== Development version + +* `+` only duplicates identical selections a single time to avoid surprising + and slow exponential growth in the number of selections. + == Kakoune 2023.08.08 * Fix compilation errors on FreeBSD and MacOS using clang diff --git a/src/main.cc b/src/main.cc index 2e844d62..db0fb5f4 100644 --- a/src/main.cc +++ b/src/main.cc @@ -45,6 +45,9 @@ struct { unsigned int version; StringView notes; } constexpr version_notes[] = { { + 0, + "» {+b}+{} only duplicates identical selections a single time\n" + }, { 20230805, "» Fix FreeBSD/MacOS clang compilation\n" }, { diff --git a/src/normal.cc b/src/normal.cc index 27c1e47e..b16b9c37 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -2191,9 +2191,17 @@ void duplicate_selections(Context& context, NormalParams params) SelectionList& sels = context.selections(); Vector new_sels; const int count = params.count ? params.count : 2; + BasicSelection last{BufferCoord{-1,-1}}; + size_t index = 0; + size_t main_index = 0; for (const auto& sel : sels) - new_sels.insert(new_sels.end(), count, sel); - context.selections().set(std::move(new_sels), sels.main_index() * count); + { + new_sels.insert(new_sels.end(), sel != last ? count : 1, sel); + last = sel; + if (index++ == sels.main_index()) + main_index = new_sels.size() - 1; + } + context.selections().set(std::move(new_sels), main_index); } void force_redraw(Context& context, NormalParams) diff --git a/src/selection.hh b/src/selection.hh index bdfd7d41..7794e093 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -13,17 +13,14 @@ using CaptureList = Vector; constexpr ColumnCount max_column{std::numeric_limits::max()}; constexpr ColumnCount max_non_eol_column{max_column-1}; -// A selection is a Selection, associated with a CaptureList -struct Selection +struct BasicSelection { static constexpr MemoryDomain Domain = MemoryDomain::Selections; - Selection() = default; - Selection(BufferCoord pos) : Selection(pos,pos) {} - Selection(BufferCoord anchor, BufferCoordAndTarget cursor, - CaptureList captures = {}) - : m_anchor{anchor}, m_cursor{cursor}, - m_captures(std::move(captures)) {} + BasicSelection() = default; + BasicSelection(BufferCoord pos) : BasicSelection(pos,pos) {} + BasicSelection(BufferCoord anchor, BufferCoordAndTarget cursor) + : m_anchor{anchor}, m_cursor{cursor} {} BufferCoord& anchor() { return m_anchor; } BufferCoordAndTarget& cursor() { return m_cursor; } @@ -39,13 +36,7 @@ struct Selection void set(BufferCoord coord) { set(coord, coord); } - CaptureList& captures() { return m_captures; } - const CaptureList& captures() const { return m_captures; } - - bool operator== (const Selection& other) const - { - return m_anchor == other.m_anchor and m_cursor == other.m_cursor; - } + friend bool operator==(const BasicSelection&, const BasicSelection&) = default; // When selections are single char, we want the anchor to be considered min, and cursor max const BufferCoord& min() const { return m_anchor <= m_cursor ? m_anchor : m_cursor; } @@ -57,11 +48,23 @@ struct Selection private: BufferCoord m_anchor; BufferCoordAndTarget m_cursor; +}; +; +struct Selection : BasicSelection +{ + Selection() = default; + Selection(BufferCoord pos) : BasicSelection(pos,pos) {} + Selection(BufferCoord anchor, BufferCoordAndTarget cursor, CaptureList captures = {}) + : BasicSelection{anchor, cursor}, m_captures(std::move(captures)) {} + CaptureList& captures() { return m_captures; } + const CaptureList& captures() const { return m_captures; } + +private: CaptureList m_captures; }; -inline bool overlaps(const Selection& lhs, const Selection& rhs) +inline bool overlaps(const BasicSelection& lhs, const BasicSelection& rhs) { return lhs.min() <= rhs.min() ? lhs.max() >= rhs.min() : lhs.min() <= rhs.max(); diff --git a/test/normal/duplicate-selections/cmd b/test/normal/duplicate-selections/cmd new file mode 100644 index 00000000..d7a3afef --- /dev/null +++ b/test/normal/duplicate-selections/cmd @@ -0,0 +1 @@ +++ao diff --git a/test/normal/duplicate-selections/in b/test/normal/duplicate-selections/in new file mode 100644 index 00000000..e9185121 --- /dev/null +++ b/test/normal/duplicate-selections/in @@ -0,0 +1 @@ +%(f) %(b) %(t) diff --git a/test/normal/duplicate-selections/out b/test/normal/duplicate-selections/out new file mode 100644 index 00000000..d3c81e41 --- /dev/null +++ b/test/normal/duplicate-selections/out @@ -0,0 +1 @@ +fooo booo tooo