55 "fmt"
66 "io"
77 "os"
8+ "path/filepath"
9+ "runtime"
810 "strings"
911
1012 "github.com/chzyer/readline"
@@ -27,7 +29,7 @@ func CanceledError() error {
2729 return errors .New ("canceled" )
2830}
2931
30- func scan (message string , out io.Writer , in io.ReadCloser , allowEmpty bool ) (string , error ) {
32+ func scan (prompt string , out io.Writer , in io.ReadCloser , allowEmpty bool ) (string , error ) {
3133 f , err := os .CreateTemp ("" , "pet-" )
3234 if err != nil {
3335 return "" , err
@@ -36,13 +38,13 @@ func scan(message string, out io.Writer, in io.ReadCloser, allowEmpty bool) (str
3638 tempFile := f .Name ()
3739
3840 l , err := readline .NewEx (& readline.Config {
39- Stdout : out ,
40- Stdin : in ,
41- Prompt : message ,
42- HistoryFile : tempFile ,
43- InterruptPrompt : "^C" ,
44- EOFPrompt : "exit" ,
45-
41+ Stdout : out ,
42+ Stdin : in ,
43+ Prompt : prompt ,
44+ HistoryFile : tempFile ,
45+ InterruptPrompt : "^C" ,
46+ EOFPrompt : "exit" ,
47+ VimMode : false ,
4648 HistorySearchFold : true ,
4749 })
4850
@@ -75,6 +77,109 @@ func scan(message string, out io.Writer, in io.ReadCloser, allowEmpty bool) (str
7577 return "" , CanceledError ()
7678}
7779
80+ // States of scanMultiLine state machine
81+ const (
82+ start = iota
83+ lastLineNotEmpty
84+ lastLineEmpty
85+ )
86+
87+ func scanMultiLine (prompt string , secondMessage string , out io.Writer , in io.ReadCloser ) (string , error ) {
88+ tempFile := "/tmp/pet.tmp"
89+ if runtime .GOOS == "windows" {
90+ tempDir := os .Getenv ("TEMP" )
91+ tempFile = filepath .Join (tempDir , "pet.tmp" )
92+ }
93+ l , err := readline .NewEx (& readline.Config {
94+ Stdout : out ,
95+ Stdin : in ,
96+ Prompt : prompt ,
97+ HistoryFile : tempFile ,
98+ InterruptPrompt : "^C" ,
99+ EOFPrompt : "exit" ,
100+ VimMode : false ,
101+ HistorySearchFold : true ,
102+ })
103+ if err != nil {
104+ return "" , err
105+ }
106+ defer l .Close ()
107+
108+ state := start
109+ multiline := ""
110+ for {
111+ line , err := l .Readline ()
112+ if err == readline .ErrInterrupt {
113+ if len (line ) == 0 {
114+ break
115+ } else {
116+ continue
117+ }
118+ } else if err == io .EOF {
119+ break
120+ }
121+ switch state {
122+ case start :
123+ if line == "" {
124+ continue
125+ }
126+ multiline += line
127+ state = lastLineNotEmpty
128+ l .SetPrompt (secondMessage )
129+ case lastLineNotEmpty :
130+ if line == "" {
131+ state = lastLineEmpty
132+ continue
133+ }
134+ multiline += "\n " + line
135+ case lastLineEmpty :
136+ if line == "" {
137+ return multiline , nil
138+ }
139+ multiline += "\n " + line
140+ state = lastLineNotEmpty
141+ }
142+ }
143+ return "" , errors .New ("canceled" )
144+ }
145+
146+ // createAndEditSnippet creates and saves a given snippet, then opens the
147+ // configured editor to edit the snippet file at startLine.
148+ func createAndEditSnippet (newSnippet snippet.SnippetInfo , snippets snippet.Snippets , startLine int ) error {
149+ snippets .Snippets = append (snippets .Snippets , newSnippet )
150+ if err := snippets .Save (); err != nil {
151+ return err
152+ }
153+
154+ // Open snippet for editing
155+ snippetFile := config .Conf .General .SnippetFile
156+ editor := config .Conf .General .Editor
157+ err := editFile (editor , snippetFile , startLine )
158+ if err != nil {
159+ return err
160+ }
161+
162+ if config .Conf .Gist .AutoSync {
163+ return petSync .AutoSync (snippetFile )
164+ }
165+
166+ return nil
167+ }
168+
169+ func countSnippetLines () int {
170+ // Count lines in snippet file
171+ f , err := os .Open (config .Conf .General .SnippetFile )
172+ if err != nil {
173+ panic ("Error reading snippet file" )
174+ }
175+ lineCount , err := CountLines (f )
176+ if err != nil {
177+ panic ("Error counting lines in snippet file" )
178+ }
179+
180+ return lineCount
181+ }
182+
78183func new (cmd * cobra.Command , args []string ) (err error ) {
79184 var command string
80185 var description string
@@ -85,11 +190,31 @@ func new(cmd *cobra.Command, args []string) (err error) {
85190 return err
86191 }
87192
193+ lineCount := countSnippetLines ()
194+
88195 if len (args ) > 0 {
89196 command = strings .Join (args , " " )
90197 fmt .Fprintf (color .Output , "%s %s\n " , color .HiYellowString ("Command>" ), command )
91198 } else {
92- command , err = scan (color .HiYellowString ("Command> " ), os .Stdout , os .Stdin , false )
199+ if config .Flag .UseMultiLine {
200+ command , err = scanMultiLine (
201+ color .YellowString ("Command> " ),
202+ color .YellowString (".......> " ),
203+ os .Stdout , os .Stdin ,
204+ )
205+ } else if config .Flag .UseEditor {
206+ // Create and save empty snippet
207+ newSnippet := snippet.SnippetInfo {
208+ Description : description ,
209+ Command : command ,
210+ Tag : tags ,
211+ }
212+
213+ return createAndEditSnippet (newSnippet , snippets , lineCount + 3 )
214+
215+ } else {
216+ command , err = scan (color .HiYellowString ("Command> " ), os .Stdout , os .Stdin , false )
217+ }
93218 if err != nil {
94219 return err
95220 }
@@ -138,4 +263,8 @@ func init() {
138263 RootCmd .AddCommand (newCmd )
139264 newCmd .Flags ().BoolVarP (& config .Flag .Tag , "tag" , "t" , false ,
140265 `Display tag prompt (delimiter: space)` )
266+ newCmd .Flags ().BoolVarP (& config .Flag .UseMultiLine , "multiline" , "m" , false ,
267+ `Can enter multiline snippet (Double \n to quit)` )
268+ newCmd .Flags ().BoolVarP (& config .Flag .UseEditor , "editor" , "e" , false ,
269+ `Use editor to create snippet` )
141270}
0 commit comments