Git Product home page Git Product logo

Comments (3)

joelim-work avatar joelim-work commented on August 12, 2024 1

So this is what I found by looking at the history:

  • shellopts was introduced because of #102, to allow specifying additional options when invoking the shell (e.g. -f to disable globbing).
  • shellflag was introduced because of #597, to override the default -c option (/c for Windows), primarily to allow the use of other shells in Windows.

I agree that such high-level configuration options can be inflexible, and that it would be beneficial to provide a more low-level method of specifying the shell invocation. However, modifying the existing shell configuration option is a breaking change, and given that the shell is a core feature of lf, I am not sure if this is a good idea.

I think it might be better to introduce a new option (e.g. shellcmd) instead, implemented like below (similar changes required for os_windows.go):

Click to expand
diff --git a/eval.go b/eval.go
index 23a6be5..ab8f1b0 100644
--- a/eval.go
+++ b/eval.go
@@ -368,6 +368,12 @@ func (e *setExpr) eval(app *app, args []string) {
 			return
 		}
 		gOpts.shellopts = strings.Split(e.val, ":")
+	case "shellcmd":
+		if e.val == "" {
+			gOpts.shellcmd = nil
+			return
+		}
+		gOpts.shellcmd = tokenize(e.val)
 	case "sortby":
 		method := sortMethod(e.val)
 		if !isValidSortMethod(method) {
diff --git a/opts.go b/opts.go
index 50db7b1..24d568a 100644
--- a/opts.go
+++ b/opts.go
@@ -94,6 +94,7 @@ var gOpts struct {
 	rulerfmt         string
 	preserve         []string
 	shellopts        []string
+	shellcmd         []string
 	keys             map[string]expr
 	cmdkeys          map[string]expr
 	cmds             map[string]expr
@@ -240,6 +241,7 @@ func init() {
 	gOpts.rulerfmt = "  %a|  %p|  \033[7;31m %m \033[0m|  \033[7;33m %c \033[0m|  \033[7;35m %s \033[0m|  \033[7;34m %f \033[0m|  %i/%t"
 	gOpts.preserve = []string{"mode"}
 	gOpts.shellopts = nil
+	gOpts.shellcmd = nil
 	gOpts.tempmarks = "'"
 	gOpts.numberfmt = "\033[33m"
 	gOpts.tagfmt = "\033[31m"
diff --git a/os.go b/os.go
index ac2d2f6..95fbe75 100644
--- a/os.go
+++ b/os.go
@@ -140,6 +140,21 @@ func shellCommand(s string, args []string) *exec.Cmd {
 		s = fmt.Sprintf("IFS='%s'; %s", gOpts.ifs, s)
 	}
 
+	if len(gOpts.shellcmd) > 0 {
+		var words []string
+		for _, word := range gOpts.shellcmd {
+			switch word {
+			case "$a":
+				words = append(words, args...)
+			case "$c":
+				words = append(words, s)
+			default:
+				words = append(words, word)
+			}
+		}
+		return exec.Command(words[0], words[1:]...)
+	}
+
 	args = append([]string{gOpts.shellflag, s, "--"}, args...)
 
 	args = append(gOpts.shellopts, args...)

Although I suppose we could just keep shell as a string option and apply the new handling logic based on whether it consists of a single word or not. I could be convinced either way on this.

from lf.

joelim-work avatar joelim-work commented on August 12, 2024 1

I experimented a bit with Windows CMD, unfortunately it has its own way of dealing with quoted arguments and requires special handling. You can find some notes about it in the description for #1309.

It might be better to implement the shell option as a string value, something like below:

func shellCommand(s string, args []string) *exec.Cmd {
	// Windows CMD requires special handling to deal with quoted arguments
	if strings.HasPrefix(strings.ToLower(gOpts.shell), "cmd ") {
		var quotedArgs []string
		for _, arg := range args {
			quotedArgs = append(quotedArgs, fmt.Sprintf(`"%s"`), arg)
		}
		cmdline := strings.ReplaceAll(gOpts.shell, "$c", s)
		cmdline = strings.ReplaceAll(cmdline, "$a", strings.Join(quotedArgs, " "))
		cmd := exec.Command("cmd")
		cmd.SysProcAttr = &windows.SysProcAttr{CmdLine: cmdline}
		return cmd
	}

	if strings.Contains(gOpts.shell, " ") {
		var words []string
		for _, word := range tokenize(gOpts.shell) {
			switch word {
			case "$a":
				words = append(words, args...)
			case "$c":
				words = append(words, s)
			default:
				words = append(words, word)
			}
		}
		return exec.Command(words[0], words[1:]...)
	}

	// original legacy configuration which uses shellopts and shellflag
	args = append([]string{gOpts.shellflag, s}, args...)
	args = append(gOpts.shellopts, args...)
	return exec.Command(gOpts.shell, args...)
}

This should work for Windows CMD:

set shell 'cmd /c "$c $a"'

And for PowerShell:

set shell 'pwsh -CommandWithArgs $c $a'

The only other suggestions I have are:

  • Whether to implement this as a new command, or just use the existing shell command (must be backwards compatible)
  • Whether to use short names for the placeholders ($c and $a) or something longer like $cmd and $args

Anyway I think this is a great idea, and an improvement over the current design. Feel free to submit a PR if you get this working.

from lf.

39555 avatar 39555 commented on August 12, 2024

Thanks for the hint! Im going to implement it and we will see how it plays out

from lf.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.