mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-08 22:28:24 +02:00
Avoid spinning up the python interpreter just for running a shebang
This commit is contained in:
@@ -94,24 +94,7 @@ def edit(args: list[str]) -> None:
|
|||||||
|
|
||||||
def shebang(args: list[str]) -> None:
|
def shebang(args: list[str]) -> None:
|
||||||
from kitty.constants import kitten_exe
|
from kitty.constants import kitten_exe
|
||||||
script_path = args[1]
|
os.execvp(kitten_exe(), ['kitten', '__shebang__', 'confirm-if-needed'] + args[1:])
|
||||||
cmd = args[2:]
|
|
||||||
if cmd == ['__ext__']:
|
|
||||||
cmd = [os.path.splitext(script_path)[1][1:].lower()]
|
|
||||||
try:
|
|
||||||
f = open(script_path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise SystemExit(f'The file {script_path} does not exist')
|
|
||||||
with f:
|
|
||||||
if f.read(2) == '#!':
|
|
||||||
line = f.readline().strip()
|
|
||||||
_plat = sys.platform.lower()
|
|
||||||
is_macos: bool = 'darwin' in _plat
|
|
||||||
if is_macos:
|
|
||||||
cmd = line.split(' ')
|
|
||||||
else:
|
|
||||||
cmd = line.split(' ', maxsplit=1)
|
|
||||||
os.execvp(kitten_exe(), ['kitten', '__confirm_and_run_shebang__'] + cmd + [script_path])
|
|
||||||
|
|
||||||
|
|
||||||
def run_kitten(args: list[str]) -> None:
|
def run_kitten(args: list[str]) -> None:
|
||||||
|
|||||||
@@ -243,12 +243,12 @@ def default_launch_actions() -> tuple[OpenAction, ...]:
|
|||||||
# Open script files
|
# Open script files
|
||||||
protocol file
|
protocol file
|
||||||
ext sh,command,tool
|
ext sh,command,tool
|
||||||
action launch --hold --type=os-window kitty +shebang $FILE_PATH $SHELL
|
action launch --hold --type=os-window kitten __shebang__ confirm-if-needed $FILE_PATH $SHELL
|
||||||
|
|
||||||
# Open shell specific script files
|
# Open shell specific script files
|
||||||
protocol file
|
protocol file
|
||||||
ext fish,bash,zsh
|
ext fish,bash,zsh
|
||||||
action launch --hold --type=os-window kitty +shebang $FILE_PATH __ext__
|
action launch --hold --type=os-window kitten __shebang__ confirm-if-needed $FILE_PATH __ext__
|
||||||
|
|
||||||
# Open directories
|
# Open directories
|
||||||
protocol file
|
protocol file
|
||||||
|
|||||||
@@ -3,9 +3,13 @@
|
|||||||
package tool
|
package tool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
@@ -17,6 +21,14 @@ import (
|
|||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
type ConfirmPolicy uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConfirmAlways = iota
|
||||||
|
ConfirmNever
|
||||||
|
ConfirmIfNeeded
|
||||||
|
)
|
||||||
|
|
||||||
func ask_for_permission(script_path string) (response string, err error) {
|
func ask_for_permission(script_path string) (response string, err error) {
|
||||||
opts := &ask.Options{Type: "choices", Default: "n", Choices: []string{"y;green:Yes", "n;red:No", "v;yellow:View", "e;magenta:Edit"}}
|
opts := &ask.Options{Type: "choices", Default: "n", Choices: []string{"y;green:Yes", "n;red:No", "v;yellow:View", "e;magenta:Edit"}}
|
||||||
|
|
||||||
@@ -27,9 +39,18 @@ func ask_for_permission(script_path string) (response string, err error) {
|
|||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func confirm_and_run_shebang(args []string) (rc int, err error) {
|
func confirm_and_run_shebang(args []string, confirm_policy ConfirmPolicy) (rc int, err error) {
|
||||||
script_path := args[len(args)-1]
|
script_path := args[len(args)-1]
|
||||||
if unix.Access(script_path, unix.X_OK) != nil {
|
do_confirm := true
|
||||||
|
switch confirm_policy {
|
||||||
|
case ConfirmNever:
|
||||||
|
do_confirm = false
|
||||||
|
case ConfirmAlways:
|
||||||
|
do_confirm = true
|
||||||
|
case ConfirmIfNeeded:
|
||||||
|
do_confirm = unix.Access(script_path, unix.X_OK) != nil
|
||||||
|
}
|
||||||
|
if do_confirm {
|
||||||
response, err := ask_for_permission(script_path)
|
response, err := ask_for_permission(script_path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
@@ -43,7 +64,7 @@ func confirm_and_run_shebang(args []string) (rc int, err error) {
|
|||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
cli.ShowHelpInPager(utils.UnsafeBytesToString(raw))
|
cli.ShowHelpInPager(utils.UnsafeBytesToString(raw))
|
||||||
return confirm_and_run_shebang(args)
|
return confirm_and_run_shebang(args, ConfirmIfNeeded)
|
||||||
case "e":
|
case "e":
|
||||||
exe, err := os.Executable()
|
exe, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -54,7 +75,7 @@ func confirm_and_run_shebang(args []string) (rc int, err error) {
|
|||||||
editor.Stdout = os.Stdout
|
editor.Stdout = os.Stdout
|
||||||
editor.Stderr = os.Stderr
|
editor.Stderr = os.Stderr
|
||||||
editor.Run()
|
editor.Run()
|
||||||
return confirm_and_run_shebang(args)
|
return confirm_and_run_shebang(args, ConfirmIfNeeded)
|
||||||
case "y":
|
case "y":
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,3 +89,53 @@ func confirm_and_run_shebang(args []string) (rc int, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func run_shebang(args []string) (rc int, err error) {
|
||||||
|
if len(args) < 3 {
|
||||||
|
return 1, fmt.Errorf("Usage: kitten __shebang__ confirm-exe path_to_script cmd...")
|
||||||
|
}
|
||||||
|
var confirm_policy ConfirmPolicy
|
||||||
|
switch args[0] {
|
||||||
|
case "confirm-always":
|
||||||
|
confirm_policy = ConfirmAlways
|
||||||
|
case "confirm-never":
|
||||||
|
confirm_policy = ConfirmNever
|
||||||
|
case "confirm-if-needed":
|
||||||
|
confirm_policy = ConfirmIfNeeded
|
||||||
|
default:
|
||||||
|
return 1, fmt.Errorf("Unknown confirmation policy: %s", args[1])
|
||||||
|
}
|
||||||
|
script_path := args[1]
|
||||||
|
cmd := args[2:]
|
||||||
|
if len(cmd) == 1 && cmd[0] == "__ext__" {
|
||||||
|
ext := filepath.Ext(script_path)
|
||||||
|
if ext == "" || ext == "." {
|
||||||
|
return 1, fmt.Errorf("%s has no file extension so cannot be used in __ext__ mode", script_path)
|
||||||
|
}
|
||||||
|
cmd = []string{ext[1:]}
|
||||||
|
}
|
||||||
|
f, err := os.Open(script_path)
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
first_line := ""
|
||||||
|
if scanner.Scan() {
|
||||||
|
first_line = scanner.Text()
|
||||||
|
} else if err = scanner.Err(); err != nil {
|
||||||
|
f.Close()
|
||||||
|
return 1, fmt.Errorf("Failed to read from %s with error: %w", script_path, err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
if strings.HasPrefix(first_line, "#!") {
|
||||||
|
first_line = strings.TrimSpace(first_line[2:])
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
cmd = strings.Split(first_line, " ")
|
||||||
|
default:
|
||||||
|
cmd = strings.SplitN(first_line, " ", 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd = append(cmd, script_path)
|
||||||
|
return confirm_and_run_shebang(cmd, confirm_policy)
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,13 +99,13 @@ func KittyToolEntryPoints(root *cli.Command) {
|
|||||||
return
|
return
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// __confirm_and_run_shebang__
|
// __shebang__
|
||||||
root.AddSubCommand(&cli.Command{
|
root.AddSubCommand(&cli.Command{
|
||||||
Name: "__confirm_and_run_shebang__",
|
Name: "__shebang__",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
OnlyArgsAllowed: true,
|
OnlyArgsAllowed: true,
|
||||||
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
|
Run: func(cmd *cli.Command, args []string) (rc int, err error) {
|
||||||
return confirm_and_run_shebang(args)
|
return run_shebang(args)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// __convert_image__
|
// __convert_image__
|
||||||
|
|||||||
Reference in New Issue
Block a user