Added 'each-row' and 'to-upper' commands
This commit is contained in:
parent
5424a6b927
commit
3ee6cbf514
|
|
@ -55,6 +55,8 @@ Others:
|
||||||
| `}` | Increase cell width |
|
| `}` | Increase cell width |
|
||||||
| `/` | Search for cell matching regular expression |
|
| `/` | Search for cell matching regular expression |
|
||||||
| `n` | Find next cell matching search |
|
| `n` | Find next cell matching search |
|
||||||
|
| `y` | Copy cell value |
|
||||||
|
| `p` | Paste cell value |
|
||||||
| `:` | Enter command |
|
| `:` | Enter command |
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
@ -62,7 +64,7 @@ Others:
|
||||||
Commands can be entered by pressing `:` and typing in the command or alias.
|
Commands can be entered by pressing `:` and typing in the command or alias.
|
||||||
|
|
||||||
| Command | Alias | Description |
|
| Command | Alias | Description |
|
||||||
|:----------------|:-----------|:------------------------|
|
|:----------------------|:-----------|:------------------------|
|
||||||
| `save` | `w` | Save the current file. |
|
| `save` | `w` | Save the current file. |
|
||||||
| `quit` | `q` | Quit the application without saving changes. |
|
| `quit` | `q` | Quit the application without saving changes. |
|
||||||
| `save-and-quit` | `wq` | Save the current file and quit the application. |
|
| `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. |
|
| `open-right` | | Insert a new column to the right of the currently selected column. |
|
||||||
| `delete-row` | | Delete the currently selected row. |
|
| `delete-row` | | Delete the currently selected row. |
|
||||||
| `delete-column` | | Delete the currently selected column. |
|
| `delete-column` | | Delete the currently selected column. |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/lmika/shellwords"
|
"github.com/lmika/shellwords"
|
||||||
|
|
||||||
|
|
@ -67,12 +68,16 @@ func (cm *CommandMapping) Eval(ctx *CommandContext, expr string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := cm.Commands[toks[0]]
|
return cm.Invoke(ctx, toks[0], toks[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *CommandMapping) Invoke(ctx *CommandContext, name string, args []string) error {
|
||||||
|
cmd := cm.Commands[name]
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
return cmd.Do(ctx.WithArgs(toks[1:]))
|
return cmd.Do(ctx.WithArgs(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("no such command: %v", expr)
|
return fmt.Errorf("no such command: %v", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registers the standard view navigation commands. These commands require the frame
|
// Registers the standard view navigation commands. These commands require the frame
|
||||||
|
|
@ -345,8 +350,65 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
return nil
|
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 {
|
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 {
|
if !isWSource {
|
||||||
return fmt.Errorf("model is not writable")
|
return fmt.Errorf("model is not writable")
|
||||||
}
|
}
|
||||||
|
|
@ -356,6 +418,7 @@ func (cm *CommandMapping) RegisterViewCommands() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Frame().Message("Wrote " + wSource.String())
|
ctx.Frame().Message("Wrote " + wSource.String())
|
||||||
|
ctx.Session().Source = wSource
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
3
main.go
3
main.go
|
|
@ -46,4 +46,7 @@ var codecModelSourceBuilders = map[string]codecModelSourceBuilder{
|
||||||
"tsv": func(filename string) ModelSource {
|
"tsv": func(filename string) ModelSource {
|
||||||
return NewCsvFileModelSource(filename, CsvFileModelSourceOptions{Comma: '\t'})
|
return NewCsvFileModelSource(filename, CsvFileModelSourceOptions{Comma: '\t'})
|
||||||
},
|
},
|
||||||
|
"jira": func(filename string) ModelSource {
|
||||||
|
return JiraTableModelSource{Filename: filename, Header: true}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ModelSource is a source of models. At a minimum, it must be able to read models.
|
// 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()
|
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