Added 'each-row' and 'to-upper' commands
This commit is contained in:
parent
5424a6b927
commit
3ee6cbf514
|
|
@ -55,6 +55,8 @@ Others:
|
|||
| `}` | Increase cell width |
|
||||
| `/` | Search for cell matching regular expression |
|
||||
| `n` | Find next cell matching search |
|
||||
| `y` | Copy cell value |
|
||||
| `p` | Paste cell value |
|
||||
| `:` | Enter command |
|
||||
|
||||
## Commands
|
||||
|
|
@ -62,7 +64,7 @@ Others:
|
|||
Commands can be entered by pressing `:` and typing in the command or alias.
|
||||
|
||||
| Command | Alias | Description |
|
||||
|:----------------|:-----------|:------------------------|
|
||||
|:----------------------|:-----------|:------------------------|
|
||||
| `save` | `w` | Save the current file. |
|
||||
| `quit` | `q` | Quit the application without saving changes. |
|
||||
| `save-and-quit` | `wq` | Save the current file and quit the application. |
|
||||
|
|
@ -70,3 +72,4 @@ Commands can be entered by pressing `:` and typing in the command or alias.
|
|||
| `open-right` | | Insert a new column to the right of the currently selected column. |
|
||||
| `delete-row` | | Delete the currently selected row. |
|
||||
| `delete-column` | | Delete the currently selected column. |
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/lmika/shellwords"
|
||||
|
||||
|
|
@ -67,12 +68,16 @@ func (cm *CommandMapping) Eval(ctx *CommandContext, expr string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
cmd := cm.Commands[toks[0]]
|
||||
if cmd != nil {
|
||||
return cmd.Do(ctx.WithArgs(toks[1:]))
|
||||
return cm.Invoke(ctx, toks[0], toks[1:])
|
||||
}
|
||||
|
||||
return fmt.Errorf("no such command: %v", expr)
|
||||
func (cm *CommandMapping) Invoke(ctx *CommandContext, name string, args []string) error {
|
||||
cmd := cm.Commands[name]
|
||||
if cmd != nil {
|
||||
return cmd.Do(ctx.WithArgs(args))
|
||||
}
|
||||
|
||||
return fmt.Errorf("no such command: %v", name)
|
||||
}
|
||||
|
||||
// Registers the standard view navigation commands. These commands require the frame
|
||||
|
|
@ -345,8 +350,65 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
|||
return nil
|
||||
})
|
||||
|
||||
cm.Define("to-upper", "Convert cell value to uppercase", "", func(ctx *CommandContext) error {
|
||||
grid := ctx.Frame().Grid()
|
||||
cellX, cellY := grid.CellPosition()
|
||||
|
||||
// TODO: allow ranges
|
||||
|
||||
if _, isRwModel := ctx.ModelVC().Model().(RWModel); !isRwModel {
|
||||
return errors.New("Model is read-only")
|
||||
}
|
||||
|
||||
currentValue := ctx.ModelVC().Model().CellValue(cellY, cellX)
|
||||
newValue := strings.ToUpper(currentValue)
|
||||
if err := ctx.ModelVC().SetCellValue(cellY, cellX, newValue); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
cm.Define("each-row", "Executes the command for each row in the column", "", func(ctx *CommandContext) error {
|
||||
if len(ctx.args) != 1 {
|
||||
return errors.New("Sub-command required")
|
||||
}
|
||||
|
||||
grid := ctx.Frame().Grid()
|
||||
rows, _ := ctx.ModelVC().Model().Dimensions()
|
||||
|
||||
cellX, cellY := grid.CellPosition()
|
||||
defer grid.MoveTo(cellX, cellY)
|
||||
|
||||
subCommand := ctx.args
|
||||
|
||||
for r := 0; r < rows; r++ {
|
||||
grid.MoveTo(cellX, r)
|
||||
|
||||
if err := ctx.Session().Commands.Invoke(ctx, subCommand[0], subCommand[1:]); err != nil {
|
||||
return fmt.Errorf("at [%d, %d]: %v", cellX, r, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
cm.Define("save", "Save current file", "", func(ctx *CommandContext) error {
|
||||
wSource, isWSource := ctx.Session().Source.(WritableModelSource)
|
||||
var source ModelSource
|
||||
if len(ctx.args) >= 2 {
|
||||
targetCodecName := ctx.args[0]
|
||||
codecBuilder, hasCodec := codecModelSourceBuilders[targetCodecName]
|
||||
if !hasCodec {
|
||||
return fmt.Errorf("unrecognsed codec: %v", targetCodecName)
|
||||
}
|
||||
|
||||
targetFilename := ctx.args[1]
|
||||
source = codecBuilder(targetFilename)
|
||||
} else {
|
||||
source = ctx.Session().Source
|
||||
}
|
||||
|
||||
wSource, isWSource := source.(WritableModelSource)
|
||||
if !isWSource {
|
||||
return fmt.Errorf("model is not writable")
|
||||
}
|
||||
|
|
@ -356,6 +418,7 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
|||
}
|
||||
|
||||
ctx.Frame().Message("Wrote " + wSource.String())
|
||||
ctx.Session().Source = wSource
|
||||
return nil
|
||||
})
|
||||
|
||||
|
|
|
|||
3
main.go
3
main.go
|
|
@ -46,4 +46,7 @@ var codecModelSourceBuilders = map[string]codecModelSourceBuilder{
|
|||
"tsv": func(filename string) ModelSource {
|
||||
return NewCsvFileModelSource(filename, CsvFileModelSourceOptions{Comma: '\t'})
|
||||
},
|
||||
"jira": func(filename string) ModelSource {
|
||||
return JiraTableModelSource{Filename: filename, Header: true}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ModelSource is a source of models. At a minimum, it must be able to read models.
|
||||
|
|
@ -108,3 +111,55 @@ func (s CsvFileModelSource) Write(m Model) error {
|
|||
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
type JiraTableModelSource struct {
|
||||
Filename string
|
||||
Header bool
|
||||
}
|
||||
|
||||
func (s JiraTableModelSource) String() string {
|
||||
return filepath.Base(s.Filename)
|
||||
}
|
||||
|
||||
func (JiraTableModelSource) Read() (Model, error) {
|
||||
return nil, errors.New("read not supported yet")
|
||||
}
|
||||
|
||||
func (s JiraTableModelSource) Write(m Model) error {
|
||||
f, err := os.Create(s.Filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
rows, cols := m.Dimensions()
|
||||
line := new(strings.Builder)
|
||||
|
||||
for r := 0; r < rows; r++ {
|
||||
sep := "|"
|
||||
if r == 0 && s.Header {
|
||||
sep = "||"
|
||||
}
|
||||
|
||||
line.Reset()
|
||||
line.WriteString(sep)
|
||||
line.WriteRune(' ')
|
||||
|
||||
for c := 0; c < cols; c++ {
|
||||
if c >= 1 {
|
||||
line.WriteRune(' ')
|
||||
line.WriteString(sep)
|
||||
line.WriteRune(' ')
|
||||
}
|
||||
line.WriteString(m.CellValue(r, c))
|
||||
}
|
||||
|
||||
line.WriteRune(' ')
|
||||
line.WriteString(sep)
|
||||
if _, err := fmt.Fprintln(f, line.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue