Allow opening the editor while creating a ticket with Ctrl+e.
Draft headline from the create input is passed into $EDITOR so title and description can be written before the ticket is saved. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+47
-6
@@ -21,9 +21,10 @@ import (
|
||||
|
||||
// editorFinishedMsg is sent after the external $EDITOR exits.
|
||||
type editorFinishedMsg struct {
|
||||
ticketID string
|
||||
ticketID string // empty when create==true
|
||||
path string
|
||||
err error
|
||||
create bool // true: create ticket from edited file
|
||||
}
|
||||
|
||||
// Mode is the active UI mode.
|
||||
@@ -374,6 +375,20 @@ func (m Model) updateInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case key.Matches(msg, m.keys.EditInEditor):
|
||||
if m.mode != ModeCreate || m.creatingProject {
|
||||
break
|
||||
}
|
||||
m.clearStatus()
|
||||
cmd, err := m.openEditorForNewTicket()
|
||||
if err != nil {
|
||||
m.setError(err)
|
||||
return m, nil
|
||||
}
|
||||
m.input.Blur()
|
||||
m.mode = ModeList
|
||||
return m, cmd
|
||||
|
||||
case key.Matches(msg, m.keys.Enter):
|
||||
value := strings.TrimSpace(m.input.Value())
|
||||
if m.mode == ModeCreate {
|
||||
@@ -397,7 +412,7 @@ func (m Model) updateInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
m.setStatus(fmt.Sprintf("created project %s", id))
|
||||
return m, nil
|
||||
}
|
||||
if err := m.createTicket(value); err != nil {
|
||||
if err := m.createTicket(value, ""); err != nil {
|
||||
m.setError(err)
|
||||
return m, nil
|
||||
}
|
||||
@@ -474,6 +489,21 @@ func (m Model) openEditorForSelected() (tea.Cmd, error) {
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (m Model) openEditorForNewTicket() (tea.Cmd, error) {
|
||||
title := strings.TrimSpace(m.input.Value())
|
||||
if title == "" {
|
||||
title = "New ticket"
|
||||
}
|
||||
path, err := ticket.WriteEditFile(ticket.Ticket{Title: title})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := ticket.EditorCmd(path)
|
||||
return tea.ExecProcess(c, func(err error) tea.Msg {
|
||||
return editorFinishedMsg{path: path, err: err, create: true}
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (m Model) handleEditorFinished(msg editorFinishedMsg) (tea.Model, tea.Cmd) {
|
||||
defer func() { _ = os.Remove(msg.path) }()
|
||||
|
||||
@@ -493,6 +523,16 @@ func (m Model) handleEditorFinished(msg editorFinishedMsg) (tea.Model, tea.Cmd)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if msg.create {
|
||||
if err := m.createTicket(title, description); err != nil {
|
||||
m.setError(err)
|
||||
return m, nil
|
||||
}
|
||||
m.mode = ModeList
|
||||
m.setStatus("created")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
before, err := m.store.Get(msg.ticketID)
|
||||
if err != nil {
|
||||
m.setError(err)
|
||||
@@ -521,13 +561,14 @@ func (m Model) handleEditorFinished(msg editorFinishedMsg) (tea.Model, tea.Cmd)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Model) createTicket(title string) error {
|
||||
func (m *Model) createTicket(title, description string) error {
|
||||
cmd := &command.CreateTicket{
|
||||
Store: m.store,
|
||||
Ticket: ticket.Ticket{
|
||||
Title: title,
|
||||
ProjectID: m.cfg.ActiveProject,
|
||||
Status: m.boardList.DefaultStatus(),
|
||||
Title: title,
|
||||
Description: description,
|
||||
ProjectID: m.cfg.ActiveProject,
|
||||
Status: m.boardList.DefaultStatus(),
|
||||
},
|
||||
}
|
||||
if err := m.exec.Do(cmd); err != nil {
|
||||
|
||||
@@ -14,10 +14,14 @@ type helpEntry struct {
|
||||
func (m Model) helpEntries() []helpEntry {
|
||||
switch m.mode {
|
||||
case ModeCreate:
|
||||
return []helpEntry{
|
||||
entries := []helpEntry{
|
||||
{"enter", "save"},
|
||||
{"esc", "cancel"},
|
||||
}
|
||||
if !m.creatingProject {
|
||||
entries = append([]helpEntry{{"ctrl+e", "editor"}}, entries...)
|
||||
}
|
||||
return entries
|
||||
case ModeSearch:
|
||||
return []helpEntry{
|
||||
{"enter", "apply"},
|
||||
|
||||
+14
-9
@@ -11,15 +11,16 @@ type KeyMap struct {
|
||||
Project key.Binding
|
||||
Board key.Binding
|
||||
Status key.Binding
|
||||
Edit key.Binding
|
||||
Delete key.Binding // x — confirm first
|
||||
DeleteForce key.Binding // X — immediate
|
||||
ConfirmYes key.Binding
|
||||
Undo key.Binding
|
||||
Redo key.Binding
|
||||
Quit key.Binding
|
||||
Enter key.Binding
|
||||
Escape key.Binding
|
||||
Edit key.Binding
|
||||
EditInEditor key.Binding // ctrl+e — open $EDITOR while creating
|
||||
Delete key.Binding // x — confirm first
|
||||
DeleteForce key.Binding // X — immediate
|
||||
ConfirmYes key.Binding
|
||||
Undo key.Binding
|
||||
Redo key.Binding
|
||||
Quit key.Binding
|
||||
Enter key.Binding
|
||||
Escape key.Binding
|
||||
}
|
||||
|
||||
// DefaultKeyMap returns the default key bindings.
|
||||
@@ -57,6 +58,10 @@ func DefaultKeyMap() KeyMap {
|
||||
key.WithKeys("e"),
|
||||
key.WithHelp("e", "edit"),
|
||||
),
|
||||
EditInEditor: key.NewBinding(
|
||||
key.WithKeys("ctrl+e"),
|
||||
key.WithHelp("ctrl+e", "editor"),
|
||||
),
|
||||
Delete: key.NewBinding(
|
||||
key.WithKeys("x"),
|
||||
key.WithHelp("x", "delete"),
|
||||
|
||||
Reference in New Issue
Block a user