Skip to content

Commit

Permalink
Render ASCII-only spinner and icons on conhost.exe
Browse files Browse the repository at this point in the history
Fixes #338
  • Loading branch information
DavidS-ovm committed Jun 14, 2024
1 parent 952ef93 commit 3c32ab3
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 21 deletions.
5 changes: 2 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (

"connectrpc.com/connect"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/google/uuid"
"github.com/overmindtech/cli/tracing"
"github.com/overmindtech/sdp-go"
Expand Down Expand Up @@ -238,9 +237,9 @@ func (m authenticateModel) View() string {
output = markdownToString(m.width, prompt)

case Authenticated:
output = wrap(lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎")+" Authenticated successfully. Press any key to continue.", m.width-4, 2)
output = wrap(RenderOk()+" Authenticated successfully. Press any key to continue.", m.width-4, 2)
case ErrorAuthenticating:
output = wrap(lipgloss.NewStyle().Foreground(ColorPalette.BgDanger).Render("✗")+" Unable to authenticate. Please try again.", m.width-4, 2)
output = wrap(RenderErr()+" Unable to authenticate. Please try again.", m.width-4, 2)
}

return containerStyle.Render(output)
Expand Down
7 changes: 3 additions & 4 deletions cmd/tea_initialisesources.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"connectrpc.com/connect"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
"github.com/overmindtech/sdp-go"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
Expand Down Expand Up @@ -271,7 +270,7 @@ func (m initialiseSourcesModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m initialiseSourcesModel) View() string {
bits := []string{m.taskModel.View()}
for _, hint := range m.errorHints {
bits = append(bits, wrap(fmt.Sprintf(" %v %v", lipgloss.NewStyle().Foreground(ColorPalette.BgDanger).Render("✗"), hint), m.width, 2))
bits = append(bits, wrap(fmt.Sprintf(" %v %v", RenderErr(), hint), m.width, 2))
}
if m.awsConfigForm != nil && !m.awsConfigFormDone {
bits = append(bits, m.awsConfigForm.View())
Expand All @@ -280,10 +279,10 @@ func (m initialiseSourcesModel) View() string {
bits = append(bits, m.profileInputForm.View())
}
if m.awsSourceRunning {
bits = append(bits, wrap(fmt.Sprintf(" %v AWS Source: running", lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎")), m.width, 4))
bits = append(bits, wrap(fmt.Sprintf(" %v AWS Source: running", RenderOk()), m.width, 4))
}
if m.stdlibSourceRunning {
bits = append(bits, wrap(fmt.Sprintf(" %v stdlib Source: running", lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎")), m.width, 4))
bits = append(bits, wrap(fmt.Sprintf(" %v stdlib Source: running", RenderOk()), m.width, 4))
}
return strings.Join(bits, "\n")
}
Expand Down
3 changes: 1 addition & 2 deletions cmd/tea_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/overmindtech/cli/tracing"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -131,7 +130,7 @@ func (m runPlanModel) View() string {
case taskStatusPending, taskStatusRunning:
bits = append(bits,
wrap(fmt.Sprintf("%v Running 'terraform %v'",
lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎"),
RenderOk(),
strings.Join(m.args, " "),
), m.width, 2))
case taskStatusDone:
Expand Down
6 changes: 2 additions & 4 deletions cmd/tea_submitplan.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,18 +211,16 @@ func (m submitPlanModel) View() string {
if m.resourceExtractionTask.status != taskStatusPending {
bits = append(bits, m.resourceExtractionTask.View())
if m.mappedItemDiffs.numTotalChanges > 0 {
greenTick := lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎")
supportedTypes := maps.Keys(m.mappedItemDiffs.supported)
slices.Sort[[]string](supportedTypes)
for _, typ := range supportedTypes {
bits = append(bits, fmt.Sprintf(" %v %v (%v)", greenTick, typ, len(m.mappedItemDiffs.supported[typ])))
bits = append(bits, fmt.Sprintf(" %v %v (%v)", RenderOk(), typ, len(m.mappedItemDiffs.supported[typ])))
}

yellowCross := lipgloss.NewStyle().Foreground(ColorPalette.BgWarning).Render("✗")
unsupportedTypes := maps.Keys(m.mappedItemDiffs.unsupported)
slices.Sort[[]string](unsupportedTypes)
for _, typ := range unsupportedTypes {
bits = append(bits, fmt.Sprintf(" %v %v (%v)", yellowCross, typ, len(m.mappedItemDiffs.unsupported[typ])))
bits = append(bits, fmt.Sprintf(" %v %v (%v)", RenderErr(), typ, len(m.mappedItemDiffs.unsupported[typ])))
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/tea_taskmodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func NewTaskModel(title string, width int) taskModel {
status: taskStatusPending,
title: title,
spinner: spinner.New(
spinner.WithSpinner(DotsSpinner),
spinner.WithSpinner(PlatformSpinner()),
spinner.WithStyle(lipgloss.NewStyle().Foreground(ColorPalette.BgMain)),
),
width: width,
Expand Down Expand Up @@ -118,9 +118,9 @@ func (m taskModel) View() string {
case taskStatusRunning:
label = m.spinner.View()
case taskStatusDone:
label = lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎")
label = RenderOk()
case taskStatusError:
label = lipgloss.NewStyle().Foreground(ColorPalette.BgDanger).Render("✗")
label = RenderErr()
case taskStatusSkipped:
label = lipgloss.NewStyle().Foreground(ColorPalette.LabelFaint).Render("-")
default:
Expand Down
5 changes: 2 additions & 3 deletions cmd/terraform_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"connectrpc.com/connect"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
"github.com/google/uuid"
"github.com/overmindtech/cli/tracing"
"github.com/overmindtech/sdp-go"
Expand Down Expand Up @@ -404,15 +403,15 @@ func (m tfApplyModel) View() string {
if m.runTfApply {
bits = append(bits,
wrap(fmt.Sprintf("%v Running 'terraform %v'",
lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎"),
RenderOk(),
strings.Join(m.args, " "),
), m.width, 2))
}

if m.isEnding && m.endingChangeSnapshot.overall.status != taskStatusPending {
bits = append(bits,
wrap(fmt.Sprintf("%v Ran 'terraform %v'",
lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render("✔︎"),
RenderOk(),
strings.Join(m.args, " "),
), m.width, 2))
bits = append(bits, m.endingChangeSnapshot.View())
Expand Down
4 changes: 2 additions & 2 deletions cmd/terraform_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,10 @@ func mappedItemDiffsFromPlan(ctx context.Context, planJson []byte, fileName stri

// Log the types
for typ, plannedChanges := range plannedChangeGroupsVar.supported {
log.WithContext(ctx).Infof(Green.Color(" %v (%v)"), typ, len(plannedChanges))
log.WithContext(ctx).Infof(" %v %v (%v)", RenderOk(), typ, len(plannedChanges))
}
for typ, plannedChanges := range plannedChangeGroupsVar.unsupported {
log.WithContext(ctx).Infof(Yellow.Color(" %v (%v)"), typ, len(plannedChanges))
log.WithContext(ctx).Infof(" %v %v (%v)", RenderErr(), typ, len(plannedChanges))
}

return removedSecrets, plannedChangeGroupsVar.MappedItemDiffs(), mappedItemDiffsMsg{
Expand Down
23 changes: 23 additions & 0 deletions cmd/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,26 @@ func wrap(s string, width, indent int) string {

return strings.ReplaceAll(wordwrap.String(s, width-indent), "\n", "\n"+strings.Repeat(" ", indent))
}

func RenderOk() string {
checkMark := "✔︎"
if IsConhost() {
checkMark = "OK"
}
return lipgloss.NewStyle().Foreground(ColorPalette.BgSuccess).Render(checkMark)
}

func RenderErr() string {
checkMark := "✗"
if IsConhost() {
checkMark = "ERR"
}
return lipgloss.NewStyle().Foreground(ColorPalette.BgDanger).Render(checkMark)
}

func PlatformSpinner() spinner.Spinner {
if IsConhost() {
return spinner.Line
}
return DotsSpinner
}
8 changes: 8 additions & 0 deletions cmd/theme_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cmd

// IsConhost returns true if the current terminal is conhost. This indicates
// that it can't deal with multi-byte characters and requires special treatment.
// See https://github.com/overmindtech/cli/issues/388 for detailed analysis.
func IsConhost() bool {
return false
}
51 changes: 51 additions & 0 deletions cmd/theme_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cmd

import (
"bytes"
"os"
"sync"
)

var isWslCache int // 0 = unset; 1 = WSL; 2 = not WSL
var isWslCacheMu sync.RWMutex

// IsConhost returns true if the current terminal is conhost. This indicates
// that it can't deal with multi-byte characters and requires special treatment.
// See https://github.com/overmindtech/cli/issues/388 for detailed analysis.
func IsConhost() bool {
// shortcut this if we (probably) run in Windows Terminal (through WSL) or
// on something that smells like a regular Linux terminal
if os.Getenv("WT_SESSION") != "" {
return false
}

isWslCacheMu.RLock()
w := isWslCache
isWslCacheMu.RUnlock()

if w == 1 {
return true
} else if w == 2 {
return false
}

// isWslCache has not yet been initialised, so we need to check if we are in WSL
// since we don't know if we are in WSL, we need to check now
isWslCacheMu.Lock()
defer isWslCacheMu.Unlock()
if w != 0 {
// someone else raced the lock and has already decided
return isWslCache == 1
}

// check if we run in WSL
ver, err := os.ReadFile("/proc/version")
if err == nil && bytes.Contains(ver, []byte("Microsoft")) {
isWslCache = 1
return true
}

// we can't access /proc/version or it does not contain Microsoft, we are _probably_ not in WSL
isWslCache = 2
return false
}
10 changes: 10 additions & 0 deletions cmd/theme_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cmd

import "os"

// IsConhost returns true if the current terminal is conhost. This indicates
// that it can't deal with multi-byte characters and requires special treatment.
// See https://github.com/overmindtech/cli/issues/388 for detailed analysis.
func IsConhost() bool {
return os.Getenv("WT_SESSION") == ""
}

0 comments on commit 3c32ab3

Please sign in to comment.