Skip to content

Commit 49dc894

Browse files
committed
Merge branch 'johan/resume-after-editor'
Fixes #381.
2 parents 591b6a0 + f87229d commit 49dc894

File tree

6 files changed

+72
-53
lines changed

6 files changed

+72
-53
lines changed

cmd/moor/moor.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -745,13 +745,6 @@ func startPaging(pager *internal.Pager, screen twin.Screen, chromaStyle *chroma.
745745
if !pager.DeInit {
746746
pager.ReprintAfterExit()
747747
}
748-
749-
if pager.AfterExit != nil {
750-
err := pager.AfterExit()
751-
if err != nil {
752-
log.Error("Failed running AfterExit hook: ", err)
753-
}
754-
}
755748
}()
756749

757750
pager.StartPaging(screen, chromaStyle, chromaFormatter)

internal/editor.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ func errUnlessExecutable(file string) error {
6969
return fmt.Errorf("Not executable: %s", file)
7070
}
7171

72+
// pickAnEditor returns the editor command to execute, where it came from
73+
// ("VISUAL", "EDITOR" or "fallback list"), and an error if no editor could
74+
// be found.
7275
func pickAnEditor() (string, string, error) {
7376
// Get an editor setting from either VISUAL or EDITOR
7477
editorEnv := "VISUAL"
@@ -168,7 +171,7 @@ func handleEditingRequest(p *Pager) {
168171
}
169172
}
170173

171-
p.AfterExit = func() error {
174+
err = p.screen.PauseAndCall(func() error {
172175
// NOTE: If you do any changes here, make sure they work with both "nano"
173176
// and "code -w" (VSCode).
174177
commandWithArgs := strings.Fields(editor)
@@ -197,6 +200,14 @@ func handleEditingRequest(p *Pager) {
197200
log.Info("Editor exited successfully: ", commandWithArgs)
198201
}
199202
return err
203+
})
204+
if err != nil {
205+
log.Warn("Failed to launch editor in paused session: ", err)
206+
p.mode = &PagerModeInfo{
207+
Pager: p,
208+
Text: "Failed to launch editor \"" + editor + "\": " + err.Error(),
209+
}
210+
211+
return
200212
}
201-
p.Quit()
202213
}

internal/pager.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,6 @@ type Pager struct {
132132
//
133133
// Ref: https://github.com/walles/moor/issues/175
134134
bookmarks map[rune]scrollPosition
135-
136-
AfterExit func() error
137135
}
138136

139137
type _PreHelpState struct {

twin/fake-screen.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,9 @@ func (screen *FakeScreen) Events() chan Event {
117117
func (screen *FakeScreen) GetRow(row int) []StyledRune {
118118
return withoutHiddenRunes(screen.cells[row])
119119
}
120+
121+
func (screen *FakeScreen) PauseAndCall(run func() error) error {
122+
// The fake screen doesn't have any special state to save and restore, just
123+
// run it.
124+
return run()
125+
}

twin/screen-suspend.go

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"os"
88
"os/signal"
99
"syscall"
10-
11-
"golang.org/x/term"
1210
)
1311

1412
// Suspend and wait for SIGCONT, then resume. Basically ctrl-Z handling.
@@ -19,47 +17,16 @@ func (screen *UnixScreen) suspend() error {
1917
signal.Notify(cont, syscall.SIGCONT)
2018
defer signal.Stop(cont)
2119

22-
screen.leaveAlternateScreenSession()
23-
24-
err := screen.restoreTtyInTtyOut()
25-
if err != nil {
26-
return fmt.Errorf("failed to restore terminal state before suspend: %w", err)
27-
}
28-
29-
// kill(0) = "Send signal to all processes in the current process group"
30-
err = syscall.Kill(0, syscall.SIGTSTP)
31-
if err != nil {
32-
restoreRawErr := screen.restoreRawModeAfterResume()
33-
if restoreRawErr != nil {
34-
return fmt.Errorf("failed to suspend process group: %w; also failed to re-enter raw mode: %v", err, restoreRawErr)
20+
return screen.PauseAndCall(func() error {
21+
// kill(0) = "Send signal to all processes in the current process group"
22+
err := syscall.Kill(0, syscall.SIGTSTP)
23+
if err != nil {
24+
return fmt.Errorf("failed to suspend process group: %w", err)
3525
}
3626

37-
screen.enterAlternateScreenSession()
38-
screen.onWindowResized()
39-
40-
return fmt.Errorf("failed to suspend process group: %w", err)
41-
}
42-
43-
// Wait for SIGCONT signal to arrive
44-
<-cont
45-
46-
err = screen.restoreRawModeAfterResume()
47-
if err != nil {
48-
return err
49-
}
50-
51-
screen.enterAlternateScreenSession()
52-
screen.onWindowResized()
53-
54-
return nil
55-
}
56-
57-
func (screen *UnixScreen) restoreRawModeAfterResume() error {
58-
terminalState, err := term.MakeRaw(int(screen.ttyIn.Fd()))
59-
if err != nil {
60-
return fmt.Errorf("failed to re-enter raw mode after suspend: %w", err)
61-
}
27+
// Wait for SIGCONT signal to arrive
28+
<-cont
6229

63-
screen.oldTerminalState = terminalState
64-
return nil
30+
return nil
31+
})
6532
}

twin/screen.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ type Screen interface {
7272

7373
// This channel is what your main loop should be checking.
7474
Events() chan Event
75+
76+
// Pause the screen, run the given function, then resume the screen. Blocks
77+
// until the function has completed and the screen has been resumed again.
78+
PauseAndCall(run func() error) error
7579
}
7680

7781
type interruptableReader interface {
@@ -1132,3 +1136,43 @@ func (screen *UnixScreen) showNLines(width int, height int, clearFirst bool) {
11321136
screen.writeLocked(builder.String())
11331137
screen.snapshotLastRenderedLocked()
11341138
}
1139+
1140+
// Pause the screen, run the given function, then resume the screen. Blocks
1141+
// until the function has completed and the screen has been resumed again.
1142+
//
1143+
// Error returns mean that either pausing failed or the run function failed. If
1144+
// resuming fails, this method will panic.
1145+
func (screen *UnixScreen) PauseAndCall(run func() error) error {
1146+
screen.leaveAlternateScreenSession()
1147+
1148+
err := screen.restoreTtyInTtyOut()
1149+
if err != nil {
1150+
return fmt.Errorf("failed to restore terminal state before pause: %w", err)
1151+
}
1152+
1153+
runErr := run()
1154+
1155+
restoreRawErr := screen.restoreRawModeAfterResume()
1156+
if restoreRawErr != nil {
1157+
panic(fmt.Errorf("failed to resume screen after paused operation (%v): %w", runErr, restoreRawErr))
1158+
}
1159+
1160+
screen.enterAlternateScreenSession()
1161+
screen.onWindowResized()
1162+
1163+
if runErr != nil {
1164+
return runErr
1165+
}
1166+
1167+
return nil
1168+
}
1169+
1170+
func (screen *UnixScreen) restoreRawModeAfterResume() error {
1171+
terminalState, err := term.MakeRaw(int(screen.ttyIn.Fd()))
1172+
if err != nil {
1173+
return fmt.Errorf("failed to re-enter raw mode after suspend: %w", err)
1174+
}
1175+
1176+
screen.oldTerminalState = terminalState
1177+
return nil
1178+
}

0 commit comments

Comments
 (0)