action: Stop processing chained actions/commands in the moment the current Pane is not a BufPane (fix crash)#3261
Conversation
internal/action/tab.go
Outdated
| // CurPane returns the currently active pane | ||
| func (t *Tab) CurPane() *BufPane { | ||
| p, ok := t.Panes[t.active].(*BufPane) | ||
| func (t *Tab) CurPane() Pane { |
There was a problem hiding this comment.
Why not keep it returning *BufPane, and in the callers just check if the returned value is not nil?
The lua-exported function returns Pane, yes, but according to the documentation it should return *BufPane. Isn't it better to fix it to be in accordance with the documentation?
There was a problem hiding this comment.
Why not keep it returning
*BufPane, and in the callers just check if the returned value is not nil?
This would be the easiest approach, but the function is called CurPane() and as such it should return the current pane, which might be a BufPane, RawPane (derived from BufPane), InfoPane (derived from BufPane) or a TermPane.
I just imagine the moment we really like to address the current active TermPane received from CurPane(), without changing the interface again.
The lua-exported function returns
Pane, yes, but according to the documentation it should return*BufPane. Isn't it better to fix it to be in accordance with the documentation?
But we could also correct the documentation, that the function returns a Pane. Anyway, isn't there a type conversion in Lua to address the BufPane based on the received value (I'm no Lua expert)?
There was a problem hiding this comment.
Anyway, isn't there a type conversion in Lua to address the
BufPanebased on the received value (I'm no Lua expert)?
I'm afraid there isn't. I don't see https://pkg.go.dev/layeh.com/gopher-luar#New saying anything about Go's type assertion.
So, unless you know a way to do that, keeping the existing (implemented but not documented) polymorphic behavior of lua's CurState() doesn't seem like a good idea. With the (documented but not implemented) non-polymorphic behavior, a plugin at least would be able to check if the current pane is a bufpane or not.
We may consider generalizing CurPane() to non-buf panes in the future, but I think that when it comes to non-buf panes, we have more important stuff to generalize anyway. At least the following:
- Lack of support for non-buf panes in chained actions. While this PR will fix the crashes in #2307 and #2288, it will still not make either of the keybindings in #2307 and #2288 actually work, since it just interrupts handling the action once it encounters to a non-buf pane.
- Lack of support for
onActionandpreActioncallbacks for non-buf panes. See e.g. 2677#issuecomment-1409211651
...In the meantime, when it comes to CurPane():
InfoPane: plugins can useInfoBar().HasPromptto check if the infopane is currently active or not (like micro itself does).TermPane: yes, plugins probably cannot check if the current pane is a term pane (and which of them, if there are multiple term panes), but possibly there will never be a use case for that?RawPane: it is quite a special type of pane, plugins would hardly want to do anything with it.
There was a problem hiding this comment.
I more or less reverted the most of the stuff. I hope I didn't overlook or brake something in regards of plugin compatibility.
Currently it feels like not moving forward with micro, since so many functions are exported and can't simply changed or evolved. :/
internal/action/bufpane.go
Outdated
| // if the action changed the current BufPane to a BufPane again, update the reference | ||
| bp, ok := MainTab().CurPane().(*BufPane) | ||
| if !ok { | ||
| return innerSuccess |
There was a problem hiding this comment.
It always return true otherwise. It returning innerSuccess here useful? Looks like the return value of this function is never really used.
In other words, can we just break here?
There was a problem hiding this comment.
But innerSuccess is modified in the condition...
innerSuccess = innerSuccess && h.execAction(a, names[i], j, te)
...depending on the result of execAction() or do I oversee something?
In case we break we return the forced true.
There was a problem hiding this comment.
So, as I said, it doesn't look like this return value is used anywhere anyway. So it doesn't look like it makes sense to return anything else than the forced true here. (Or, if we still want to return innerSuccess here, then at least for consistency we should also at line 177 return success instead of the forced true, right?)
AFAICS success and innerSuccess are only for determining if we should execute the next action, but for determining which value should bufAction return.
I actually have no idea what is the semantics of this return value. To be precise, I actually see just one place where this return value is used: https://github.com/zyedidia/micro/blob/master/internal/action/infopane.go#L143
and, this particular code path looks VERY confusing to me. Can you explain to me what does this code do and why does it use this return value the way it does? Why doesn't InfoPane's DoKeyEvent() just do the same thing as BufPane's DoKeyEvent()?
There was a problem hiding this comment.
Reading your question again... Yes, innerSuccess is modified depending on the value returned by execAction(), but what does it have to do with the value returned by bufAction?
There was a problem hiding this comment.
but what does it have to do with the value returned by
bufAction?
Unfortunately nothing.
[...] return
successinstead of the forcedtrue, right?
Yes and so I did.
There was a problem hiding this comment.
Answering myself:
To be precise, I actually see just one place where this return value is used: https://github.com/zyedidia/micro/blob/master/internal/action/infopane.go#L143
and, this particular code path looks VERY confusing to me. Can you explain to me what does this code do and why does it use this return value the way it does? Why doesn't InfoPane's
DoKeyEvent()just do the same thing as BufPane'sDoKeyEvent()?
Ok, I've figured out myself what's up with that logic in InfoPane's DoKeyEvent(). That logic makes sense, although is quite non-obvious and also somewhat buggy... PR #3267 is the result of my investigations.
[...] return
successinstead of the forcedtrue, right?Yes and so I did.
And I'm still not sure it's the best idea. I still believe this return value is not really used, and should not really be used. (Please see 8c7f63a and let me know if you disagree with the commit message.)
Well... if we merge #3267, including 8c7f63a, then there will be no more code that (improperly) tries to use this return value. So then I'm ok with either keeping this return true here or changing it to return success.
There was a problem hiding this comment.
Just a nit though: it's better to do this (change to return success) in a separate commit, since it is a separate change, not related to the fix for the crash.
There was a problem hiding this comment.
Please see 8c7f63a and let me know if you disagree with the commit message.
Hm, then we could drop the return at all. Anyway, I'm fine with that.
9798978 to
ba324c0
Compare
TermPane as Pane (fix crash)Pane is not a BufPane (fix crash)
ba324c0 to
b94981f
Compare
This will add the capability to address the `TermPane` within the tabs, since the tab list only stores panes.
b94981f to
07cda68
Compare
Actually the
TermPaneisn't really considered as aPane, even it really is aPane.-> In the first version of this PR I added the
Name()function to theTermPaneto be able to consider theTermPaneas aPanein the future.The main change to fix the crash is the check the return of
MainTab().CurPane()againstnilin theBufMapEvent()respectivebufAction()function, becauseCurPane()will return it in the moment the current pane changes to a pane other than aBufPane.Fixes #2288
Fixes #2307