Skip to content
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: Exec, program.ReleaseTerminal and RestoreTerminal to re-use input and terminal #237

Merged
merged 22 commits into from Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8f4002e
add: program.ReleaseTerminal and RestoreTerminal to re-use input & te…
muesli Feb 21, 2022
aa2acb9
chore(examples): add altscreen toggling to exec demo
meowgorithm Mar 1, 2022
0e5c9d3
chore: put low-level altscreen stuff alongside other screen funcs
meowgorithm Mar 1, 2022
a7a1d01
docs: edit GoDocs for ReleaseTerminal and RestoreTerminal
meowgorithm Mar 1, 2022
e78a869
feat(renderer): add internal Msg renderMsg to immediately repaint
meowgorithm Mar 1, 2022
91f7d34
fix: repaint instantly on RestoreTerminal
meowgorithm Mar 1, 2022
9f21889
fix: restore the altscreen state when restoring the terminal
meowgorithm Mar 1, 2022
c483da8
feat: implement Cmd-based API for blocking *exec.Cmds
meowgorithm Mar 21, 2022
2473b3e
feat: allow Exec to return custom messages
meowgorithm Mar 21, 2022
2884231
feat: allow Exec to be run without a callback
meowgorithm Mar 21, 2022
781f7f0
fix: typo
muesli Mar 21, 2022
6651901
fix: separate parameters for exec.Command examples
muesli Mar 21, 2022
67f6aa0
fix: error message would get printed over by prompt in exec example
meowgorithm Mar 22, 2022
d56440e
fix: ignore signals while child process is running
muesli Apr 1, 2022
67c6ded
feat: allow to execute other things besides exec.Commands (#280)
caarlos0 Apr 4, 2022
f3f2ac2
fix: callback type should be exported
caarlos0 Apr 4, 2022
b365a7e
fix: fix some comments
caarlos0 Apr 5, 2022
6a80e7c
docs(exce): tiny ExecCommand doc comment correction
meowgorithm Apr 6, 2022
54a84a3
chore(exec): break out Cmd for clarity's sake in example
meowgorithm Apr 6, 2022
7d6e0c2
fix(exec): give the terminal a moment to catch up if exiting altscreen
meowgorithm Apr 6, 2022
8e380dc
docs(exec): tidy up doc comments
meowgorithm Apr 6, 2022
f5ff931
chore(exec): disambiguate methods for restoring the terminal state vs…
meowgorithm Apr 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 67 additions & 0 deletions examples/exec/main.go
@@ -0,0 +1,67 @@
package main

import (
"fmt"
"os"
"os/exec"

tea "github.com/charmbracelet/bubbletea"
)

type editorFinishedMsg struct{ err error }

func openEditor() tea.Cmd {
c := exec.Command(os.Getenv("EDITOR")) //nolint:gosec
return tea.Exec(tea.WrapExecCommand(c), func(err error) tea.Msg {
return editorFinishedMsg{err}
})
}

type model struct {
altscreenActive bool
err error
}

func (m model) Init() tea.Cmd {
return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "a":
m.altscreenActive = !m.altscreenActive
cmd := tea.EnterAltScreen
if !m.altscreenActive {
cmd = tea.ExitAltScreen
}
return m, cmd
case "e":
return m, openEditor()
case "ctrl+c", "q":
return m, tea.Quit
}
case editorFinishedMsg:
if msg.err != nil {
m.err = msg.err
return m, tea.Quit
}
}
return m, nil
}

func (m model) View() string {
if m.err != nil {
return "Error: " + m.err.Error() + "\n"
}
return "Press 'e' to open your EDITOR.\nPress 'a' to toggle the altscreen\nPress 'q' to quit.\n"
}

func main() {
m := model{}
if err := tea.NewProgram(m).Start(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
}
8 changes: 7 additions & 1 deletion renderer.go
Expand Up @@ -15,7 +15,10 @@ type renderer interface {
// output at its discretion.
write(string)

// Request a full re-render.
// Request a full re-render. Note that this will not trigger a render
// immediately. Rather, this method causes the next render to be a full
// repaint. Because of this, it's safe to call this method multiple times
// in succession.
repaint()

// Whether or not the alternate screen buffer is enabled.
Expand All @@ -25,3 +28,6 @@ type renderer interface {
// does not actually toggle the alternate screen buffer.
setAltScreen(bool)
}

// repaintMsg forces a full repaint.
type repaintMsg struct{}
9 changes: 9 additions & 0 deletions screen.go
Expand Up @@ -42,3 +42,12 @@ func changeScrollingRegion(w io.Writer, top, bottom int) {
func cursorBack(w io.Writer, n int) {
fmt.Fprintf(w, te.CSI+te.CursorBackSeq, n)
}

func enterAltScreen(w io.Writer) {
fmt.Fprintf(w, te.CSI+te.AltScreenSeq)
moveCursor(w, 0, 0)
}

func exitAltScreen(w io.Writer) {
fmt.Fprintf(w, te.CSI+te.ExitAltScreenSeq)
}
5 changes: 5 additions & 0 deletions standard_renderer.go
Expand Up @@ -345,6 +345,11 @@ func (r *standardRenderer) insertBottom(lines []string, topBoundary, bottomBound
// handleMessages handles internal messages for the renderer.
func (r *standardRenderer) handleMessages(msg Msg) {
switch msg := msg.(type) {
case repaintMsg:
// Force a repaint by clearing the render cache as we slide into a
// render.
r.repaint()

case WindowSizeMsg:
r.mtx.Lock()
r.width = msg.Width
Expand Down