fix(cli): correct execution logic and improve messages

This commit is contained in:
L. Yeung 2024-08-24 12:19:01 +08:00 committed by Jan De Dobbeleer
parent 48f633e0cb
commit e28d91b854
8 changed files with 102 additions and 76 deletions

View file

@ -3,7 +3,6 @@ package cli
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
@ -50,7 +49,7 @@ You can do the following:
clear(env.CachePath()) clear(env.CachePath())
case "edit": case "edit":
cacheFilePath := filepath.Join(env.CachePath(), cache.FileName) cacheFilePath := filepath.Join(env.CachePath(), cache.FileName)
editFileWithEditor(cacheFilePath) os.Exit(editFileWithEditor(cacheFilePath))
} }
}, },
} }
@ -59,25 +58,6 @@ func init() {
RootCmd.AddCommand(getCache) RootCmd.AddCommand(getCache)
} }
func editFileWithEditor(file string) {
editor := os.Getenv("EDITOR")
var args []string
if strings.Contains(editor, " ") {
splitted := strings.Split(editor, " ")
editor = splitted[0]
args = splitted[1:]
}
args = append(args, file)
cmd := exec.Command(editor, args...)
err := cmd.Run()
if err != nil {
fmt.Println(err.Error())
}
}
func clear(cachePath string) { func clear(cachePath string) {
// get all files in the cache directory that start with omp.cache and delete them // get all files in the cache directory that start with omp.cache and delete them
files, err := os.ReadDir(cachePath) files, err := os.ReadDir(cachePath)

View file

@ -5,16 +5,18 @@ import (
"os" "os"
"time" "time"
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// configCmd represents the config command // configCmd represents the config command
var configCmd = &cobra.Command{ var configCmd = &cobra.Command{
Use: "config [export|migrate|edit]", Use: "config edit",
Short: "Interact with the config", Short: "Interact with the config",
Long: `Interact with the config. Long: `Interact with the config.
You can export, migrate or edit the config.`, You can export, migrate or edit the config (via the editor specified in the environment variable "EDITOR").`,
ValidArgs: []string{ ValidArgs: []string{
"export", "export",
"migrate", "migrate",
@ -29,7 +31,13 @@ You can export, migrate or edit the config.`,
} }
switch args[0] { switch args[0] {
case "edit": case "edit":
editFileWithEditor(os.Getenv("POSH_THEME")) env := &runtime.Terminal{
CmdFlags: &runtime.Flags{
Config: configFlag,
},
}
env.ResolveConfigPath()
os.Exit(editFileWithEditor(env.CmdFlags.Config))
case "get": case "get":
// only here for backwards compatibility // only here for backwards compatibility
fmt.Print(time.Now().UnixNano() / 1000000) fmt.Print(time.Now().UnixNano() / 1000000)

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"strings" "strings"
"github.com/jandedobbeleer/oh-my-posh/src/config" "github.com/jandedobbeleer/oh-my-posh/src/config"
@ -13,9 +12,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var ( var output string
output string
)
// exportCmd represents the export command // exportCmd represents the export command
var exportCmd = &cobra.Command{ var exportCmd = &cobra.Command{
@ -27,15 +24,21 @@ You can choose to print the output to stdout, or export your config in the forma
Example usage: Example usage:
> oh-my-posh config export --config ~/myconfig.omp.json
Exports the ~/myconfig.omp.json config file and prints the result to stdout.
> oh-my-posh config export --config ~/myconfig.omp.json --format toml > oh-my-posh config export --config ~/myconfig.omp.json --format toml
Exports the ~/myconfig.omp.json config file to toml and prints the result to stdout.`, Exports the config file "~/myconfig.omp.json" in TOML format and prints the result to stdout.
> oh-my-posh config export --output ~/new_config.omp.json
Exports the current config to "~/new_config.omp.json" (in JSON format).`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
Run: func(_ *cobra.Command, _ []string) { Run: func(_ *cobra.Command, _ []string) {
if len(output) == 0 && len(format) == 0 {
// usage error
fmt.Println("neither output path nor export format is specified")
os.Exit(2)
}
env := &runtime.Terminal{ env := &runtime.Terminal{
CmdFlags: &runtime.Flags{ CmdFlags: &runtime.Flags{
Config: configFlag, Config: configFlag,
@ -45,15 +48,25 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
defer env.Close() defer env.Close()
cfg := config.Load(env) cfg := config.Load(env)
if len(output) == 0 && len(format) == 0 { validateExportFormat := func() {
// usage error format = strings.ToLower(format)
os.Exit(2) switch format {
case "json", "jsonc":
format = config.JSON
case "toml", "tml":
format = config.TOML
case "yaml", "yml":
format = config.YAML
default:
formats := []string{"json", "jsonc", "toml", "tml", "yaml", "yml"}
// usage error
fmt.Printf("export format must be one of these: %s\n", strings.Join(formats, ", "))
os.Exit(2)
}
} }
formats := []string{"json", "jsonc", "toml", "tml", "yaml", "yml"} if len(format) != 0 {
if len(format) != 0 && !slices.Contains(formats, format) { validateExportFormat()
// usage error
os.Exit(2)
} }
if len(output) == 0 { if len(output) == 0 {
@ -65,18 +78,7 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
if len(format) == 0 { if len(format) == 0 {
format = strings.TrimPrefix(filepath.Ext(output), ".") format = strings.TrimPrefix(filepath.Ext(output), ".")
} validateExportFormat()
switch format {
case "json", "jsonc":
format = config.JSON
case "toml", "tml":
format = config.TOML
case "yaml", "yml":
format = config.YAML
default:
// data error
os.Exit(65)
} }
cfg.Write(format) cfg.Write(format)
@ -84,10 +86,7 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
} }
func cleanOutputPath(path string, env runtime.Environment) string { func cleanOutputPath(path string, env runtime.Environment) string {
if strings.HasPrefix(path, "~") { path = runtime.ReplaceTildePrefixWithHomeDir(env, path)
path = strings.TrimPrefix(path, "~")
path = filepath.Join(env.Home(), path)
}
if !filepath.IsAbs(path) { if !filepath.IsAbs(path) {
if absPath, err := filepath.Abs(path); err == nil { if absPath, err := filepath.Abs(path); err == nil {

34
src/cli/edit.go Normal file
View file

@ -0,0 +1,34 @@
package cli
import (
"fmt"
"os"
"os/exec"
"strings"
)
func editFileWithEditor(file string) int {
editor := strings.TrimSpace(os.Getenv("EDITOR"))
if len(editor) == 0 {
fmt.Println(`no editor specified in the environment variable "EDITOR"`)
return 1
}
var args []string
if strings.Contains(editor, " ") {
strs := strings.Split(editor, " ")
editor = strs[0]
args = strs[1:]
}
args = append(args, file)
cmd := exec.Command(editor, args...)
err := cmd.Run()
if err != nil {
fmt.Println(err.Error())
return 1
}
return cmd.ProcessState.ExitCode()
}

View file

@ -66,7 +66,6 @@ var printCmd = &cobra.Command{
Plain: plain, Plain: plain,
Primary: args[0] == "primary", Primary: args[0] == "primary",
Cleared: cleared, Cleared: cleared,
Cached: cached,
NoExitCode: noStatus, NoExitCode: noStatus,
Column: column, Column: column,
JobCount: jobCount, JobCount: jobCount,

View file

@ -98,7 +98,6 @@ type Flags struct {
HasTransient bool HasTransient bool
PromptCount int PromptCount int
Cleared bool Cleared bool
Cached bool
NoExitCode bool NoExitCode bool
Column int Column int
JobCount int JobCount int

View file

@ -74,7 +74,7 @@ func (term *Terminal) Init() {
term.deviceCache = initCache(cache.FileName) term.deviceCache = initCache(cache.FileName)
term.sessionCache = initCache(cache.SessionFileName) term.sessionCache = initCache(cache.SessionFileName)
term.resolveConfigPath() term.ResolveConfigPath()
term.cmdCache = &cache.Command{ term.cmdCache = &cache.Command{
Commands: maps.NewConcurrent(), Commands: maps.NewConcurrent(),
@ -82,12 +82,10 @@ func (term *Terminal) Init() {
term.tmplCache = &cache.Template{} term.tmplCache = &cache.Template{}
if !term.CmdFlags.Cached { term.SetPromptCount()
term.SetPromptCount()
}
} }
func (term *Terminal) resolveConfigPath() { func (term *Terminal) ResolveConfigPath() {
defer term.Trace(time.Now()) defer term.Trace(time.Now())
// if the config flag is set, we'll use that over POSH_THEME // if the config flag is set, we'll use that over POSH_THEME
@ -129,11 +127,7 @@ func (term *Terminal) resolveConfigPath() {
return return
} }
configFile := term.CmdFlags.Config configFile := ReplaceTildePrefixWithHomeDir(term, term.CmdFlags.Config)
if strings.HasPrefix(configFile, "~") {
configFile = strings.TrimPrefix(configFile, "~")
configFile = filepath.Join(term.Home(), configFile)
}
abs, err := filepath.Abs(configFile) abs, err := filepath.Abs(configFile)
if err != nil { if err != nil {
@ -726,11 +720,14 @@ func dirMatchesOneOf(dir, home, goos string, regexes []string) bool {
} }
for _, element := range regexes { for _, element := range regexes {
normalizedElement := strings.ReplaceAll(element, "\\\\", "/") normalized := strings.ReplaceAll(element, "\\\\", "/")
if strings.HasPrefix(normalizedElement, "~") { if strings.HasPrefix(normalized, "~") {
normalizedElement = strings.Replace(normalizedElement, "~", home, 1) rem := normalized[1:]
if len(rem) == 0 || rem[0] == '/' {
normalized = home + rem
}
} }
pattern := fmt.Sprintf("^%s$", normalizedElement) pattern := fmt.Sprintf("^%s$", normalized)
if goos == WINDOWS || goos == DARWIN { if goos == WINDOWS || goos == DARWIN {
pattern = "(?i)" + pattern pattern = "(?i)" + pattern
} }
@ -843,9 +840,19 @@ func Base(env Environment, path string) string {
return path return path
} }
func ReplaceTildePrefixWithHomeDir(env Environment, path string) string {
if !strings.HasPrefix(path, "~") {
return path
}
rem := path[1:]
if len(rem) == 0 || IsPathSeparator(env, rem[0]) {
return env.Home() + rem
}
return path
}
func ReplaceHomeDirPrefixWithTilde(env Environment, path string) string { func ReplaceHomeDirPrefixWithTilde(env Environment, path string) string {
home := env.Home() home := env.Home()
// match Home directory exactly
if !strings.HasPrefix(path, home) { if !strings.HasPrefix(path, home) {
return path return path
} }

View file

@ -141,7 +141,7 @@ will not be rendered when in one of the excluded locations.
``` ```
The strings specified in these properties are evaluated as [regular expressions][regex]. You The strings specified in these properties are evaluated as [regular expressions][regex]. You
can use any valid regular expression construct, but the regular expression must match the entire directory can use any valid regular expression construct, but the regular expression must match the **ENTIRE** directory
name. The following will match `/Users/posh/Projects/Foo` but not `/home/Users/posh/Projects/Foo`. name. The following will match `/Users/posh/Projects/Foo` but not `/home/Users/posh/Projects/Foo`.
```json ```json
@ -165,8 +165,8 @@ You can also combine these properties:
- Oh My Posh will accept both `/` and `\` as path separators for a folder and will match regardless of which - Oh My Posh will accept both `/` and `\` as path separators for a folder and will match regardless of which
is used by the current operating system. is used by the current operating system.
- Because the strings are evaluated as regular expressions, if you want to use a `\` in a Windows - Because the strings are evaluated as regular expressions, if you want to use a backslash (`\`) in a Windows
directory name, you need to specify it as `\\\\`. directory name, you need to specify it as double backslashes, and if using JSON format you should escape it as `\\\\`.
- The character `~` at the start of a specified folder will match the user's home directory. - The character `~` at the start of a specified folder will match the user's home directory.
- The comparison is case-insensitive on Windows and macOS, but case-sensitive on other operating systems. - The comparison is case-insensitive on Windows and macOS, but case-sensitive on other operating systems.