-
Notifications
You must be signed in to change notification settings - Fork 187
Add chat history accessible with up/down arrows #1244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add chat history accessible with up/down arrows #1244
Conversation
src/gui/windows/chat.zig
Outdated
| pub var input: *TextInput = undefined; | ||
| var hideInput: bool = true; | ||
| var upHistory: CircularBufferQueue([]const u8) = undefined; | ||
| var downHistory: CircularBufferQueue([]const u8) = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering, is there an advantage to this double-queue system? Why not just have one "scratch history" buffer and an index into it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a result you are never directly modifying any history entry, so they are all preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is more subtle gain from it compared to single queue history. Assume you have history and move up twice to a command, then you modify it and by mistake move up again. With current system modified message will be inserted in the middle, between history entry you were modifying and history entry you moved to, so you can always go back.
So other options either don't preserve entries you modify or preserving modified entries is O(N).
If you think otherwise please provide more details, I am no data structure specialist, I do stuff so it works the way I want it rather than making it optimal from the beginning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, your model here is totally different from what I expected. I expected that you just had a list of old messages, and you could modify each one (or all) as you wished, and that scratch history would get replaced as soon as you entered something. Coincidentally like my favorite zsh.
What you have is that you can scroll through each message, and edit them, but when scrolling away from an edited message you first insert it into the history list. I mean, if that's intended behavior, your data structure is pretty good (though do you need to duplicate the string before immediately freeing it?). I get it now, but the user experience is quite surprising.
Oddities:
- The first time you press up or down after editing a message, the message box "undoes" your message rather than actually scrolling to the message before/after the one you were just editing.
- Message boxes get cloned every time you scroll up or down, and this can get quite cluttered if you move back and forth. I don't think there's a great reason to preserve the edit-histories of history like that. Generally this would be written into an invisible undo-buffer to be accessed with +Z.
- Edits to messages get preserved after pressing enter. Combined with the previous issue, it means that the intermediate edits linger up in your history, while the final drafts ironically get removed.
Overall, I think this is way too conservative. I don't think chat messages are precious enough that every change must be remembered. Most software (besides vim, which has 2 dimensions of undo) would start dropping edits much earlier than this.
I just checked, and it turns out that shells also have different ways of dealing with history. My zsh keeps history edits when scrolling, then clears them when any command is run. Apparently my bash keeps history edits until that specific line is entered, and only resets one line at a time. So there is variation there.
Now I see what you're getting at, but I also think you're just trying to serialize a list of undo histories (a 2d array) into the main edit history. So I guess I would implement this as an array of doubly-linked lists (rather than your center-insertable circular buffer, which I do think is quite clever). This feels like a natural modern GUI generalization of shell-style edit histories. Thus you could scroll through the history above, have it not desync from the displayed history, and you can edit it at your leisure, and your can use standard UI conventions to recover from accidents.
Wow this is quite a long message, but there are a surprising number of subtleties going on here. Basically, your UI is rather idiosyncratic, which wouldn't help your PR or first-time users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I don't find this this particularly "idiosyncratic", but this is expression of my personal preference - it pisses me of like hell when I navigate to long command I used before, edit it (possibly significantly) just to lose it cause one of my fingers hit up/down arrow by mistake or the original command goes away, especially if it was doing some clever thing like $(git log -1 --format=%H) and I have to type it again.
As for messages being precious, no, they are not, but commands with complex block patterns are and it is common to shuffle through many different patterns and mask expressions while building, so I find preserving them very valuable. In shell you can easily open a notepad, and use Ctrl+Z/fg to back and forth if you really need that high precision while modifying commands, but here there are no scripts, no subshells, only command line. If you want to preserve anything you need to copy it, open notepad as separate window and paste it there, that significantly increases complexity the command has to reach before it is worth the effort.
As for command history, problem is that history done per command can not diverge, if you change command, undo that change and change it again you inevitably lose first branch. As I explained it is common to experiment a lot when doing patterns, so it is inconvenient.
This behavior can be actually quite easy to summarize: "Every unique message (command) you submit is preserved" and "submit means that you either hit enter or move to different history entry with up/down arrows"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, instead of putting it above the command that's modified, you could just put it below it???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, it doesn't exactly work like that. See transferBetweenQueues, it doesn't place anything explicitly above or below, it moves current message (if it is different than top value of outQueue) and first inQueue entry to outQueue. Rest is emerging behavior of that implementation. While it is possible to do what you are asking for, it is not trivial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I kind of figured that out, so now behavior is as follows:
type: A <Enter>
type: B <Enter>
type: C <Enter>
History:
A
B
C
textInput: ``
type: <upArrow>
textInput: C
type: <upArrow>
textInput: B
type: 12
textInput: B12
And now from that state:
1. Scenario
<upArrow>
textInput: B
History:
A
B <-- we are here
B12
C
2. Scenario
<downArrow>
textInput: C
A
B
B12
C <-- we are here
Rest should be more or less the same. I would appreciate if @archbirdplus and @IntegratedQuantum could try to break it cause I have no faith that it will work correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be an issue when changing the last history entry:
If you press up after changing the oldest entry, it will still not navigate to the previous value of it.
But to be honest, with an 8192 history buffer and since this will likely be saved in the future too, I don't think it matters much.
So we can keep it bugged if that keeps the implementation simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that edge case would be hard to get rid of, since this is because up queue is empty when you reach last item.
src/gui/windows/chat.zig
Outdated
| pub var input: *TextInput = undefined; | ||
| var hideInput: bool = true; | ||
| var upHistory: CircularBufferQueue([]const u8) = undefined; | ||
| var downHistory: CircularBufferQueue([]const u8) = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think it would be nicer if the message was inserted below the original and not above it. It also shouldn't be too much trouble to implement, right?
Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com>
|
I have changed the code to use FixedSizeCircularBuffer. |
|
Alright I also made it so it is possible to return to empty entry if you have navigated up, but it is not saved. |
|
@archbirdplus alr so, I am too stupid to fully understand what you are saying, but I changed it again a bit, and I cant really reproduce the duplicated message behavior so for me it seems alr... Could you check again and pretty please provide simple step-by-step guide how to reproduce any undesired behaviors you find? |
|
Sorry.
Issue 2:
Issue 4:
These are the setups that I am testing. Tell me if you disagree or can't replicate something. |
|
Issue 1 - can't reproduce after last change. Can you confirm you still can reproduce? |
|
Issue 1 is resolved. Issue 2 is resolved. Ok, great! For 4a: For 4b:
Alright, I'll take that. Then the 4b problem is slightly more reasonable, but there are still 2 consecutive |
|
@archbirdplus Alr, I misunderstood issue 4, you are right, in 4a result should be |
|
I thought I saw it again, but I can't reproduce 4. But I found one more- when you scroll up, exit chat, then press |
|
@archbirdplus Ehh that is a bad one, I already made tools to fix this in #1380 (onCharacter callback) but fixing conflicts caused by adding it here would suck, can we note this down as known limitation and fix this after #1380? Basically fix would be to flush history in onCharacter if all input contains is '/', just adding that callback now is not necessarily fun experience C: |
|
As long as the bug won't be left hanging indefinitely, I approve this PR from the functionality perspective. |
|
I will make PR for that today |
src/utils.zig
Outdated
| } | ||
|
|
||
| pub fn dequeueFront(self: *Self) ?T { | ||
| return self.dequeue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a mismatch in the naming conventions.
Below, in CircularBufferQueue, you delcared dequeue_back as self.dequeue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I suppose we prefer camelCase rigth? That other one should be gone + it was done that way because of other methods being snake_case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed changes from CircularBufferQueue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also about back vs front naming
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, right, I'll swap those
IntegratedQuantum
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I'm still confused on the naming convention, I looked it up on wikipedia and consensus seems to be that back is at the end of the range, which kind of makes more sense.
But I don't want to keep this PR blocked on naming issues any longer, so please resolve this as a part of #1320, and please do it sooner rather than later.
* Add up down message history * Deduplicate messages when inserting into history * Add dedicated function for inserting strings into TextInput * Fix formatting issues * Change history behavior * Rename inputString to setString * Move clearing to setString * Apply suggestions from code review Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> * Remove unused cursor capture * Use FixedSizeCircularBuffer for history * Restore large queue size * Allow navigation to empty entry * self.len must never be bigger than capacity * Move optional callbacks into struct * Use enum for moveCursorVertically return value * WA attempt #1 * Fix edge case from review * Update src/gui/windows/chat.zig Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> * Remove isEmpty and isFull * Allow for empty history entry * Change empty message handling some more * Remove unused methods * Go to hell with all of those edge cases <3 * WA for 4b * Remove CircularBufferQueue methods * Fix queue thing --------- Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com>
This pull request adds mechanism widely present in terminals which allows user to quickly access commands they typed before by using up and down arrows. Implementation has few assumptions: