2
1
Fork 0

Adds init command

This commit is contained in:
sloum 2021-03-27 19:57:29 -07:00
parent 18ee12f21c
commit 4379ef12de
4 changed files with 163 additions and 2 deletions

View File

@ -86,6 +86,31 @@ This tree style listing shows commands and their sub-commands. Anything inside o
- `task` - Always refers to the currently selected story
- `{task number}`
- `quit`
- `regex` - Uses golang style regular expressions
- `board`
- `title`
- Expression in the form: `/[match expression]/[replacement]/`
- `lane`
- `title`
- Expression in the form: `/[match expression]/[replacement]/`
- `lanes` - Applies the expression to all lanes, abbreviating `lanes` is not allowed
- `title`
- Expression in the form: `/[match expression]/[replacement]/`
- `story`
- `title`
- Expression in the form: `/[match expression]/[replacement]/`
- `description`
- Expression in the form: `/[match expression]/[replacement]/`
- `stories` - Applies the expression to all stories in the current lane, abbreviating `stories` is not allowed
- `title`
- Expression in the form: `/[match expression]/[replacement]/`
- `description`
- Expression in the form: `/[match expression]/[replacement]/`
- `stories!` - Applies the expression to all stories on the board (use with care), abbreviating `stories!` is not allowed
- `title`
- Expression in the form: `/[match expression]/[replacement]/`
- `description`
- Expression in the form: `/[match expression]/[replacement]/`
- `set`
- `board`
- `title`

111
board.go
View File

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"reflect"
"regexp"
"strings"
"tildegit.org/sloum/swim/termios"
"time"
@ -223,6 +224,12 @@ func (b *Board) EnterCommand() {
if mainCom == "wq" {
Quit()
}
case "regex", "re", "regexp":
if len(f) < 4 {
b.SetMessage("Not enough arguments: regex [target] [location] [/pattern/replacement/]", true)
return
}
b.Regex(f[1:])
case "q", "quit":
Quit()
case "c", "create":
@ -497,6 +504,110 @@ func (b *Board) ViewStory() {
}
}
func (b *Board) Regex(args []string) {
target := strings.ToLower(args[0])
location := strings.ToLower(args[1])
exp := strings.Join(args[2:], " ")
if !strings.HasPrefix(exp, "/") {
b.SetMessage("Invalid expression, must start with \"/\"", true)
return
}
if len(exp) < 5 {
b.SetMessage("Invalid expression", true)
return
}
exp = strings.Replace(exp, "\\/", "~@!", -1)
findReplaceFlag := strings.Split(exp[1:], "/")
if len(findReplaceFlag) < 2 {
b.SetMessage("Invalid expression, no replacement value", true)
return
}
for i := range findReplaceFlag {
findReplaceFlag[i] = strings.Replace(findReplaceFlag[i], "~@!", "\\/", -1)
}
re, err := regexp.Compile(findReplaceFlag[0])
if err != nil {
b.SetMessage(err.Error(), true)
return
}
var src string
switch target {
case "board", "b", "bo", "boa", "boar":
if !strings.HasPrefix(location, "t") {
b.SetMessage(fmt.Sprintf("Invalid location, %q, for %s", location, target), true)
return
}
src = b.Title
b.Title = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
case "lanes":
if !strings.HasPrefix(location, "t") {
b.SetMessage(fmt.Sprintf("Invalid location, %q, for %s", location, target), true)
return
}
for i := range b.Lanes {
src = b.Lanes[i].Title
b.Lanes[i].Title = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
}
case "lane", "l", "la", "lan":
if !strings.HasPrefix(location, "t") {
b.SetMessage(fmt.Sprintf("Invalid location, %q, for %s", location, target), true)
return
}
src = b.Lanes[b.Current].Title
b.Lanes[b.Current].Title = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
case "stories!":
for l := range b.Lanes {
for i := range b.Lanes[l].Stories {
switch location {
case "title", "t", "ti", "tit", "titl":
src = b.Lanes[l].Stories[i].Title
b.Lanes[l].Stories[i].Title = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
case "description", "desc", "d", "de":
src = b.Lanes[l].Stories[i].Body
b.Lanes[l].Stories[i].Body = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
default:
b.SetMessage(fmt.Sprintf("Invalid location, %q, for %s", location, target), true)
return
}
}
}
case "stories":
for i := range b.Lanes[b.Current].Stories {
switch location {
case "title", "t", "ti", "tit", "titl":
src = b.Lanes[b.Current].Stories[i].Title
b.Lanes[b.Current].Stories[i].Title = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
case "description", "desc", "d", "de":
src = b.Lanes[b.Current].Stories[i].Body
b.Lanes[b.Current].Stories[i].Body = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
default:
b.SetMessage(fmt.Sprintf("Invalid location, %q, for %s", location, target), true)
return
}
}
case "story", "s", "st", "sto", "stor":
switch location {
case "title", "t", "ti", "tit", "titl":
src = b.Lanes[b.Current].Stories[b.Lanes[b.Current].Current].Title
b.Lanes[b.Current].Stories[b.Lanes[b.Current].Current].Title = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
case "description", "desc", "d", "de":
src = b.Lanes[b.Current].Stories[b.Lanes[b.Current].Current].Body
b.Lanes[b.Current].Stories[b.Lanes[b.Current].Current].Body = re.ReplaceAllLiteralString(src, findReplaceFlag[1])
default:
b.SetMessage(fmt.Sprintf("Invalid location, %q, for %s", location, target), true)
return
}
default:
b.SetMessage(fmt.Sprintf("Invalid target %q", target), true)
return
}
if b.StoryOpen && strings.HasPrefix(target, "s") {
b.Lanes[b.Current].Stories[b.Lanes[b.Current].Current].BuildStorySlice(b.width)
b.Lanes[b.Current].Stories[b.Lanes[b.Current].Current].Draw(b)
}
b.SetMessage("Regular expression applied", false)
}
func (b Board) Draw() {
var out strings.Builder
out.WriteString(cursorHome)

25
main.go
View File

@ -153,7 +153,7 @@ func WrapText(text string, lineWidth int) string {
func Quit() {
termios.Restore()
fmt.Print(cursorEnd)
fmt.Print("\033[0m\n\033[?25h")
fmt.Print("\033[0m\033[?25h")
os.Exit(0)
}
@ -206,6 +206,23 @@ func handleSignals(c <-chan os.Signal) {
}
}
func InitNewBoard() {
b := DefaultBoard(80,30)
b.CreateLane("Backlog")
b.CreateLane("Active")
b.CreateLane("Complete")
b.message = "Welcome to SWIM"
workingPath, err := os.Getwd()
if err != nil {
panic(err.Error())
}
filename := filepath.Base(workingPath) + ".swim"
filename = strings.Replace(filename, " ", "_", -1)
savepath := filepath.Join(workingPath, filename)
b.Write([]string{"write", savepath})
Quit()
}
func DefaultBoard(cols, rows int) Board {
return Board{
Title: "",
@ -243,7 +260,11 @@ func main() {
style.Init(SetColorFromFlag(*colors))
cols, rows := termios.GetWindowSize()
if len(args) > 0 {
LoadFile(args[0], cols, rows)
if strings.ToLower(args[0]) == "init" {
InitNewBoard()
} else {
LoadFile(args[0], cols, rows)
}
} else {
fp = ""
board = DefaultBoard(cols, rows)

4
swim.1
View File

@ -153,6 +153,10 @@ quit
Quits the program immediately. To save and quit use \fIwq\fP.
.TP
.B
regexp
Accepts three required arguments: target (\fIbaord\fP, \fIlane\fP, \fIlanes\fP, \fIstory\fP, \fIstories\fP, and \fIstories!\fP), location (\fItitle\fP, and for story: \fIdescription\fP), and an expression representing a substitution in the form of \fI/[match expresion]/[substitution]/\fP. Matching is done with golang style regular expressions and flags can be added inline as a part of the expression. Example: \fIregexp story title /^My/Your/\fP. \fIlanes\fP and \fIstories\fP will apply the regex to all lanes or to all stories in the current lane. To target all stories on the whole board use \fIstories!\fP. Standard command abbreviation rules apply for all targets and locations with the exception of \fIlanes\fP, \fIstories\fP, and \fIstories!\fP, which must be written out fully.
.TP
.B
set
Sets a value for a particular target and location. Available targets are \fIboard\fP, \fIlane\fP, and \fIstory\fP. All three have a \fItitle\fP location. \fIstory\fP also has \fIdescription\fP, \fIpoints\fP, and \fIuser\fP. All commands accept an optional fourth argument representing the new value to set the location to. If no fourth argument, which can be many space separated words, is not passed the user will be queried for input. Many of the story locations can be modified via a key command but exist here to allow for user workflow preference. Example: \fIset lane title My Cool Lane\fP.
.TP