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  }