From 2856b99e0914cc7a659977f2b33308cb5b4c9bb7 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Fri, 20 Dec 2024 22:09:32 +1100 Subject: WIP linked list shared strings --- src/buffer.cc | 21 +++++++++++++++++++ src/buffer.hh | 13 ++++++------ src/buffer.inl.hh | 59 +++++++++++++++++++++++++++++----------------------- src/shared_string.hh | 2 ++ 4 files changed, 62 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/buffer.cc b/src/buffer.cc index f0205c54..0b943207 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -20,6 +20,23 @@ namespace Kakoune { +void fix_links(BufferLines& lines, StringData* sentinel) +{ + StringData* prev = nullptr; + for (auto& ptr : lines) + { + ptr->prev = prev; + if (prev) + prev->next = ptr.get(); + prev = ptr.get(); + } + if (prev) + { + prev->next = sentinel; + sentinel->prev = prev; + } +} + Buffer::HistoryNode::HistoryNode(HistoryId parent) : parent{parent}, committed{Clock::now()} {} @@ -42,6 +59,7 @@ Buffer::Buffer(String name, Flags flags, BufferLines lines, line->data()[line->length-1] == '\n'); #endif static_cast(m_lines) = std::move(lines); + fix_links(m_lines, m_sentinel.get()); m_changes.push_back({ Change::Insert, {0,0}, line_count() }); @@ -271,6 +289,7 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F m_lines.erase(write_it, m_lines.end()); } + fix_links(m_lines, m_sentinel.get()); commit_undo_group(); options().get_local_option("eolformat").set(eolformat); @@ -452,6 +471,7 @@ BufferRange Buffer::do_insert(BufferCoord pos, StringView content) const auto end = at_end ? line_count() : BufferCoord{ last_line, m_lines[last_line].length() - suffix.length() }; + fix_links(m_lines, m_sentinel.get()); m_changes.push_back({ Change::Insert, pos, end }); return {pos, end}; } @@ -473,6 +493,7 @@ BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end) if (new_line) m_lines.get_storage(begin.line) = std::move(new_line); + fix_links(m_lines, m_sentinel.get()); return begin; } diff --git a/src/buffer.hh b/src/buffer.hh index 50423502..df899f44 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -66,17 +66,17 @@ public: BufferIterator() = default; BufferIterator(const Buffer& buffer, BufferCoord coord) noexcept; - BufferIterator(const StringDataPtr* lines, LineCount line_count, BufferCoord coord) noexcept; + BufferIterator(const StringData* line, BufferCoord coord) noexcept; bool operator== (const BufferIterator& iterator) const noexcept; auto operator<=>(const BufferIterator& iterator) const noexcept; bool operator== (const BufferCoord& coord) const noexcept; - const char& operator* () const noexcept; + const char& operator*() const noexcept; const char& operator[](size_t n) const noexcept; size_t operator- (const BufferIterator& iterator) const; - explicit operator bool() const { return m_lines; } + explicit operator bool() const { return m_line; } BufferIterator operator+ (ByteCount size) const; BufferIterator operator- (ByteCount size) const; @@ -95,9 +95,7 @@ public: using Sentinel = BufferCoord; private: - const StringDataPtr* m_lines; - [[no_unique_address]] StringView m_line; - [[no_unique_address]] LineCount m_line_count; + const StringData* m_line; BufferCoord m_coord; }; @@ -188,7 +186,7 @@ public: { return m_lines[line]; } const StringDataPtr& line_storage(LineCount line) const - { return m_lines.get_storage(line); } + { return line == line_count() ? m_sentinel : m_lines.get_storage(line); } // returns an iterator at given coordinates. clamp line_and_column BufferIterator iterator_at(BufferCoord coord) const; @@ -289,6 +287,7 @@ private: StringView back() const { return BufferLines::back()->strview(); } }; LineList m_lines; + StringDataPtr m_sentinel = StringData::create(""); String m_name; String m_display_name; diff --git a/src/buffer.inl.hh b/src/buffer.inl.hh index 890a5cbd..e55cae05 100644 --- a/src/buffer.inl.hh +++ b/src/buffer.inl.hh @@ -60,7 +60,7 @@ inline BufferIterator Buffer::begin() const inline BufferIterator Buffer::end() const { - return {*this, end_coord()}; + return {m_sentinel.get(), end_coord()}; } [[gnu::always_inline]] @@ -99,23 +99,19 @@ inline BufferCoord Buffer::end_coord() const } inline BufferIterator::BufferIterator(const Buffer& buffer, BufferCoord coord) noexcept - : BufferIterator{buffer.m_lines.data(), buffer.line_count(), coord} {} + : BufferIterator{buffer.line_storage(coord.line).get(), coord} {} -inline BufferIterator::BufferIterator(const StringDataPtr* lines, LineCount line_count, BufferCoord coord) noexcept - : m_lines{lines}, - m_line{coord.line < line_count ? m_lines[(size_t)coord.line]->strview() : StringView{}}, - m_line_count{line_count}, +inline BufferIterator::BufferIterator(const StringData* line, BufferCoord coord) noexcept + : m_line{line}, m_coord{coord} {} inline bool BufferIterator::operator==(const BufferIterator& iterator) const noexcept { - kak_assert(m_lines == iterator.m_lines); return m_coord == iterator.m_coord; } inline auto BufferIterator::operator<=>(const BufferIterator& iterator) const noexcept { - kak_assert(m_lines == iterator.m_lines); return (m_coord <=> iterator.m_coord); } @@ -127,52 +123,62 @@ inline bool BufferIterator::operator==(const BufferCoord& coord) const noexcept [[gnu::always_inline]] inline const char& BufferIterator::operator*() const noexcept { - return m_line[m_coord.column]; + return m_line->data()[(size_t)(int)m_coord.column]; +} + +inline BufferIterator advance(BufferIterator iterator, int n) +{ + for (; n > 0; --n) + ++iterator; + for (; n < 0; ++n) + --iterator; + return iterator; } inline const char& BufferIterator::operator[](size_t n) const noexcept { - auto coord = Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, n); - return m_lines[(size_t)coord.line]->strview()[coord.column]; + return *advance(*this, n); } inline size_t BufferIterator::operator-(const BufferIterator& iterator) const { - kak_assert(m_lines == iterator.m_lines); - return (size_t)Buffer::distance({m_lines, (size_t)(int)m_line_count}, iterator.m_coord, m_coord); + size_t distance = 0; + auto* line = iterator.m_line; + while (line != m_line) + { + distance += line->length - (line == iterator.m_line ? (int)iterator.m_coord.column : 0); + } + distance += (int)(m_coord.column - (line == iterator.m_line ? (int)iterator.m_coord.column : 0)); + return distance; } inline BufferIterator BufferIterator::operator+(ByteCount size) const { kak_assert(*this); - return { m_lines, m_line_count, Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, size) }; + return advance(*this, (int)size); } inline BufferIterator BufferIterator::operator-(ByteCount size) const { - return { m_lines, m_line_count, Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, -size) }; + return advance(*this, -(int)size); } inline BufferIterator& BufferIterator::operator+=(ByteCount size) { - m_coord = Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, size); - m_line = m_lines[(size_t)m_coord.line]->strview(); - return *this; + return *this = advance(*this, (int)size); } inline BufferIterator& BufferIterator::operator-=(ByteCount size) { - m_coord = Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, -size); - m_line = m_lines[(size_t)m_coord.line]->strview(); - return *this; + return *this = advance(*this, -(int)size); } inline BufferIterator& BufferIterator::operator++() { - if (++m_coord.column == m_line.length()) + if (++m_coord.column == m_line->length) { - m_line = ((size_t)++m_coord.line < m_line_count) ? - m_lines[(size_t)m_coord.line]->strview() : StringView{}; + m_line = m_line->next; + ++m_coord.line; m_coord.column = 0; } return *this; @@ -182,8 +188,9 @@ inline BufferIterator& BufferIterator::operator--() { if (m_coord.column == 0) { - m_line = m_lines[(size_t)--m_coord.line]->strview(); - m_coord.column = m_line.length() - 1; + m_line = m_line->prev; + --m_coord.line; + m_coord.column = m_line->length - 1; } else --m_coord.column; diff --git a/src/shared_string.hh b/src/shared_string.hh index 79c34830..2d39971a 100644 --- a/src/shared_string.hh +++ b/src/shared_string.hh @@ -15,6 +15,8 @@ struct StringData : UseMemoryDomain { uint32_t refcount; const int length; + StringData* prev = nullptr; + StringData* next = nullptr; [[gnu::always_inline]] const char* data() const { return reinterpret_cast(this + 1); } -- cgit v1.2.3 From d92496449d0c9655253ad16363685bb8446dc582 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Thu, 5 Dec 2024 22:48:09 +1100 Subject: Use uint64_t for regex step Its unclear that maintaining a small instruction size outweigh the cost of handling wrapping of the current_step every 64K codepoints, this makes the code simpler. --- src/regex_impl.cc | 2 +- src/regex_impl.hh | 33 +++++++++------------------------ 2 files changed, 10 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/regex_impl.cc b/src/regex_impl.cc index 0789ef6d..6ac9b4f2 100644 --- a/src/regex_impl.cc +++ b/src/regex_impl.cc @@ -867,7 +867,7 @@ private: const auto res = op_count(); if (res >= max_instructions) throw regex_error(format("regex compiled to more than {} instructions", max_instructions)); - m_program.instructions.push_back({ op, 0, param }); + m_program.instructions.push_back({ op, param, 0 }); return OpIndex(res); } diff --git a/src/regex_impl.hh b/src/regex_impl.hh index b04d99e7..8dc1bc10 100644 --- a/src/regex_impl.hh +++ b/src/regex_impl.hh @@ -130,12 +130,9 @@ struct CompiledRegex : UseMemoryDomain struct Instruction { Op op; - mutable uint16_t last_step; // mutable as used during execution Param param; + mutable uint64_t last_step; // mutable as used during execution }; -#ifndef __ppc__ - static_assert(sizeof(Instruction) == 8); -#endif explicit operator bool() const { return not instructions.empty(); } @@ -358,7 +355,7 @@ private: // Steps a thread until it consumes the current character, matches or fail [[gnu::always_inline]] - void step_current_thread(const Iterator& pos, Codepoint cp, uint16_t current_step, const ExecConfig& config) + void step_current_thread(const Iterator& pos, Codepoint cp, uint64_t current_step, const ExecConfig& config) { Thread thread = m_threads.pop_current(); auto failed = [this, &thread]() { @@ -482,23 +479,17 @@ private: const auto insts = forward ? ArrayView(m_program.instructions).subrange(0, m_program.first_backward_inst) : ArrayView(m_program.instructions).subrange(m_program.first_backward_inst); + for (auto& inst : insts) + inst.last_step = 0; m_threads.push_current({insts.begin(), -1}); - uint16_t current_step = -1; - uint8_t idle_count = 0; // Run idle loop every 256 * 65536 == 16M codepoints + constexpr uint64_t idle_frequency = 256 * 65536; + uint64_t current_step = 0; Iterator pos = next_start; while (pos != config.end) { - if (++current_step == 0) - { - if (++idle_count == 0) - idle_func(); - - // We wrapped, avoid potential collision on inst.last_step by resetting them - for (auto& inst : insts) - inst.last_step = 0; - current_step = 1; // step 0 is never valid - } + if ((++current_step % idle_frequency) == 0) + idle_func(); auto next = pos; Codepoint cp = codepoint(next, config); @@ -525,14 +516,8 @@ private: m_threads.swap_next(); } - if (++current_step == 0) - { - for (auto& inst : insts) - inst.last_step = 0; - current_step = 1; // step 0 is never valid - } while (not m_threads.current_is_empty()) - step_current_thread(pos, -1, current_step, config); + step_current_thread(pos, -1, -1, config); } static Iterator find_next_start(const Iterator& start, const Sentinel& end, const StartDesc& start_desc) -- cgit v1.2.3 From 8eb753adfd914b0deade48c7510e30f7e05df860 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 4 Feb 2025 19:21:43 +1100 Subject: Revert "Use uint64_t for regex step" This got pushed by accident This reverts commit d92496449d0c9655253ad16363685bb8446dc582. --- src/regex_impl.cc | 2 +- src/regex_impl.hh | 33 ++++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/regex_impl.cc b/src/regex_impl.cc index 6ac9b4f2..0789ef6d 100644 --- a/src/regex_impl.cc +++ b/src/regex_impl.cc @@ -867,7 +867,7 @@ private: const auto res = op_count(); if (res >= max_instructions) throw regex_error(format("regex compiled to more than {} instructions", max_instructions)); - m_program.instructions.push_back({ op, param, 0 }); + m_program.instructions.push_back({ op, 0, param }); return OpIndex(res); } diff --git a/src/regex_impl.hh b/src/regex_impl.hh index 8dc1bc10..b04d99e7 100644 --- a/src/regex_impl.hh +++ b/src/regex_impl.hh @@ -130,9 +130,12 @@ struct CompiledRegex : UseMemoryDomain struct Instruction { Op op; + mutable uint16_t last_step; // mutable as used during execution Param param; - mutable uint64_t last_step; // mutable as used during execution }; +#ifndef __ppc__ + static_assert(sizeof(Instruction) == 8); +#endif explicit operator bool() const { return not instructions.empty(); } @@ -355,7 +358,7 @@ private: // Steps a thread until it consumes the current character, matches or fail [[gnu::always_inline]] - void step_current_thread(const Iterator& pos, Codepoint cp, uint64_t current_step, const ExecConfig& config) + void step_current_thread(const Iterator& pos, Codepoint cp, uint16_t current_step, const ExecConfig& config) { Thread thread = m_threads.pop_current(); auto failed = [this, &thread]() { @@ -479,17 +482,23 @@ private: const auto insts = forward ? ArrayView(m_program.instructions).subrange(0, m_program.first_backward_inst) : ArrayView(m_program.instructions).subrange(m_program.first_backward_inst); - for (auto& inst : insts) - inst.last_step = 0; m_threads.push_current({insts.begin(), -1}); - constexpr uint64_t idle_frequency = 256 * 65536; - uint64_t current_step = 0; + uint16_t current_step = -1; + uint8_t idle_count = 0; // Run idle loop every 256 * 65536 == 16M codepoints Iterator pos = next_start; while (pos != config.end) { - if ((++current_step % idle_frequency) == 0) - idle_func(); + if (++current_step == 0) + { + if (++idle_count == 0) + idle_func(); + + // We wrapped, avoid potential collision on inst.last_step by resetting them + for (auto& inst : insts) + inst.last_step = 0; + current_step = 1; // step 0 is never valid + } auto next = pos; Codepoint cp = codepoint(next, config); @@ -516,8 +525,14 @@ private: m_threads.swap_next(); } + if (++current_step == 0) + { + for (auto& inst : insts) + inst.last_step = 0; + current_step = 1; // step 0 is never valid + } while (not m_threads.current_is_empty()) - step_current_thread(pos, -1, -1, config); + step_current_thread(pos, -1, current_step, config); } static Iterator find_next_start(const Iterator& start, const Sentinel& end, const StartDesc& start_desc) -- cgit v1.2.3 From 3ff33830627438de3f9638c18f7e192cdf5b32a5 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 4 Feb 2025 19:22:03 +1100 Subject: Revert "WIP linked list shared strings" This got pushed by accident This reverts commit 2856b99e0914cc7a659977f2b33308cb5b4c9bb7. --- src/buffer.cc | 21 ------------------- src/buffer.hh | 13 ++++++------ src/buffer.inl.hh | 59 +++++++++++++++++++++++----------------------------- src/shared_string.hh | 2 -- 4 files changed, 33 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/buffer.cc b/src/buffer.cc index 0b943207..f0205c54 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -20,23 +20,6 @@ namespace Kakoune { -void fix_links(BufferLines& lines, StringData* sentinel) -{ - StringData* prev = nullptr; - for (auto& ptr : lines) - { - ptr->prev = prev; - if (prev) - prev->next = ptr.get(); - prev = ptr.get(); - } - if (prev) - { - prev->next = sentinel; - sentinel->prev = prev; - } -} - Buffer::HistoryNode::HistoryNode(HistoryId parent) : parent{parent}, committed{Clock::now()} {} @@ -59,7 +42,6 @@ Buffer::Buffer(String name, Flags flags, BufferLines lines, line->data()[line->length-1] == '\n'); #endif static_cast(m_lines) = std::move(lines); - fix_links(m_lines, m_sentinel.get()); m_changes.push_back({ Change::Insert, {0,0}, line_count() }); @@ -289,7 +271,6 @@ void Buffer::reload(BufferLines lines, ByteOrderMark bom, EolFormat eolformat, F m_lines.erase(write_it, m_lines.end()); } - fix_links(m_lines, m_sentinel.get()); commit_undo_group(); options().get_local_option("eolformat").set(eolformat); @@ -471,7 +452,6 @@ BufferRange Buffer::do_insert(BufferCoord pos, StringView content) const auto end = at_end ? line_count() : BufferCoord{ last_line, m_lines[last_line].length() - suffix.length() }; - fix_links(m_lines, m_sentinel.get()); m_changes.push_back({ Change::Insert, pos, end }); return {pos, end}; } @@ -493,7 +473,6 @@ BufferCoord Buffer::do_erase(BufferCoord begin, BufferCoord end) if (new_line) m_lines.get_storage(begin.line) = std::move(new_line); - fix_links(m_lines, m_sentinel.get()); return begin; } diff --git a/src/buffer.hh b/src/buffer.hh index df899f44..50423502 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -66,17 +66,17 @@ public: BufferIterator() = default; BufferIterator(const Buffer& buffer, BufferCoord coord) noexcept; - BufferIterator(const StringData* line, BufferCoord coord) noexcept; + BufferIterator(const StringDataPtr* lines, LineCount line_count, BufferCoord coord) noexcept; bool operator== (const BufferIterator& iterator) const noexcept; auto operator<=>(const BufferIterator& iterator) const noexcept; bool operator== (const BufferCoord& coord) const noexcept; - const char& operator*() const noexcept; + const char& operator* () const noexcept; const char& operator[](size_t n) const noexcept; size_t operator- (const BufferIterator& iterator) const; - explicit operator bool() const { return m_line; } + explicit operator bool() const { return m_lines; } BufferIterator operator+ (ByteCount size) const; BufferIterator operator- (ByteCount size) const; @@ -95,7 +95,9 @@ public: using Sentinel = BufferCoord; private: - const StringData* m_line; + const StringDataPtr* m_lines; + [[no_unique_address]] StringView m_line; + [[no_unique_address]] LineCount m_line_count; BufferCoord m_coord; }; @@ -186,7 +188,7 @@ public: { return m_lines[line]; } const StringDataPtr& line_storage(LineCount line) const - { return line == line_count() ? m_sentinel : m_lines.get_storage(line); } + { return m_lines.get_storage(line); } // returns an iterator at given coordinates. clamp line_and_column BufferIterator iterator_at(BufferCoord coord) const; @@ -287,7 +289,6 @@ private: StringView back() const { return BufferLines::back()->strview(); } }; LineList m_lines; - StringDataPtr m_sentinel = StringData::create(""); String m_name; String m_display_name; diff --git a/src/buffer.inl.hh b/src/buffer.inl.hh index e55cae05..890a5cbd 100644 --- a/src/buffer.inl.hh +++ b/src/buffer.inl.hh @@ -60,7 +60,7 @@ inline BufferIterator Buffer::begin() const inline BufferIterator Buffer::end() const { - return {m_sentinel.get(), end_coord()}; + return {*this, end_coord()}; } [[gnu::always_inline]] @@ -99,19 +99,23 @@ inline BufferCoord Buffer::end_coord() const } inline BufferIterator::BufferIterator(const Buffer& buffer, BufferCoord coord) noexcept - : BufferIterator{buffer.line_storage(coord.line).get(), coord} {} + : BufferIterator{buffer.m_lines.data(), buffer.line_count(), coord} {} -inline BufferIterator::BufferIterator(const StringData* line, BufferCoord coord) noexcept - : m_line{line}, +inline BufferIterator::BufferIterator(const StringDataPtr* lines, LineCount line_count, BufferCoord coord) noexcept + : m_lines{lines}, + m_line{coord.line < line_count ? m_lines[(size_t)coord.line]->strview() : StringView{}}, + m_line_count{line_count}, m_coord{coord} {} inline bool BufferIterator::operator==(const BufferIterator& iterator) const noexcept { + kak_assert(m_lines == iterator.m_lines); return m_coord == iterator.m_coord; } inline auto BufferIterator::operator<=>(const BufferIterator& iterator) const noexcept { + kak_assert(m_lines == iterator.m_lines); return (m_coord <=> iterator.m_coord); } @@ -123,62 +127,52 @@ inline bool BufferIterator::operator==(const BufferCoord& coord) const noexcept [[gnu::always_inline]] inline const char& BufferIterator::operator*() const noexcept { - return m_line->data()[(size_t)(int)m_coord.column]; -} - -inline BufferIterator advance(BufferIterator iterator, int n) -{ - for (; n > 0; --n) - ++iterator; - for (; n < 0; ++n) - --iterator; - return iterator; + return m_line[m_coord.column]; } inline const char& BufferIterator::operator[](size_t n) const noexcept { - return *advance(*this, n); + auto coord = Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, n); + return m_lines[(size_t)coord.line]->strview()[coord.column]; } inline size_t BufferIterator::operator-(const BufferIterator& iterator) const { - size_t distance = 0; - auto* line = iterator.m_line; - while (line != m_line) - { - distance += line->length - (line == iterator.m_line ? (int)iterator.m_coord.column : 0); - } - distance += (int)(m_coord.column - (line == iterator.m_line ? (int)iterator.m_coord.column : 0)); - return distance; + kak_assert(m_lines == iterator.m_lines); + return (size_t)Buffer::distance({m_lines, (size_t)(int)m_line_count}, iterator.m_coord, m_coord); } inline BufferIterator BufferIterator::operator+(ByteCount size) const { kak_assert(*this); - return advance(*this, (int)size); + return { m_lines, m_line_count, Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, size) }; } inline BufferIterator BufferIterator::operator-(ByteCount size) const { - return advance(*this, -(int)size); + return { m_lines, m_line_count, Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, -size) }; } inline BufferIterator& BufferIterator::operator+=(ByteCount size) { - return *this = advance(*this, (int)size); + m_coord = Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, size); + m_line = m_lines[(size_t)m_coord.line]->strview(); + return *this; } inline BufferIterator& BufferIterator::operator-=(ByteCount size) { - return *this = advance(*this, -(int)size); + m_coord = Buffer::advance({m_lines, (size_t)(int)m_line_count}, m_coord, -size); + m_line = m_lines[(size_t)m_coord.line]->strview(); + return *this; } inline BufferIterator& BufferIterator::operator++() { - if (++m_coord.column == m_line->length) + if (++m_coord.column == m_line.length()) { - m_line = m_line->next; - ++m_coord.line; + m_line = ((size_t)++m_coord.line < m_line_count) ? + m_lines[(size_t)m_coord.line]->strview() : StringView{}; m_coord.column = 0; } return *this; @@ -188,9 +182,8 @@ inline BufferIterator& BufferIterator::operator--() { if (m_coord.column == 0) { - m_line = m_line->prev; - --m_coord.line; - m_coord.column = m_line->length - 1; + m_line = m_lines[(size_t)--m_coord.line]->strview(); + m_coord.column = m_line.length() - 1; } else --m_coord.column; diff --git a/src/shared_string.hh b/src/shared_string.hh index 2d39971a..79c34830 100644 --- a/src/shared_string.hh +++ b/src/shared_string.hh @@ -15,8 +15,6 @@ struct StringData : UseMemoryDomain { uint32_t refcount; const int length; - StringData* prev = nullptr; - StringData* next = nullptr; [[gnu::always_inline]] const char* data() const { return reinterpret_cast(this + 1); } -- cgit v1.2.3 From a6b12ffd1e0c345d4d73462d855c4822b4cf5234 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Wed, 5 Feb 2025 12:55:33 +1100 Subject: Make Control modifier quote inserted registers in prompt mode It is often usefull to quote the inserted register, like when doing `:grep /`, or when pulling selected filenames with ``. --- src/input_handler.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/input_handler.cc b/src/input_handler.cc index 0f2c8987..ea803217 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -764,15 +764,17 @@ public: on_next_key_with_autoinfo(context(), "register", KeymapMode::None, [this](Key key, Context&) { const bool joined = (bool)(key.modifiers & Key::Modifiers::Alt); - key.modifiers &= ~Key::Modifiers::Alt; + const bool quoted = (bool)(key.modifiers & Key::Modifiers::Control); + key.modifiers &= ~(Key::Modifiers::Alt | Key::Modifiers::Control); auto cp = key.codepoint(); if (not cp or key == Key::Escape) return; + auto* quoter = Kakoune::quoter(quoted ? Quoting::Kakoune : Quoting::Raw); m_line_editor.insert( - joined ? join(RegisterManager::instance()[*cp].get(context()), ' ', false) - : context().main_sel_register_value(String{*cp})); + joined ? join(RegisterManager::instance()[*cp].get(context()) | transform(quoter), ' ', false) + : quoter(context().main_sel_register_value(String{*cp}))); display(); m_line_changed = true; -- cgit v1.2.3 From 22b461c3a0b22dd4501943230f0774c34f0b4b35 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 10 Feb 2025 11:58:23 +1100 Subject: WIP history register --- src/commands.cc | 5 ++++- src/main.cc | 3 ++- src/register_manager.cc | 17 +++++++++++------ src/register_manager.hh | 7 ++++--- 4 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/commands.cc b/src/commands.cc index 0f892004..086097ec 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -2269,6 +2269,7 @@ const CommandDesc prompt_cmd = { { "shell-completion", { {}, "use shell command completion for prompt" } }, { "shell-script-completion", { ArgCompleter{}, "use shell command completion for prompt" } }, { "shell-script-candidates", { ArgCompleter{}, "use shell command completion for prompt" } }, + { "history-register", { ArgCompleter{}, "register to read and store history to, default to '_'"} }, { "on-change", { ArgCompleter{}, "command to execute whenever the prompt changes" } }, { "on-abort", { ArgCompleter{}, "command to execute whenever the prompt is canceled" } } }, ParameterDesc::Flags::None, 2, 2 @@ -2288,9 +2289,11 @@ const CommandDesc prompt_cmd = { const auto flags = parser.get_switch("password") ? PromptFlags::Password : PromptFlags::None; + auto history_register = RegisterManager::parse_register_name(parser.get_switch("history-register").value_or("_")); + context.input_handler().prompt( parser[0], initstr.str(), {}, context.faces()["Prompt"], - flags, '_', std::move(completer), + flags, history_register, std::move(completer), [command, on_change = parser.get_switch("on-change").value_or("").str(), on_abort = parser.get_switch("on-abort").value_or("").str(), diff --git a/src/main.cc b/src/main.cc index ac9af4ca..9f479768 100644 --- a/src/main.cc +++ b/src/main.cc @@ -49,6 +49,7 @@ struct { "» kak_* appearing in shell arguments will be added to the environment\n" "» {+U}double underline{} support\n" "» {+u}git apply{} can stage/revert selected changes to current buffer\n" + "» {+u}prompt -history-register{} switch\n" }, { 20240518, "» Fix tests failing on some platforms\n" @@ -606,7 +607,7 @@ void register_options() " terminal_info_max_width int\n", UserInterface::Options{}); reg.declare_option("modelinefmt", "format string used to generate the modeline", - "%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str); + "%val{bufname} %val{cursor_line}:%val{cursor_char_column} {{busy_indicator}} {{context_info}} {{mode_info}} - %val{client}@[%val{session}]"_str); reg.declare_option("debug", "various debug flags", DebugFlags::None); reg.declare_option("readonly", "prevent buffers from being modified", false); diff --git a/src/register_manager.cc b/src/register_manager.cc index b9d09a52..987849e1 100644 --- a/src/register_manager.cc +++ b/src/register_manager.cc @@ -70,7 +70,7 @@ const String& HistoryRegister::get_main(const Context&, size_t) return m_content.empty() ? String::ms_empty : m_content.front(); } -static const HashMap reg_names { +static const HashMap reg_names { { "slash", '/' }, { "dquote", '"' }, { "pipe", '|' }, @@ -83,18 +83,23 @@ static const HashMap reg_names { { "colon", ':' } }; -Register& RegisterManager::operator[](StringView reg) const +char RegisterManager::parse_register_name(StringView reg) { if (reg.length() == 1) - return (*this)[reg[0_byte]]; + return reg[0_byte]; auto it = reg_names.find(reg); if (it == reg_names.end()) throw runtime_error(format("no such register: '{}'", reg)); - return (*this)[it->value]; + return it->value; +} + +Register& RegisterManager::operator[](StringView reg) const +{ + return (*this)[parse_register_name(reg)]; } -Register& RegisterManager::operator[](Codepoint c) const +Register& RegisterManager::operator[](char c) const { c = to_lower(c); auto it = m_registers.find(c); @@ -104,7 +109,7 @@ Register& RegisterManager::operator[](Codepoint c) const return *(it->value); } -void RegisterManager::add_register(Codepoint c, std::unique_ptr reg) +void RegisterManager::add_register(char c, std::unique_ptr reg) { auto& reg_ptr = m_registers[c]; kak_assert(not reg_ptr); diff --git a/src/register_manager.hh b/src/register_manager.hh index 9d843eff..6c7cf084 100644 --- a/src/register_manager.hh +++ b/src/register_manager.hh @@ -118,16 +118,17 @@ public: class RegisterManager : public Singleton { public: + static char parse_register_name(StringView reg); Register& operator[](StringView reg) const; - Register& operator[](Codepoint c) const; - void add_register(Codepoint c, std::unique_ptr reg); + Register& operator[](char c) const; + void add_register(char c, std::unique_ptr reg); CandidateList complete_register_name(StringView prefix, ByteCount cursor_pos) const; auto begin() const { return m_registers.begin(); } auto end() const { return m_registers.end(); } protected: - HashMap, MemoryDomain::Registers> m_registers; + HashMap, MemoryDomain::Registers> m_registers; }; } -- cgit v1.2.3