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:
parent
3ee6cbf514
commit
9267fc92a7
|
|
@ -126,12 +126,17 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
})
|
})
|
||||||
cm.Define("search", "Search for a cell", "", func(ctx *CommandContext) error {
|
cm.Define("search", "Search for a cell", "", func(ctx *CommandContext) error {
|
||||||
ctx.Frame().Prompt(PromptOptions{Prompt: "/"}, func(res string) error {
|
ctx.Frame().Prompt(PromptOptions{Prompt: "/"}, func(res string) error {
|
||||||
|
if res == "" {
|
||||||
|
ctx.Session().SetLastSearch(nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
re, err := regexp.Compile(res)
|
re, err := regexp.Compile(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid regexp: %v", err)
|
return fmt.Errorf("invalid regexp: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.session.LastSearch = re
|
ctx.Session().SetLastSearch(re)
|
||||||
return ctx.Session().Commands.Eval(ctx, "search-next")
|
return ctx.Session().Commands.Eval(ctx, "search-next")
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -252,6 +257,28 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
cm.Define("mark-row-red", "Set row marker to red", "", func(ctx *CommandContext) error {
|
cm.Define("mark-row-red", "Set row marker to red", "", func(ctx *CommandContext) error {
|
||||||
_, cellY := ctx.Frame().Grid().CellPosition()
|
_, 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 := ctx.ModelVC().RowAttrs(cellY)
|
||||||
attrs.Marker = MarkerRed
|
attrs.Marker = MarkerRed
|
||||||
ctx.ModelVC().SetRowAttrs(cellY, attrs)
|
ctx.ModelVC().SetRowAttrs(cellY, attrs)
|
||||||
|
|
@ -495,3 +522,28 @@ func gridNavOperation(op func(grid *ui.Grid)) func(ctx *CommandContext) error {
|
||||||
return nil
|
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
18
go.mod
|
|
@ -1,11 +1,21 @@
|
||||||
module github.com/lmika/ted
|
module github.com/lmika/ted
|
||||||
|
|
||||||
go 1.15
|
go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/tcell v1.4.0
|
github.com/gdamore/tcell v1.4.0
|
||||||
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe
|
github.com/lmika/shellwords v0.0.0-20140714114018-ce258dd729fe
|
||||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
github.com/stretchr/testify v1.7.5
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
)
|
||||||
github.com/stretchr/testify v1.7.5 // indirect
|
|
||||||
|
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
3
go.sum
|
|
@ -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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
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.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/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.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
|
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/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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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/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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|
|
||||||
1
main.go
1
main.go
|
|
@ -30,6 +30,7 @@ func main() {
|
||||||
frame := NewFrame(uiManager)
|
frame := NewFrame(uiManager)
|
||||||
session := NewSession(uiManager, frame, codecBuilder(flag.Arg(0)))
|
session := NewSession(uiManager, frame, codecBuilder(flag.Arg(0)))
|
||||||
session.LoadFromSource()
|
session.LoadFromSource()
|
||||||
|
session.ResizeToBestFit()
|
||||||
|
|
||||||
uiManager.SetRootComponent(frame.RootComponent())
|
uiManager.SetRootComponent(frame.RootComponent())
|
||||||
frame.enterMode(GridMode)
|
frame.enterMode(GridMode)
|
||||||
|
|
|
||||||
38
session.go
38
session.go
|
|
@ -6,6 +6,12 @@ import (
|
||||||
"github.com/lmika/ted/ui"
|
"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 session is responsible for managing the UI and the model and handling
|
||||||
// the interaction between the two and the user.
|
// the interaction between the two and the user.
|
||||||
type Session struct {
|
type Session struct {
|
||||||
|
|
@ -15,7 +21,7 @@ type Session struct {
|
||||||
Commands *CommandMapping
|
Commands *CommandMapping
|
||||||
UIManager *ui.Ui
|
UIManager *ui.Ui
|
||||||
modelController *ModelViewCtrl
|
modelController *ModelViewCtrl
|
||||||
pasteBoard RWModel
|
pasteBoard RWModel
|
||||||
|
|
||||||
LastSearch *regexp.Regexp
|
LastSearch *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +35,7 @@ func NewSession(uiManager *ui.Ui, frame *Frame, source ModelSource) *Session {
|
||||||
Commands: NewCommandMapping(),
|
Commands: NewCommandMapping(),
|
||||||
UIManager: uiManager,
|
UIManager: uiManager,
|
||||||
modelController: NewGridViewModel(model),
|
modelController: NewGridViewModel(model),
|
||||||
pasteBoard: NewSingleCellStdModel(),
|
pasteBoard: NewSingleCellStdModel(),
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.SetModel(&SessionGridModel{session.modelController})
|
frame.SetModel(&SessionGridModel{session.modelController})
|
||||||
|
|
@ -55,6 +61,23 @@ func (session *Session) LoadFromSource() {
|
||||||
session.modelController.SetModel(newModel)
|
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
|
// Input from the frame
|
||||||
func (session *Session) KeyPressed(key rune, mod int) {
|
func (session *Session) KeyPressed(key rune, mod int) {
|
||||||
// Add the mod key modifier
|
// 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
|
// The command context used by the session
|
||||||
type CommandContext struct {
|
type CommandContext struct {
|
||||||
session *Session
|
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) {
|
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)
|
rowAttrs := sgm.GridViewModel.RowAttrs(y)
|
||||||
colAttrs := sgm.GridViewModel.ColAttrs(y)
|
colAttrs := sgm.GridViewModel.ColAttrs(y)
|
||||||
|
|
||||||
|
|
|
||||||
12
viewmodel.go
12
viewmodel.go
|
|
@ -2,12 +2,14 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ModelViewCtrl struct {
|
type ModelViewCtrl struct {
|
||||||
model Model
|
model Model
|
||||||
rowAttrs []SliceAttr
|
rowAttrs []SliceAttr
|
||||||
colAttrs []SliceAttr
|
colAttrs []SliceAttr
|
||||||
|
lastSearchRegexp *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGridViewModel(model Model) *ModelViewCtrl {
|
func NewGridViewModel(model Model) *ModelViewCtrl {
|
||||||
|
|
@ -16,6 +18,10 @@ func NewGridViewModel(model Model) *ModelViewCtrl {
|
||||||
return gvm
|
return gvm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gvm *ModelViewCtrl) SetLastSearchRegexp(r *regexp.Regexp) {
|
||||||
|
gvm.lastSearchRegexp = r
|
||||||
|
}
|
||||||
|
|
||||||
func (gvm *ModelViewCtrl) Model() Model {
|
func (gvm *ModelViewCtrl) Model() Model {
|
||||||
return gvm.model
|
return gvm.model
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue