Explorar el Código

Enables create widget to change title from title defined by path

arianagiroux hace 2 semanas
padre
commit
d30742f820
Se han modificado 3 ficheros con 78 adiciones y 57 borrados
  1. 1 0
      Readme.md
  2. 70 50
      tui.go
  3. 7 7
      tui_test.go

+ 1 - 0
Readme.md

@@ -22,6 +22,7 @@ go install cmd/issues.go
 - `tui.go:	case IssueCollection: // TODO handle updates to IssueCollection widgets in its own update func`
 - `tui.go:// TODO add keyhelp func to widget interface`
 - `tui.go:// TODO invoke editor for descriptions`
+- `tui.go:// TODO handle reset on esc`
 - `tui.go:// TODO implement description field in createIssue.create cmd`
 
 ## See also

+ 70 - 50
tui.go

@@ -9,6 +9,7 @@ package issues
 
 import (
 	"fmt"
+	"path/filepath"
 	"strings"
 
 	"github.com/charmbracelet/bubbles/textinput"
@@ -115,7 +116,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		}
 	case widget: // widget is initialized from m.load()
 		switch T := msg.(type) {
-		case createIssue:
+		case create:
 			m.widget = T
 			cmds = append(cmds, T.render, T.init())
 		default:
@@ -127,7 +128,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	}
 
 	// finally, handle input updates if any
-	if w, ok := m.widget.(createIssue); ok {
+	if w, ok := m.widget.(create); ok {
 		var cmd tea.Cmd
 		m.widget, cmd = w.update(msg)
 		cmds = append(cmds, cmd, w.render)
@@ -151,9 +152,9 @@ func (m Model) View() string {
 // WIDGET DEFINITIONS ---------------------------------------------------------
 // ----------------------------------------------------------------------------
 
-// interface definition for widgets
-//
 // TODO add keyhelp func to widget interface
+
+// interface definition for widgets
 type widget interface {
 	render() tea.Msg
 }
@@ -167,17 +168,19 @@ type inputField struct {
 	title string
 }
 
-// widget for creating an issue
-//
 // TODO invoke editor for descriptions
-type createIssue struct {
+// TODO handle reset on esc
+
+// widget for creating an issue
+type create struct {
 	inputFields []inputField
 	Path        string
 	selected    int
 	Err         error // not implemented
 }
 
-func initialCreateIssueModel(path string, placeholder string) createIssue {
+// constructor for createIssue widget
+func initialCreateModel(path string, placeholder string) create {
 	spawnInput := func(f bool) textinput.Model {
 		ti := textinput.New()
 		ti.Placeholder = placeholder
@@ -196,9 +199,22 @@ func initialCreateIssueModel(path string, placeholder string) createIssue {
 		} else {
 			inputs = append(inputs, inputField{title: t, input: spawnInput(false)})
 		}
+
+		switch t {
+		case "title":
+			parsed := parsePathToHuman(path)
+
+			if parsed == "." {
+				parsed = ""
+			}
+
+			inputs[i].input.SetValue(parsed)
+		case "status":
+			inputs[i].input.SetValue("open")
+		}
 	}
 
-	return createIssue{
+	return create{
 		inputFields: inputs,
 		Path:        path,
 		selected:    0,
@@ -206,46 +222,46 @@ func initialCreateIssueModel(path string, placeholder string) createIssue {
 	}
 }
 
-func (ci createIssue) init() tea.Cmd {
+func (c create) init() tea.Cmd {
 	return textinput.Blink
 }
 
-func (ci createIssue) update(msg tea.Msg) (createIssue, tea.Cmd) {
+func (c create) update(msg tea.Msg) (create, tea.Cmd) {
 	var cmds []tea.Cmd
 	var cmd tea.Cmd
 
 	// simple anon funcs to increment the selected index
 	incrementSelected := func() {
-		if ci.selected < len(ci.inputFields) {
-			ci.selected++
-			for i := 0; i < len(ci.inputFields); i++ {
-				if i == ci.selected {
-					ci.inputFields[i].input.Focus()
+		if c.selected < len(c.inputFields) {
+			c.selected++
+			for i := 0; i < len(c.inputFields); i++ {
+				if i == c.selected {
+					c.inputFields[i].input.Focus()
 				} else {
-					ci.inputFields[i].input.Blur()
+					c.inputFields[i].input.Blur()
 				}
 			}
 		} else {
-			ci.selected = 0
-			ci.inputFields[ci.selected].input.Focus()
+			c.selected = 0
+			c.inputFields[c.selected].input.Focus()
 		}
 	}
 
 	decrementSelected := func() {
-		if ci.selected != 0 {
-			ci.selected--
-			for i := 0; i < len(ci.inputFields); i++ {
-				if i == ci.selected {
-					ci.inputFields[i].input.Focus()
+		if c.selected != 0 {
+			c.selected--
+			for i := 0; i < len(c.inputFields); i++ {
+				if i == c.selected {
+					c.inputFields[i].input.Focus()
 				} else {
-					ci.inputFields[i].input.Blur()
+					c.inputFields[i].input.Blur()
 				}
 			}
 		} else {
-			for i := 0; i < len(ci.inputFields); i++ {
-				ci.inputFields[i].input.Blur()
+			for i := 0; i < len(c.inputFields); i++ {
+				c.inputFields[i].input.Blur()
 			}
-			ci.selected = len(ci.inputFields)
+			c.selected = len(c.inputFields)
 		}
 	}
 
@@ -257,10 +273,10 @@ func (ci createIssue) update(msg tea.Msg) (createIssue, tea.Cmd) {
 		case "shift+tab":
 			decrementSelected()
 		case "enter":
-			if ci.selected == len(ci.inputFields) { // confirm create
-				ci.selected++
-			} else if ci.selected == len(ci.inputFields)+1 { // confirmed
-				cmds = append(cmds, ci.create)
+			if c.selected == len(c.inputFields) { // confirm create
+				c.selected++
+			} else if c.selected == len(c.inputFields)+1 { // confirmed
+				cmds = append(cmds, c.create)
 			} else {
 				incrementSelected()
 			}
@@ -268,7 +284,7 @@ func (ci createIssue) update(msg tea.Msg) (createIssue, tea.Cmd) {
 			cmds = append(cmds, tea.Quit)
 		}
 	case createResult:
-		cmds = append(cmds, ci.write(Issue(msg)))
+		cmds = append(cmds, c.write(Issue(msg)))
 	case writeResult:
 		switch value := msg.(type) {
 		case bool:
@@ -281,16 +297,16 @@ func (ci createIssue) update(msg tea.Msg) (createIssue, tea.Cmd) {
 		}
 	}
 
-	for i, ti := range ci.inputFields {
-		ci.inputFields[i].input, cmd = ti.input.Update(msg)
+	for i, ti := range c.inputFields {
+		c.inputFields[i].input, cmd = ti.input.Update(msg)
 		cmds = append(cmds, cmd)
 	}
 
 	cmds = append(cmds, cmd)
-	return ci, tea.Batch(cmds...)
+	return c, tea.Batch(cmds...)
 }
 
-func (ci createIssue) render() tea.Msg {
+func (c create) render() tea.Msg {
 	borderStyle := lipgloss.NewStyle().
 		BorderStyle(lipgloss.NormalBorder()).
 		Margin(1).
@@ -299,7 +315,7 @@ func (ci createIssue) render() tea.Msg {
 	ulStyle := lipgloss.NewStyle().Underline(true)
 
 	var output string
-	for _, field := range ci.inputFields {
+	for _, field := range c.inputFields {
 		output = output + fmt.Sprintf(
 			"\n%s:%s",
 			field.title,
@@ -309,14 +325,14 @@ func (ci createIssue) render() tea.Msg {
 
 	output = strings.TrimLeft(output, "\n")
 
-	if ci.selected < len(ci.inputFields) {
+	if c.selected < len(c.inputFields) {
 		output = output + borderStyle.Render("press enter to submit...")
-	} else if ci.selected == len(ci.inputFields) {
+	} else if c.selected == len(c.inputFields) {
 		output = output + borderStyle.Render(ulStyle.Render("press enter to submit..."))
-	} else if ci.selected == len(ci.inputFields)+1 {
+	} else if c.selected == len(c.inputFields)+1 {
 		confirmPrompt := fmt.Sprintf(
-			"creating issue titled \"%s\"...\n\n%s",
-			ulStyle.Render(ci.inputFields[0].input.Value()),
+			"create issue titled \"%s\"?\n\n%s",
+			ulStyle.Render(c.inputFields[0].input.Value()),
 			ulStyle.Render("press enter to confirm..."),
 		)
 		output = output + borderStyle.Render(confirmPrompt)
@@ -417,15 +433,15 @@ func (m Model) load() tea.Msg {
 		return collection
 	}
 
-	return initialCreateIssueModel(m.Path, "lorem ipsum")
+	return initialCreateModel(m.Path, "lorem ipsum")
 }
 
 type createResult Issue
 
-// A widget for creating issues
-//
 // TODO implement description field in createIssue.create cmd
-func (ci createIssue) create() tea.Msg {
+
+// A widget for creating issues
+func (c create) create() tea.Msg {
 	data := make(map[string]string)
 	commaSplit := func(t string) []string {
 		s := strings.Split(t, ",")
@@ -438,12 +454,12 @@ func (ci createIssue) create() tea.Msg {
 		return s
 	}
 
-	for _, field := range ci.inputFields {
+	for _, field := range c.inputFields {
 		data[field.title] = field.input.Value()
 	}
 
 	var newIssue = Issue{
-		Path:      ci.Path,
+		Path:      c.Path,
 		Tags:      VariadicField{Path: "/tags"},
 		Blockedby: VariadicField{Path: "/blockedby"},
 	}
@@ -452,6 +468,10 @@ func (ci createIssue) create() tea.Msg {
 		switch key {
 		case "title":
 			newIssue.Title = value
+			if parsePathToHuman(newIssue.Path) != value {
+				dir, _ := filepath.Split(newIssue.Path)
+				newIssue.Path = filepath.Join(dir, value)
+			}
 		case "status":
 			newIssue.Status = Field{Path: "/status", Data: value}
 		case "description":
@@ -477,7 +497,7 @@ func (ci createIssue) create() tea.Msg {
 type writeResult any
 
 // Wraps a cmd func, passes an initialized Issue to WriteIssue()
-func (ci createIssue) write(issue Issue) tea.Cmd {
+func (c create) write(issue Issue) tea.Cmd {
 	return func() tea.Msg {
 		result, err := WriteIssue(issue, false)
 		if err != nil {

+ 7 - 7
tui_test.go

@@ -198,16 +198,16 @@ func Test_Model_View(t *testing.T) {
 	assert.Equal(t, "loading...", render2)
 }
 
-func Test_createIssue_create_cmd(t *testing.T) {
+func Test_create_create_cmd(t *testing.T) {
 	// test data init
 	testData := make(map[string]string)
 
 	// because the resulting test data never gets written from memory to disk,
 	// we can set just about any test path
-	testCi := initialCreateIssueModel("tests/bugs/test-create-in-memory", "lorem ipsum")
-	for i := range testCi.inputFields {
-		testData[testCi.inputFields[i].title] = fmt.Sprintf("test%d", i)
-		testCi.inputFields[i].input.SetValue(fmt.Sprintf("test%d", i))
+	testC := initialCreateModel("tests/bugs/test-create-in-memory", "lorem ipsum")
+	for i := range testC.inputFields {
+		testData[testC.inputFields[i].title] = fmt.Sprintf("test%d", i)
+		testC.inputFields[i].input.SetValue(fmt.Sprintf("test%d", i))
 	}
 
 	commaSplit := func(t string) []string {
@@ -222,7 +222,7 @@ func Test_createIssue_create_cmd(t *testing.T) {
 	}
 
 	var testIssue = Issue{
-		Path:      "tests/bugs/test-create-in-memory",
+		Path:      "tests/bugs/test0", // tests that title permutation has propogated
 		Tags:      VariadicField{Path: "/tags"},
 		Blockedby: VariadicField{Path: "/blockedby"},
 	}
@@ -248,7 +248,7 @@ func Test_createIssue_create_cmd(t *testing.T) {
 		}
 	}
 
-	result := testCi.create()
+	result := testC.create()
 	assert.IsType(t, createResult(testIssue), result)
 	assert.Equal(t, createResult(testIssue).Path, result.(createResult).Path)
 }