gh gist edit
This commit is contained in:
parent
39b6ec8aec
commit
0d3056d9a7
5 changed files with 217 additions and 8 deletions
203
pkg/cmd/gist/edit/edit.go
Normal file
203
pkg/cmd/gist/edit/edit.go
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
package edit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/cli/cli/api"
|
||||||
|
"github.com/cli/cli/internal/config"
|
||||||
|
"github.com/cli/cli/internal/ghinstance"
|
||||||
|
"github.com/cli/cli/pkg/cmd/gist/shared"
|
||||||
|
"github.com/cli/cli/pkg/cmdutil"
|
||||||
|
"github.com/cli/cli/pkg/iostreams"
|
||||||
|
"github.com/cli/cli/pkg/prompt"
|
||||||
|
"github.com/cli/cli/pkg/surveyext"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EditOptions struct {
|
||||||
|
IO *iostreams.IOStreams
|
||||||
|
HttpClient func() (*http.Client, error)
|
||||||
|
Config func() (config.Config, error)
|
||||||
|
|
||||||
|
Selector string
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Command {
|
||||||
|
opts := EditOptions{
|
||||||
|
IO: f.IOStreams,
|
||||||
|
HttpClient: f.HttpClient,
|
||||||
|
Config: f.Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "edit {<gist ID> | <gist URL>}",
|
||||||
|
Short: "Edit one of your gists",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
|
opts.Selector = args[0]
|
||||||
|
|
||||||
|
if runF != nil {
|
||||||
|
return runF(&opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return editRun(&opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().StringVarP(&opts.Filename, "filename", "f", "", "a specific file to edit")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func editRun(opts *EditOptions) error {
|
||||||
|
gistID := opts.Selector
|
||||||
|
|
||||||
|
u, err := url.Parse(opts.Selector)
|
||||||
|
if err == nil {
|
||||||
|
if strings.HasPrefix(u.Path, "/") {
|
||||||
|
gistID = u.Path[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := opts.HttpClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
gist, err := shared.GetGist(client, ghinstance.OverridableDefault(), gistID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
filesToUpdate := map[string]string{}
|
||||||
|
|
||||||
|
for true {
|
||||||
|
filename := opts.Filename
|
||||||
|
candidates := []string{}
|
||||||
|
for filename, _ := range gist.Files {
|
||||||
|
candidates = append(candidates, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(candidates)
|
||||||
|
|
||||||
|
if filename == "" {
|
||||||
|
if len(candidates) == 1 {
|
||||||
|
filename = candidates[0]
|
||||||
|
} else {
|
||||||
|
if !opts.IO.CanPrompt() {
|
||||||
|
return errors.New("unsure what file to edit; either specify --filename or run interactively")
|
||||||
|
}
|
||||||
|
err = prompt.SurveyAskOne(&survey.Select{
|
||||||
|
Message: "Edit which file?",
|
||||||
|
Options: candidates,
|
||||||
|
}, &filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := gist.Files[filename]; !ok {
|
||||||
|
return fmt.Errorf("gist has no file %q", filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
editorCommand, err := cmdutil.DetermineEditor(opts.Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
text, err := surveyext.Edit(
|
||||||
|
editorCommand,
|
||||||
|
"*."+filename,
|
||||||
|
gist.Files[filename].Content,
|
||||||
|
// TODO: consider using iostreams here
|
||||||
|
os.Stdin, os.Stdout, os.Stderr, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if text != gist.Files[filename].Content {
|
||||||
|
gistFile := gist.Files[filename]
|
||||||
|
gistFile.Content = text // so it appears if they re-edit
|
||||||
|
filesToUpdate[filename] = text
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.IO.CanPrompt() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(candidates) == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
choice := ""
|
||||||
|
|
||||||
|
err = prompt.SurveyAskOne(&survey.Select{
|
||||||
|
Message: "What next?",
|
||||||
|
Options: []string{
|
||||||
|
"Edit another file",
|
||||||
|
"Submit",
|
||||||
|
"Cancel",
|
||||||
|
},
|
||||||
|
}, &choice)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stop := false
|
||||||
|
|
||||||
|
switch choice {
|
||||||
|
case "Edit another file":
|
||||||
|
continue
|
||||||
|
case "Submit":
|
||||||
|
stop = true
|
||||||
|
case "Cancel":
|
||||||
|
return cmdutil.SilentError
|
||||||
|
}
|
||||||
|
|
||||||
|
if stop {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = updateGist(client, ghinstance.OverridableDefault(), gist)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateGist(client *http.Client, hostname string, gist *shared.Gist) error {
|
||||||
|
body := shared.Gist{
|
||||||
|
Description: gist.Description,
|
||||||
|
Files: gist.Files,
|
||||||
|
}
|
||||||
|
|
||||||
|
path := "gists/" + gist.ID
|
||||||
|
|
||||||
|
requestByte, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBody := bytes.NewReader(requestByte)
|
||||||
|
|
||||||
|
result := shared.Gist{}
|
||||||
|
|
||||||
|
apiClient := api.NewClientFromHTTP(client)
|
||||||
|
err = apiClient.REST(hostname, "POST", path, requestBody, &result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package gist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gistCreateCmd "github.com/cli/cli/pkg/cmd/gist/create"
|
gistCreateCmd "github.com/cli/cli/pkg/cmd/gist/create"
|
||||||
|
gistEditCmd "github.com/cli/cli/pkg/cmd/gist/edit"
|
||||||
gistListCmd "github.com/cli/cli/pkg/cmd/gist/list"
|
gistListCmd "github.com/cli/cli/pkg/cmd/gist/list"
|
||||||
gistViewCmd "github.com/cli/cli/pkg/cmd/gist/view"
|
gistViewCmd "github.com/cli/cli/pkg/cmd/gist/view"
|
||||||
"github.com/cli/cli/pkg/cmdutil"
|
"github.com/cli/cli/pkg/cmdutil"
|
||||||
|
|
@ -18,6 +19,7 @@ func NewCmdGist(f *cmdutil.Factory) *cobra.Command {
|
||||||
cmd.AddCommand(gistCreateCmd.NewCmdCreate(f, nil))
|
cmd.AddCommand(gistCreateCmd.NewCmdCreate(f, nil))
|
||||||
cmd.AddCommand(gistListCmd.NewCmdList(f, nil))
|
cmd.AddCommand(gistListCmd.NewCmdList(f, nil))
|
||||||
cmd.AddCommand(gistViewCmd.NewCmdView(f, nil))
|
cmd.AddCommand(gistViewCmd.NewCmdView(f, nil))
|
||||||
|
cmd.AddCommand(gistEditCmd.NewCmdEdit(f, nil))
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Gist struct {
|
type Gist struct {
|
||||||
Description string `json:"description"`
|
Description string
|
||||||
HTMLURL string `json:"html_url"`
|
HTMLURL string `json:"html_url"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
Public bool
|
Public bool
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,19 @@ import (
|
||||||
"github.com/cli/cli/api"
|
"github.com/cli/cli/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO make gist create use this file
|
||||||
|
|
||||||
type GistFile struct {
|
type GistFile struct {
|
||||||
Filename string
|
Filename string `json:"filename"`
|
||||||
Type string
|
Type string `json:"type,omitempty"`
|
||||||
Language string
|
Language string `json:"language,omitempty"`
|
||||||
Content string
|
Content string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Gist struct {
|
type Gist struct {
|
||||||
Description string
|
ID string `json:"id,omitempty"`
|
||||||
Files map[string]GistFile
|
Description string `json:"description"`
|
||||||
|
Files map[string]*GistFile `json:"files"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGist(client *http.Client, hostname, gistID string) (*Gist, error) {
|
func GetGist(client *http.Client, hostname, gistID string) (*Gist, error) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/cli/cli/internal/ghinstance"
|
"github.com/cli/cli/internal/ghinstance"
|
||||||
|
"github.com/cli/cli/pkg/cmd/gist/shared"
|
||||||
"github.com/cli/cli/pkg/cmdutil"
|
"github.com/cli/cli/pkg/cmdutil"
|
||||||
"github.com/cli/cli/pkg/iostreams"
|
"github.com/cli/cli/pkg/iostreams"
|
||||||
"github.com/cli/cli/utils"
|
"github.com/cli/cli/utils"
|
||||||
|
|
@ -65,7 +66,7 @@ func viewRun(opts *ViewOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gist, err := getGist(client, ghinstance.OverridableDefault(), gistID)
|
gist, err := shared.GetGist(client, ghinstance.OverridableDefault(), gistID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue