A few minor feature

Added automatic resizing of the columns when loading a model
Added colouring of the currently search for cell
This commit is contained in:
Leon Mika 2024-09-24 12:45:22 +10:00
parent 3ee6cbf514
commit 9267fc92a7
6 changed files with 114 additions and 12 deletions

View file

@ -126,12 +126,17 @@ func (cm *CommandMapping) RegisterViewCommands() {
})
cm.Define("search", "Search for a cell", "", func(ctx *CommandContext) error {
ctx.Frame().Prompt(PromptOptions{Prompt: "/"}, func(res string) error {
if res == "" {
ctx.Session().SetLastSearch(nil)
return nil
}
re, err := regexp.Compile(res)
if err != nil {
return fmt.Errorf("invalid regexp: %v", err)
}
ctx.session.LastSearch = re
ctx.Session().SetLastSearch(re)
return ctx.Session().Commands.Eval(ctx, "search-next")
})
return nil
@ -252,6 +257,28 @@ func (cm *CommandMapping) RegisterViewCommands() {
cm.Define("mark-row-red", "Set row marker to red", "", func(ctx *CommandContext) error {
_, cellY := ctx.Frame().Grid().CellPosition()
namedArgs, _ := extractNamedArgs(ctx.args)
if rowMatch, hasRowMatch := namedArgs["row-match"]; hasRowMatch {
rows, cols := ctx.ModelVC().Model().Dimensions()
for r := 0; r < rows; r++ {
rowMatches := false
for c := 0; c < cols; c++ {
cellVal := ctx.ModelVC().Model().CellValue(r, c)
if cellVal == rowMatch {
rowMatches = true
break
}
}
if rowMatches {
attrs := ctx.ModelVC().RowAttrs(r)
attrs.Marker = MarkerRed
ctx.ModelVC().SetRowAttrs(r, attrs)
}
}
return nil
}
attrs := ctx.ModelVC().RowAttrs(cellY)
attrs.Marker = MarkerRed
ctx.ModelVC().SetRowAttrs(cellY, attrs)
@ -495,3 +522,28 @@ func gridNavOperation(op func(grid *ui.Grid)) func(ctx *CommandContext) error {
return nil
}
}
// extractNamedArgs will extract out any arguments of the form '-name value' and stick it in a map.
// Any non-named arguments will be extracted out as positional. If an argument only has a name, it
// will be added to the map with the empty string as the value.
func extractNamedArgs(args []string) (namedArgs map[string]string, positionalArgs []string) {
for len(args) > 0 {
if args[0][0] == '-' {
if namedArgs == nil {
namedArgs = make(map[string]string)
}
if len(args) >= 2 && args[1][0] != '-' {
namedArgs[args[0][1:]] = args[1]
args = args[2:]
} else {
namedArgs[args[0][1:]] = ""
args = args[1:]
}
} else {
positionalArgs = append(positionalArgs, args[0])
args = args[1:]
}
}
return namedArgs, positionalArgs
}

18
go.mod
View file

@ -1,11 +1,21 @@
module github.com/lmika/ted
go 1.15
go 1.22
require (
github.com/gdamore/tcell v1.4.0
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe
github.com/mattn/go-runewidth v0.0.10 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/stretchr/testify v1.7.5 // indirect
github.com/stretchr/testify v1.7.5
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
github.com/mattn/go-runewidth v0.0.10 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 // indirect
golang.org/x/text v0.3.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

3
go.sum
View file

@ -14,12 +14,10 @@ github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRR
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
@ -28,6 +26,7 @@ golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeo
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View file

@ -30,6 +30,7 @@ func main() {
frame := NewFrame(uiManager)
session := NewSession(uiManager, frame, codecBuilder(flag.Arg(0)))
session.LoadFromSource()
session.ResizeToBestFit()
uiManager.SetRootComponent(frame.RootComponent())
frame.enterMode(GridMode)

View file

@ -6,6 +6,12 @@ import (
"github.com/lmika/ted/ui"
)
const (
bestFixRightMargin = 2
bestFixMinColumnSize = 8
bestFixMaxColumnSize = 32
)
// The session is responsible for managing the UI and the model and handling
// the interaction between the two and the user.
type Session struct {
@ -15,7 +21,7 @@ type Session struct {
Commands *CommandMapping
UIManager *ui.Ui
modelController *ModelViewCtrl
pasteBoard RWModel
pasteBoard RWModel
LastSearch *regexp.Regexp
}
@ -29,7 +35,7 @@ func NewSession(uiManager *ui.Ui, frame *Frame, source ModelSource) *Session {
Commands: NewCommandMapping(),
UIManager: uiManager,
modelController: NewGridViewModel(model),
pasteBoard: NewSingleCellStdModel(),
pasteBoard: NewSingleCellStdModel(),
}
frame.SetModel(&SessionGridModel{session.modelController})
@ -55,6 +61,23 @@ func (session *Session) LoadFromSource() {
session.modelController.SetModel(newModel)
}
func (sesion *Session) ResizeToBestFit() {
mr, mc := sesion.model.Dimensions()
colMaxSize := make([]int, mc)
for c := 0; c < mc; c++ {
colMaxSize[c] = bestFixMinColumnSize
for r := 0; r < mr; r++ {
colMaxSize[c] = max(colMaxSize[c], len(sesion.model.CellValue(r, c))+bestFixRightMargin)
}
}
for c, ml := range colMaxSize {
sesion.modelController.SetColAttrs(c, SliceAttr{
Size: min(ml, bestFixMaxColumnSize),
})
}
}
// Input from the frame
func (session *Session) KeyPressed(key rune, mod int) {
// Add the mod key modifier
@ -71,6 +94,11 @@ func (session *Session) KeyPressed(key rune, mod int) {
}
}
func (s *Session) SetLastSearch(re *regexp.Regexp) {
s.LastSearch = re
s.modelController.lastSearchRegexp = re
}
// The command context used by the session
type CommandContext struct {
session *Session
@ -132,6 +160,12 @@ func (sgm *SessionGridModel) CellValue(x int, y int) string {
}
func (sgm *SessionGridModel) CellAttributes(x int, y int) (fg, bg ui.Attribute) {
if sgm.GridViewModel.lastSearchRegexp != nil {
if sgm.GridViewModel.lastSearchRegexp.MatchString(sgm.GridViewModel.Model().CellValue(y, x)) {
return ui.ColorMagenta, 0
}
}
rowAttrs := sgm.GridViewModel.RowAttrs(y)
colAttrs := sgm.GridViewModel.ColAttrs(y)

View file

@ -2,12 +2,14 @@ package main
import (
"errors"
"regexp"
)
type ModelViewCtrl struct {
model Model
rowAttrs []SliceAttr
colAttrs []SliceAttr
model Model
rowAttrs []SliceAttr
colAttrs []SliceAttr
lastSearchRegexp *regexp.Regexp
}
func NewGridViewModel(model Model) *ModelViewCtrl {
@ -16,6 +18,10 @@ func NewGridViewModel(model Model) *ModelViewCtrl {
return gvm
}
func (gvm *ModelViewCtrl) SetLastSearchRegexp(r *regexp.Regexp) {
gvm.lastSearchRegexp = r
}
func (gvm *ModelViewCtrl) Model() Model {
return gvm.model
}