github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/editor/edit.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package editor 16 17 import ( 18 "fmt" 19 "os" 20 "os/exec" 21 "path/filepath" 22 "unicode" 23 24 "github.com/google/uuid" 25 ) 26 27 // OpenTempEditor allows user to write/edit message in temporary file 28 func OpenTempEditor(ed string, initialContents string) (string, error) { 29 filename := filepath.Join(os.TempDir(), uuid.New().String()) 30 err := os.WriteFile(filename, []byte(initialContents), os.ModePerm) 31 32 if err != nil { 33 return "", err 34 } 35 36 cmdName, cmdArgs := getCmdNameAndArgsForEditor(ed) 37 38 if cmdName == "" { 39 panic("No editor specified: " + ed) 40 } 41 42 cmdArgs = append(cmdArgs, filename) 43 44 cmd := exec.Command(cmdName, cmdArgs...) 45 cmd.Stdin = os.Stdin 46 cmd.Stdout = os.Stdout 47 cmd.Stderr = os.Stderr 48 err = cmd.Start() 49 if err != nil { 50 return "", err 51 } 52 fmt.Printf("Waiting for command to finish.\n") 53 err = cmd.Wait() 54 if err != nil { 55 return "", err 56 } 57 58 data, err := os.ReadFile(filename) 59 60 if err != nil { 61 return "", err 62 } 63 64 return string(data), nil 65 } 66 67 func getCmdNameAndArgsForEditor(es string) (string, []string) { 68 type span struct { 69 start int 70 end int 71 } 72 spans := make([]span, 0, 32) 73 74 lastQuote := rune(0) 75 f := func(c rune) bool { 76 switch { 77 case c == lastQuote: 78 lastQuote = rune(0) 79 return true 80 case lastQuote != rune(0): 81 return false 82 case unicode.In(c, unicode.Quotation_Mark): 83 lastQuote = c 84 return false 85 default: 86 return unicode.IsSpace(c) 87 88 } 89 } 90 91 hasStarted := false 92 start := 0 93 for i, rune := range es { 94 if f(rune) { 95 if hasStarted { 96 if unicode.In(rune, unicode.Quotation_Mark) { 97 spans = append(spans, span{start: start + 1, end: i}) 98 } else { 99 spans = append(spans, span{start: start, end: i}) 100 } 101 102 hasStarted = false 103 } 104 } else { 105 if !hasStarted { 106 start = i 107 hasStarted = true 108 } 109 } 110 } 111 112 if hasStarted { 113 spans = append(spans, span{start, len(es)}) 114 } 115 116 results := make([]string, len(spans)) 117 for i, span := range spans { 118 results[i] = es[span.start:span.end] 119 } 120 121 return results[0], results[1:] 122 }