3ef6d864ef7378b64e56e088f07e6c52a7092f45

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
GitHub <noreply@github.com>
Date

Message

fix(ui): UI regressions  (#279)

* fix(backend): wrap i/o operations in transactions

when i/o errors, transaction rolls back

* fix(lint): inefficient assignment

* fix(ui): respect log settings

* fix(ui): truncate command string after yanking

* fix(git): make sure to use system git config in diff

* fix(ui): move support goto top/bottom in viewport

Diff

  1diff --git a/cmd/soft/root.go b/cmd/soft/root.go
  2index edd7c21492974d1923c7cf8a6967237fa24bc09b..1dc8e1883c9bac8c1b38b00c422cbc4bb7037532 100644
  3--- a/cmd/soft/root.go
  4+++ b/cmd/soft/root.go
  5@@ -4,11 +4,9 @@ import (
  6 	"context"
  7 	"os"
  8 	"runtime/debug"
  9-	"strconv"
 10-	"strings"
 11-	"time"
 12 
 13 	"github.com/charmbracelet/log"
 14+	. "github.com/charmbracelet/soft-serve/internal/log"
 15 	"github.com/spf13/cobra"
 16 )
 17 
 18@@ -53,28 +51,8 @@ func init() {
 19 }
 20 
 21 func main() {
 22-	ctx := context.Background()
 23-	logger := log.NewWithOptions(os.Stderr, log.Options{
 24-		ReportTimestamp: true,
 25-		TimeFormat:      time.DateOnly,
 26-	})
 27-	if debug, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_DEBUG")); debug {
 28-		logger.SetLevel(log.DebugLevel)
 29-	}
 30-	if tsfmt := os.Getenv("SOFT_SERVE_LOG_TIME_FORMAT"); tsfmt != "" {
 31-		logger.SetTimeFormat(tsfmt)
 32-	}
 33-
 34-	switch strings.ToLower(os.Getenv("SOFT_SERVE_LOG_FORMAT")) {
 35-	case "json":
 36-		logger.SetFormatter(log.JSONFormatter)
 37-	case "logfmt":
 38-		logger.SetFormatter(log.LogfmtFormatter)
 39-	case "text":
 40-		logger.SetFormatter(log.TextFormatter)
 41-	}
 42-
 43-	ctx = log.WithContext(ctx, logger)
 44+	logger := NewDefaultLogger()
 45+	ctx := log.WithContext(context.Background(), logger)
 46 	if err := rootCmd.ExecuteContext(ctx); err != nil {
 47 		os.Exit(1)
 48 	}
 49diff --git a/git/repo.go b/git/repo.go
 50index 050d3f67fd85a53766ce7b7de5f4311bc3563b9e..67a0e286d45ce438102a9e793665f86745c1be8e 100644
 51--- a/git/repo.go
 52+++ b/git/repo.go
 53@@ -147,7 +147,11 @@ func (r *Repository) TreePath(ref *Reference, path string) (*Tree, error) {
 54 
 55 // Diff returns the diff for the given commit.
 56 func (r *Repository) Diff(commit *Commit) (*Diff, error) {
 57-	ddiff, err := r.Repository.Diff(commit.Hash.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars)
 58+	ddiff, err := r.Repository.Diff(commit.Hash.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars, git.DiffOptions{
 59+		CommandOptions: git.CommandOptions{
 60+			Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
 61+		},
 62+	})
 63 	if err != nil {
 64 		return nil, err
 65 	}
 66diff --git a/internal/log/log.go b/internal/log/log.go
 67new file mode 100644
 68index 0000000000000000000000000000000000000000..a1184153b81da766f76fc10ca7f533c0a486b710
 69--- /dev/null
 70+++ b/internal/log/log.go
 71@@ -0,0 +1,39 @@
 72+package log
 73+
 74+import (
 75+	"os"
 76+	"strconv"
 77+	"strings"
 78+	"time"
 79+
 80+	"github.com/charmbracelet/log"
 81+)
 82+
 83+var contextKey = &struct{ string }{"logger"}
 84+
 85+// NewDefaultLogger returns a new logger with default settings.
 86+func NewDefaultLogger() *log.Logger {
 87+	logger := log.NewWithOptions(os.Stderr, log.Options{
 88+		ReportTimestamp: true,
 89+		TimeFormat:      time.DateOnly,
 90+	})
 91+
 92+	if debug, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_DEBUG")); debug {
 93+		logger.SetLevel(log.DebugLevel)
 94+	}
 95+
 96+	if tsfmt := os.Getenv("SOFT_SERVE_LOG_TIME_FORMAT"); tsfmt != "" {
 97+		logger.SetTimeFormat(tsfmt)
 98+	}
 99+
100+	switch strings.ToLower(os.Getenv("SOFT_SERVE_LOG_FORMAT")) {
101+	case "json":
102+		logger.SetFormatter(log.JSONFormatter)
103+	case "logfmt":
104+		logger.SetFormatter(log.LogfmtFormatter)
105+	case "text":
106+		logger.SetFormatter(log.TextFormatter)
107+	}
108+
109+	return logger
110+}
111diff --git a/server/git/git.go b/server/git/git.go
112index 0b14f0b5031901b2b4299d87981f43134509edb9..85e2cf068e5f24e141c0455db2d8a1a5aa01121c 100644
113--- a/server/git/git.go
114+++ b/server/git/git.go
115@@ -88,6 +88,7 @@ func RunGit(ctx context.Context, in io.Reader, out io.Writer, er io.Writer, dir
116 	c.Env = append(c.Env, "SOFT_SERVE_DEBUG="+os.Getenv("SOFT_SERVE_DEBUG"))
117 	if cfg != nil {
118 		c.Env = append(c.Env, "SOFT_SERVE_LOG_FORMAT="+cfg.LogFormat)
119+		c.Env = append(c.Env, "SOFT_SERVE_LOG_TIME_FORMAT="+cfg.LogTimeFormat)
120 	}
121 
122 	stdin, err := c.StdinPipe()
123diff --git a/server/ssh/session.go b/server/ssh/session.go
124index e24bdc83a4cd9d453ae0acf3793c6c459fa5c6a3..b39e15e24cc305c14eac1f59c777c0baa5f20f52 100644
125--- a/server/ssh/session.go
126+++ b/server/ssh/session.go
127@@ -4,6 +4,8 @@ import (
128 	"strings"
129 
130 	tea "github.com/charmbracelet/bubbletea"
131+	"github.com/charmbracelet/log"
132+	. "github.com/charmbracelet/soft-serve/internal/log"
133 	"github.com/charmbracelet/soft-serve/server/backend"
134 	"github.com/charmbracelet/soft-serve/server/config"
135 	"github.com/charmbracelet/soft-serve/server/errors"
136@@ -48,7 +50,9 @@ func SessionHandler(cfg *config.Config) bm.ProgramHandler {
137 
138 		envs := &sessionEnv{s}
139 		output := termenv.NewOutput(s, termenv.WithColorCache(true), termenv.WithEnvironment(envs))
140-		c := common.NewCommon(s.Context(), output, pty.Window.Width, pty.Window.Height)
141+		logger := NewDefaultLogger()
142+		ctx := log.WithContext(s.Context(), logger)
143+		c := common.NewCommon(ctx, output, pty.Window.Width, pty.Window.Height)
144 		c.SetValue(common.ConfigKey, cfg)
145 		m := ui.New(c, initialRepo)
146 		p := tea.NewProgram(m,
147diff --git a/server/ui/components/code/code.go b/server/ui/components/code/code.go
148index bf3fc4c4d080159e478aac05280d22631e97a9b7..8291183349ee1059b6baa703f692014f0c55952c 100644
149--- a/server/ui/components/code/code.go
150+++ b/server/ui/components/code/code.go
151@@ -6,7 +6,6 @@ import (
152 	"sync"
153 
154 	"github.com/alecthomas/chroma/lexers"
155-	"github.com/charmbracelet/bubbles/key"
156 	tea "github.com/charmbracelet/bubbletea"
157 	"github.com/charmbracelet/glamour"
158 	gansi "github.com/charmbracelet/glamour/ansi"
159@@ -99,18 +98,10 @@ func (r *Code) Init() tea.Cmd {
160 // Update implements tea.Model.
161 func (r *Code) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
162 	cmds := make([]tea.Cmd, 0)
163-	switch msg := msg.(type) {
164+	switch msg.(type) {
165 	case tea.WindowSizeMsg:
166 		// Recalculate content width and line wrap.
167 		cmds = append(cmds, r.Init())
168-	case tea.KeyMsg:
169-		// Viewport doesn't handle these keys, so we do it here.
170-		switch {
171-		case key.Matches(msg, r.common.KeyMap.GotoTop):
172-			r.GotoTop()
173-		case key.Matches(msg, r.common.KeyMap.GotoBottom):
174-			r.GotoBottom()
175-		}
176 	}
177 	v, cmd := r.Viewport.Update(msg)
178 	r.Viewport = v.(*vp.Viewport)
179diff --git a/server/ui/components/viewport/viewport.go b/server/ui/components/viewport/viewport.go
180index bdccc0f0ad5971938b6dc364b938e529c0c6248b..35fe8f24da7147bb2404d03af6fdeb6982c3514d 100644
181--- a/server/ui/components/viewport/viewport.go
182+++ b/server/ui/components/viewport/viewport.go
183@@ -1,6 +1,7 @@
184 package viewport
185 
186 import (
187+	"github.com/charmbracelet/bubbles/key"
188 	"github.com/charmbracelet/bubbles/viewport"
189 	tea "github.com/charmbracelet/bubbletea"
190 	"github.com/charmbracelet/soft-serve/server/ui/common"
191@@ -36,6 +37,15 @@ func (v *Viewport) Init() tea.Cmd {
192 
193 // Update implements tea.Model.
194 func (v *Viewport) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
195+	switch msg := msg.(type) {
196+	case tea.KeyMsg:
197+		switch {
198+		case key.Matches(msg, v.common.KeyMap.GotoTop):
199+			v.GotoTop()
200+		case key.Matches(msg, v.common.KeyMap.GotoBottom):
201+			v.GotoBottom()
202+		}
203+	}
204 	vp, cmd := v.Model.Update(msg)
205 	v.Model = &vp
206 	return v, cmd
207diff --git a/server/ui/pages/repo/log.go b/server/ui/pages/repo/log.go
208index 0b1480b466840e9f6f6fb9ca09c4fbdaed0adb2a..21bbcdf9fe46cf8fec7738168fcdbb50d2efd3b1 100644
209--- a/server/ui/pages/repo/log.go
210+++ b/server/ui/pages/repo/log.go
211@@ -105,6 +105,8 @@ func (l *Log) ShortHelp() []key.Binding {
212 		return []key.Binding{
213 			l.common.KeyMap.UpDown,
214 			l.common.KeyMap.BackItem,
215+			l.common.KeyMap.GotoTop,
216+			l.common.KeyMap.GotoBottom,
217 		}
218 	default:
219 		return []key.Binding{}
220@@ -151,6 +153,8 @@ func (l *Log) FullHelp() [][]key.Binding {
221 			{
222 				k.Down,
223 				k.Up,
224+				l.common.KeyMap.GotoTop,
225+				l.common.KeyMap.GotoBottom,
226 			},
227 		}...)
228 	}
229diff --git a/server/ui/pages/selection/item.go b/server/ui/pages/selection/item.go
230index bc06210738a1b4206ad794f53a86b0e5506ce864..6550497c78b954f03ce5dc9627aefce0d9ce66b4 100644
231--- a/server/ui/pages/selection/item.go
232+++ b/server/ui/pages/selection/item.go
233@@ -83,7 +83,7 @@ func (i Item) Title() string {
234 }
235 
236 // Description returns the item description. Implements list.DefaultItem.
237-func (i Item) Description() string { return i.repo.Description() }
238+func (i Item) Description() string { return strings.TrimSpace(i.repo.Description()) }
239 
240 // FilterValue implements list.Item.
241 func (i Item) FilterValue() string { return i.Title() }
242@@ -199,13 +199,16 @@ func (d *ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem lis
243 	s.WriteRune('\n')
244 	s.WriteString(desc)
245 	s.WriteRune('\n')
246-	cmd := common.TruncateString(i.Command(), m.Width()-styles.Base.GetHorizontalFrameSize())
247-	cmd = styles.Command.Render(cmd)
248+
249+	cmd := i.Command()
250+	cmdStyler := styles.Command.Render
251 	if d.copiedIdx == index {
252-		cmd += " " + styles.Desc.Render("(copied to clipboard)")
253+		cmd = "(copied to clipboard)"
254+		cmdStyler = styles.Desc.Render
255 		d.copiedIdx = -1
256 	}
257-	s.WriteString(cmd)
258+	cmd = common.TruncateString(cmd, m.Width()-styles.Base.GetHorizontalFrameSize())
259+	s.WriteString(cmdStyler(cmd))
260 	fmt.Fprint(w,
261 		d.common.Zone.Mark(i.ID(),
262 			styles.Base.Render(s.String()),