summaryrefslogtreecommitdiff
path: root/src
AgeCommit message (Collapse)Author
2025-07-10More CI compilation fixesMaxime Coste
2025-07-08Tentative fix for CIMaxime Coste
2025-07-08Replace on_scope_end with CTAD with OnScopeEnd directlyMaxime Coste
2025-07-08Replace std::unique_ptr with a custom implementationMaxime Coste
<memory> is a costly header we can avoid by just implementing UniquePtr ourselves, which is a pretty straightforward in modern C++, this saves around 10% of the compilation time here.
2025-07-08Remove uses of Regex in BufferManager by taking a more general filterMaxime Coste
2025-07-08Merge remote-tracking branch 'pjungkamp/history'Maxime Coste
2025-07-07Copy instruction to local variable in step_current_threadMaxime Coste
This helps the compiler realize that data cannot change and does not need reloading, improving codegen slightly.
2025-07-07Use uint32_t for DualThreadStack indicesMaxime Coste
2025-07-07Add a CharRange regex op to optimize the common simple range caseMaxime Coste
Instead of jumping into the general CharClass code, detect simple [a-z] style ranges and use a specific op. Also detect when a range can be converted to ignore case
2025-07-07Avoid branches in ThreadedRegexVM::DualThreadStack iterationMaxime Coste
decrement and post_increment do not get cmov optimised as expected, we can avoid this altogether by taking advantage of the fact that capacity is always a power-of-two and we can hence use a bitwise and we can use a bitwise and to loop around capacity.
2025-07-05Introduce `history_since_<id>` value expansionPhilipp Jungkamp
The `history_since_<id>` value expansion allows incremental parsing of a buffer's history. declare-option int my_last_history_id define-command my-process-history ... # process the initial buffer history my-process-history %val{bufname} 0 %val{history} set-option buffer my_last_history_id 0 # only process new history changes on idle hook buffer NormalIdle %{ evaluate-commands %exp{ my-process-history \ %%val{bufname} \ %%opt{my_last_history_id} \ %%val{history_since_%opt{my_last_history_id}} } set-option buffer my_last_history_id %val{history_id} }
2025-07-03Rework startup messageMaxime Coste
Link to `:doc changelog` in the title and link to startup-info there so that this information stays visible even if the changelog overflows the info box. Use format support for 0 padding instead of custom code
2025-07-02Only keep the last 3 versions in startup infoMaxime Coste
We have this information in the changelog and its unlikely users will read past the third entry. Fixes #5352
2025-06-30Remove unused capture modeMaxime Coste
2025-06-30Stop apply_diff() from creating non-monotonic changesJohannes Altmanninger
Commit ba41928aa (Remove spurious newline when | replaces at buffer end, 2024-11-28) also tried to fix a "DiffOp::Add" code path. Unless I'm misremembering, I had run into a crash and extracted this unit test. When apply_diff() decides to erase the last line, and then insert something at the end, buffer changes might look like: {.type=Erase, .begin={2, 0}, .end={3, 0}} {.type=Insert, .begin={1, 5}, .end={5, 0}} The "{1, 5}" is caused by "pos = buffer.prev(pos);" added by the aforementioned commit. It's problematic because it breaks the caller's "ForwardChangesTracker" invariant: the previous Erase leaves the cursor at {2, 0}, so the insert cannot start before that. Additionally, the inserted range returned by apply_diff() would be "{ {2, 0}, {4, 0} }" whose end position is weirdly different from the Insert's end position. It questionable that we are passing on this weird state. The "pos = buffer.prev(pos);" statement was added so we actually delete the current final newline. We still leave "tried_to_erase_final_newline" set, meaning we'll also delete the the final newline after we're done. But deleting the "current final newline" the way we do it is confusing, because we do it at every step DiffOp::Add step. This would blow up if there are multiple successive contiguous DiffOp::Add events. I doubt this ever happens but it's still confusing. Remove this logic, and restore historical behavior in case we append at the buffer end. This does not break the system test added by ba41928aa because in that case, we 1. first delete the entire buffer, setting pos={0, 0}, but also restoring the EOL invariant, so the end position would be {0, 1}. 2. then we insert at pos={0, 0}, and since "buffer.is_end(pos)" is false we don't run into this bad code path. While at it add an assertion to clarify that we already assume that the diff algorithm never gives us empty DiffOp::Keep, and always monotonically increasing positions, except for the very special case where we deleted until buffer end, which should already be handled. The unit test is not perfect because it depends on the current behavior of the diff algorithm.
2025-06-29Fix compilation with compilers not supporting auto(x)Maxime Coste
2025-06-28Move some String constructors out of the headerMaxime Coste
Those do not really need to get inlined and pull std::max which really wants <algorithm> which is an expensive header.
2025-06-28Merge remote-tracking branch 'Yukaii/feature/native-cursor'Maxime Coste
2025-06-28fix: simplify variable by removing terminal_ partYukai Huang
2025-06-28fix: remove comment and document usageYukai Huang
2025-06-28Revert "chore: remove terminal option"Yukai Huang
This reverts commit 549a5d2c223d422390795741537b150b492a3935.
2025-06-28Fix wrap interaction with show-whitespaces and test word wrapping moreMaxime Coste
Consider atom boundaries as word boundaries, which should be correct du to passes ordering. Fixes #5350
2025-06-27Merge branch 'master' into feature/native-cursorYukai Huang
2025-06-27fix: revert new line changesYukai Huang
2025-06-27chore: remove terminal optionYukai Huang
2025-06-27fix: prevent cursor rendering conflicts by simplifying cursor positioning logicYukai Huang
2025-06-27Move helper logic inside static_gatherMaxime Coste
2025-06-27Remove <algorithm> include from string.hhMaxime Coste
This was only used for std::min and std::equal, which can be replaced with custom code and memcmp, this removes a costly header from the often used string.hh and may improve compilation speed slightly
2025-06-27Small code style tweak in ranges utilitiesMaxime Coste
2025-06-27Prevent changing Client's buffer while lockedMaxime Coste
If the current buffer is locked, it means we are in the middle of creating the client or already changing the buffer, error out in this case instead of getting into an inconsistent state. Fixes #5338
2025-06-26Remove spurious newline when | replaces at buffer endJohannes Altmanninger
My kak -e "exec %{%ca<ret>b<esc>%|printf '\n\n'<ret>}" adds a spurious third line. When we replace up to the end everything, we keep around a single newline to uphold the buffer invariant, but that newline Commit de1433d30 (Avoid the spurious newline insertion when replacing at end of buffer, 2016-03-16) fixed an issue for most commands that replace until the buffer end. A similar issue exists for the "|" command. It triggers in fewer cases because the replacement is implemented by applying edits computed by the diff algorithm. It does trigger when "|" replaces the entire buffer. Fix that by erasing the spurious newline. Alternatively, we could allow the diff steps of kind "remove" to delete the entire buffer, and only restore the invariant after the whole diff is applied. This should work because the one-past-end position is valid for Buffer::insert() even if the buffer is empty. It is not valid for Buffer::erase() or Buffer::advance() where count>0 but if we do that when we're already at the buffer end, that is probably a bug in the diff. I tried this but ran into a assertion in ForwardChangesTracker (kak_assert(change.begin >= cur_pos)). Alternatively, we could change the diff algorithm to always insert before deleting. I encountered this issue when using ansi-enable on a fifo buffer. Specifically, the first BufReadFifo hook would replace the entire inserted text but leave around a spurious newline.
2025-06-26Fix flaky blame-in-diff test and "edit -fifo" in BufCloseFifoJohannes Altmanninger
This test uses ui_out and ui_in to coordinate events. This is brittle[1] because ui_out behavior depends on timing. Since this test doesn't really care about intermediate UI state, express the sequence using BufCloseFifo instead. This hits another issue: inside git blame-jump's BufCloseFifo, we run git blame, which runs another "edit -fifo .. *git*". A special aspect of fifo buffers is that any existing *git* buffer will be reused instead of being recreated[2]. After BufCloseFifo, the fifo watcher destructor will reset the fifo flag, even if BufCloseFifo has recreated the fifo buffer. This breaks invariants and causes the next fifo watcher destructor do fail its assertion. Let's not reset fifo flags in this case. This should be safe because it's the very last thing the fifo watcher does, so it's okay if another one is active now. Alternatively we could reject this kind of recursion, or implement it in a different way (using ScopedSetBool for the flags). Reported-by: Nico Sonack <nsonack@herrhotzenplotz.de> [1]: https://lists.sr.ht/~mawww/kakoune/%3C20241210100417.1150697-1-aclopte@gmail.com%3E [2]: This removes the need to use delete-buffer which also ensures that the buffer remains visible in any client it's already shown.
2025-06-26Delay destruction of item in hash map without returning an optionalMaxime Coste
Returning an optional of the removed item is not very idiomatic and we currently have no use case for this.
2025-06-26Fix crash on redraw in BufCloseFifo hookJohannes Altmanninger
FifoWatcher::read_fifo() deletes the fifo watcher in m_buffer.values().erase(fifo_watcher_id); // will delete this which calls: HashMap::unordered_remove() constexpr_swap(m_items[index], m_items.back()); destructor called here --> m_items.pop_back(); m_index.remove(hash, index); So hash map invariants (of buffer.values()) are broken, when calling ~FifoWatcher which fires BufCloseFifo hooks. Things blow up if those hooks access buffer.values() such as by accessing cached highlighters to redraw the buffer. A shell call with a long sleep in the client context seems to trigger this. Fix this by destroying removed map items only at the end of HashMap::remove(), when invariants are restored. Alternatively, we could introduce a fifo_trash container; I haven't explored that.
2025-06-26Rework WrapHighlighter to take replaced ranges into accountMaxime Coste
Replaced ranges will count towards the wrapping column but will not be split. Fixes #4883
2025-06-25Introduce a Replace HighlightPass that runs before WrapMaxime Coste
For WrapHighlighter to be able to take ReplaceRangesHighlighter into account, it needs to run afterwards, moreover, moving TabulationHighlighter to the replace pass means we should be able to remove any tabulation specific handling from WrapHighlighter Also move ShowWhitespaceHighlighter to this pass as it is designed to replace TabulationHighlighter by running before (as builtin highlighters run after non builtin ones for each passes)
2025-06-25Rework highlighting management of cursor positionMaxime Coste
Move the whole responsibility of making the cursor visible to the window, removing cursor_pos from the display setup and resolving the cursor location by finding it in the display buffer afterwards. This simplifies hightlighters' do_compute_display_setup as they do not need to compute the cursor location. Highlighting may run on more lines than necessary after this change but this should be a minor performance hit.
2025-06-24feat(terminal): add option for native terminal cursor renderingYukai Huang
2025-06-19Merge remote-tracking branch 'SeerLite/macro-disable-keymaps'Maxime Coste
2025-06-03Kakoune 2025.06.03Maxime Coste
2025-06-03Support '*' and comma separated list for exec/eval -client switchMaxime Coste
Closes #5326
2025-06-03Refactor context_wrap use a context_wrap_for_context lambdaMaxime Coste
This makes the code a bit clearer by avoiding the base_context and effective_context pointers and replacing them with calling this lambda with the correct context we want to use as base. This also paves the way towards adding `-client *` support
2025-06-03Prevent deletion of buffers while creating new windowsMaxime Coste
`kak -n -E 'hook global WinCreate .* %{ delete-buffer }'` was crashing because we would delete the buffer during window construction, which would not be able to delete the window as it was not fully constructed and registered yet. This led to a window referencing a deleted buffer. Fixing this by deleting the window later on failed because we can enter an infinite loop where we constantly create a new *scratch* buffer, then a window to display it, which deletes that buffer. Make it an error to try to delete a buffer while a new window is being setup by adding a Locked flag to buffers and checking that in BufferManager::delete_buffer Fixes #5311
2025-05-29Merge overlapping selections before replacing charactersMaxime Coste
Without doing this, replace with multiple selection at buffer end breaks as the first selection will replace the end-of-line with a non-eol character, then a new eol will automatically be added which will put remaining selections past the end of the buffer. Fixes #5316
2025-05-23Disable BufSetOption hook during buffer registrationMaxime Coste
The hook is manually triggred at the end of registration, by disabling it we avoid the hook being potentially called multiple times due to interaction with other hooks. Fixes #5324
2025-05-23Fix out-of-bounds write in short strings when reaching capacityMaxime Coste
When we reach capacity for Short strings, we re-use the field past the buffer (remaining_capacity) to hold the terminal null character, this works well but the code was trying to set it twice: when computing the remaining capacity (which is 0 when we hit capacity), and when setting the null character. While this second operation should be fine in practice, it is technically undefined behaviour as we overwrite it by accessing past the end of the buffer.
2025-05-11Use linewise paste only if register is missing no trailing newlineJohannes Altmanninger
We use linewise paste if any of the pasted register elements has a trailing newline. As shown in the previous patch, this leads to awkward situations where insertion positions from <a-P> are not monotonically increasing. This complicated behavior is questionable. The root cause is that linewise paste may insert some strings that don't have a trailing newline. This leads to weird cases of GIGO, especially when using <a-P> when the last element is missing a trailing newline. Simplify the implementation by not activating linewise paste in that case. Instead of special-casing the last element, try to simplify the behavior further by using line-wise paste only if *all* elements are properly terminated. I can't think of a real-world case where the existing behavior would be desired. A regression test shows a change in behavior. Might be less weird now. The added test case is a copy except it uses "P" instead of "p" (the extra newline is because "d" deletes the last newline in the buffer which gets added back implicitly). (I think the list of register elements is never empty, so this should never cause a trivial linewise paste. Even if so, that would be fine because inserting nothing is a nop either way.) Fixes #5312
2025-05-11Work around non-forward insertions by linewise <a-P>Johannes Altmanninger
Given a buffer with two selections %(a)b%(c) Consider <a-P>, which inserts all elements of the dquote register, joined to a scalar string. This insertion happens once before each selection. Let the dquote register contain "\n" and "\nd". Since the first element ends in a newline, we enable linewise paste mode. The insertion at the first selection results in: contents: \n\nd%(a)b%(c) timestamps: 11111000000000 On top of that, the insertion at the second selection would result in: contents: \n\n\n\ndd%(a)b%(c) timestamps: 1111222221000000000 Observe that the second insertion actually takes place inside the text added by the first insertion. This is because we are inserting before the current line, and the first insertion does not end in a newline. This breaks the forward change tracker's assumption that each change takes place after the previous one, causing an assertion failure. We use this data structure to translate from old to new coordinates. Specifically, after the first insertion, the selection around %(c) which is initially 1.3,1.3 needs to be updated to 3.4,3.4. Work around this by instantiating a new ForwardChangesTracker after each step. This is very ugly because it's quadratic, and because it doesn't change the fact that the second insertion is made inside the first one. I think we can revert the workaround added by this patch, see the next patch. Closes #5312
2025-05-11Fix <a-?> crash when first selection wrapsJohannes Altmanninger
The <?> and <a-?> commands drop selections where the search wrapped. If a selection is dropped this way, we adjust the new main index. If the very first selection is dropped this way, *and* it happens to be the main index, "--main_index" overflows and chaos ensues. Fix this by saturating main index at zero. This seems more intuitive than wrapping around ("% new_sels.size()"). This issue only happens to <a-?>, not <?>. When <?> wraps when starting from the first selection, it necessarily also wraps after all other selections. (Based on this insight, I started drafting an optimization to skip searches whose results would be discarded anyway because they will definitely wrap. Not sure if it's worth it, since it applies only to the rare edge case of <?>/<a-?> with multiple selections.) Fixes #5314
2025-05-11Fix compiler warning on space in string-literal suffix defJohannes Altmanninger
GCC complains: src/string.hh:376:39: warning: space between quotes and suffix is deprecated in C++23 [-Wdeprecated-literal-operator] 376 | inline constexpr StringView operator"" _sv(const char* str, size_t) | ^~