Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions internal/action/tab.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,55 @@ func (t *TabList) HandleEvent(event tcell.Event) {

if i := t.LocFromVisual(buffer.Loc{mx, my}); i == -1 {
t.List[t.Active()].CurPane().AddTab()
} else {
anyModified := false
for _, p := range t.List[i].Panes {
if bp, ok := p.(*BufPane); ok {
if bp.Buf.Modified() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This produces false negatives if the same buffer is also opened in another pane in another tab (so closing this tab would not cause losing unsaved changes in this buffer).

The similar problem was addressed in #3559 + #3719 by adding Shared() which checks if the buffer is opened in more than one pane. Unfortunately we cannot just reuse Shared() here, since we need to distinguish the case when all the panes with this buffer are in the tab we are closing (then we need to prompt the user) and the case when at least one such pane is in a different tab (then we don't need to prompt the user).

anyModified = true
break
}
}
}

removeTab := func() {
panes := append([]Pane(nil), t.List[i].Panes...)
for _, p := range panes {
switch t := p.(type) {
case *BufPane:
t.ForceQuit()
case *RawPane:
t.Quit()
case *TermPane:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are closing the terminal pane without prompting the user. Whereas the user might run anything at all in this terminal, for example another instance of micro, with some unsaved changes, lol.

I don't really like the fact that we support this embedded terminal functionality (which is doomed to never be on par with proper terminals), and I never used it myself, but apparently a lot of users do use it (as they often report bugs about this or that terminal functionality not working in it)...

t.Quit()
}
}
Comment on lines +205 to +214
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is the best approach. Perhaps we could add Quit to Pane struct? It seems every Pane uses it.

}

a := t.Active()
if anyModified {
t.SetActive(i)
InfoBar.YNPrompt("Discard unsaved changes? (y,n,esc)", func(yes, canceled bool) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user may middle-click the tab name accidentally and may not be aware that it closes the tab, so the user may not know why this prompt is being shown and which exact unsaved changes (in which buffers) are going to be discarded.

So we'd better make it more clear, at least something like "Discard unsaved changed in this tab?"?

if !canceled {
if yes {
removeTab()
if i <= a {
a--
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes a crash when closing a tab which is the first tab in the tab bar and which is currently active.

Micro encountered an error: runtime.boundsError runtime error: index out of range [-1]
runtime/panic.go:115 (0x43ccb4)
github.com/zyedidia/micro/v2/internal/action/tab.go:338 (0x90a68f)
github.com/zyedidia/micro/v2/cmd/micro/micro.go:467 (0x90a0d2)
github.com/zyedidia/micro/v2/cmd/micro/micro.go:455 (0x909e70)
internal/runtime/atomic/types.go:194 (0x441f4b)
runtime/asm_amd64.s:1700 (0x47d341)

And besides that, when closing an active tab, it is more intuitive to activate the tab after it, not the tab before it (except the case when there is no tab after it)?

So, change i <= a for example to i < a || (i == a && a > 0 && a == len(t.List)-1) (both here and at line 236)?

}
}
t.SetActive(a)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We return to the original active tab if the user pressed n but not if the user pressed esc? Why?

}
})
t.release = true
t.tbClick = false
t.tabDrag = false
Comment on lines +231 to +233
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we call YNPrompt on click, the release is handled by infobar and not by Tablist.
I assumed that when the user exits the prompt, they've already released mouse3, this might be untrue though. The user could enter and leave the prompt while still holding mouse3.. Not sure if it's worth solving this.

} else {
removeTab()
if i <= a {
t.SetActive(a - 1)
}
}
return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This return seems redundant?

}
case tcell.WheelUp:
t.Scroll(4)
Expand Down
Loading