mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-11-10 04:54:03 -08:00
refactor(config): extract to module
This commit is contained in:
parent
1855a786dc
commit
3c6abf6163
|
@ -7,7 +7,7 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -38,12 +38,12 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
|
|||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
},
|
||||
}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
if len(output) == 0 && len(format) == 0 {
|
||||
// usage error
|
||||
|
@ -69,11 +69,11 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
|
|||
|
||||
switch format {
|
||||
case "json", "jsonc":
|
||||
format = engine.JSON
|
||||
format = config.JSON
|
||||
case "toml", "tml":
|
||||
format = engine.TOML
|
||||
format = config.TOML
|
||||
case "yaml", "yml":
|
||||
format = engine.YAML
|
||||
format = config.YAML
|
||||
default:
|
||||
// data error
|
||||
os.Exit(65)
|
||||
|
|
|
@ -3,6 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
|
@ -49,7 +50,7 @@ Exports the config to an image file using customized output options.`,
|
|||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
Shell: shell.GENERIC,
|
||||
TerminalWidth: 150,
|
||||
},
|
||||
|
@ -57,7 +58,7 @@ Exports the config to an image file using customized output options.`,
|
|||
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
// set sane defaults for things we don't print
|
||||
cfg.ConsoleTitleTemplate = ""
|
||||
|
|
|
@ -3,7 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -41,13 +41,13 @@ A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
|
|||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
Migrate: true,
|
||||
},
|
||||
}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
if write {
|
||||
cfg.BackupAndMigrate()
|
||||
return
|
||||
|
|
|
@ -3,7 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -36,13 +36,13 @@ A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
|
|||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
},
|
||||
}
|
||||
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
cfg.MigrateGlyphs = true
|
||||
if len(format) == 0 {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/build"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
|
@ -24,7 +25,7 @@ var debugCmd = &cobra.Command{
|
|||
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
Debug: true,
|
||||
PWD: pwd,
|
||||
Shell: shellName,
|
||||
|
@ -35,7 +36,7 @@ var debugCmd = &cobra.Command{
|
|||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
// add variables to the environment
|
||||
env.Var = cfg.Var
|
||||
|
|
|
@ -3,7 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
@ -58,7 +58,7 @@ func runInit(shellName string) {
|
|||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Shell: shellName,
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
Strict: strict,
|
||||
Manual: manual,
|
||||
},
|
||||
|
@ -66,7 +66,7 @@ func runInit(shellName string) {
|
|||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
shell.Transient = cfg.TransientPrompt != nil
|
||||
shell.ErrorLine = cfg.ErrorLine != nil || cfg.ValidLine != nil
|
||||
|
@ -79,7 +79,8 @@ func runInit(shellName string) {
|
|||
if !cfg.DisableCursorPositioning && (i == 0 && block.Newline) {
|
||||
shell.CursorPositioning = true
|
||||
}
|
||||
if block.Type == engine.RPrompt {
|
||||
|
||||
if block.Type == config.RPrompt {
|
||||
shell.RPrompt = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ var printCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
flags := &platform.Flags{
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
PWD: pwd,
|
||||
PSWD: pswd,
|
||||
ErrorCode: status,
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
config string
|
||||
configFlag string
|
||||
displayVersion bool
|
||||
)
|
||||
|
||||
|
@ -48,7 +48,7 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
RootCmd.PersistentFlags().StringVarP(&config, "config", "c", "", "config file path")
|
||||
RootCmd.PersistentFlags().StringVarP(&configFlag, "config", "c", "", "config file path")
|
||||
RootCmd.Flags().BoolVarP(&initialize, "init", "i", false, "init (deprecated)")
|
||||
RootCmd.Flags().BoolVar(&displayVersion, "version", false, "version")
|
||||
RootCmd.Flags().StringVarP(&shellName, "shell", "s", "", "shell (deprecated)")
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"runtime"
|
||||
"slices"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
|
@ -48,7 +48,7 @@ var upgradeCmd = &cobra.Command{
|
|||
return
|
||||
}
|
||||
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
if _, hasNotice := upgrade.Notice(env, true); !hasNotice {
|
||||
if !cfg.DisableNotice {
|
||||
|
|
107
src/config/backup.go
Normal file
107
src/config/backup.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
func (cfg *Config) Backup() {
|
||||
dst := cfg.origin + ".bak"
|
||||
source, err := os.Open(cfg.origin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer source.Close()
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer destination.Close()
|
||||
_, err = io.Copy(destination, source)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) Export(format string) string {
|
||||
if len(format) != 0 {
|
||||
cfg.Format = format
|
||||
}
|
||||
|
||||
var result bytes.Buffer
|
||||
|
||||
switch cfg.Format {
|
||||
case YAML:
|
||||
prefix := "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
|
||||
yamlEncoder := yaml.NewEncoder(&result)
|
||||
|
||||
err := yamlEncoder.Encode(cfg)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return prefix + result.String()
|
||||
case JSON:
|
||||
jsonEncoder := json.NewEncoder(&result)
|
||||
jsonEncoder.SetEscapeHTML(false)
|
||||
jsonEncoder.SetIndent("", " ")
|
||||
_ = jsonEncoder.Encode(cfg)
|
||||
prefix := "{\n \"$schema\": \"https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\","
|
||||
data := strings.Replace(result.String(), "{", prefix, 1)
|
||||
return escapeGlyphs(data, cfg.MigrateGlyphs)
|
||||
case TOML:
|
||||
prefix := "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
|
||||
tomlEncoder := toml.NewEncoder(&result)
|
||||
tomlEncoder.SetIndentTables(true)
|
||||
|
||||
err := tomlEncoder.Encode(cfg)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return prefix + result.String()
|
||||
}
|
||||
|
||||
// unsupported format
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cfg *Config) BackupAndMigrate() {
|
||||
cfg.Backup()
|
||||
cfg.Migrate()
|
||||
cfg.Write(cfg.Format)
|
||||
}
|
||||
|
||||
func (cfg *Config) Write(format string) {
|
||||
content := cfg.Export(format)
|
||||
if len(content) == 0 {
|
||||
// we are unable to perform the export
|
||||
os.Exit(65)
|
||||
return
|
||||
}
|
||||
|
||||
destination := cfg.Output
|
||||
if len(destination) == 0 {
|
||||
destination = cfg.origin
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(destination, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
_, err = f.WriteString(content)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
119
src/config/block.go
Normal file
119
src/config/block.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
)
|
||||
|
||||
// BlockType type of block
|
||||
type BlockType string
|
||||
|
||||
// BlockAlignment aligment of a Block
|
||||
type BlockAlignment string
|
||||
|
||||
// Overflow defines how to handle a right block that overflows with the previous block
|
||||
type Overflow string
|
||||
|
||||
const (
|
||||
// Prompt writes one or more Segments
|
||||
Prompt BlockType = "prompt"
|
||||
// LineBreak creates a line break in the prompt
|
||||
LineBreak BlockType = "newline"
|
||||
// RPrompt is a right aligned prompt
|
||||
RPrompt BlockType = "rprompt"
|
||||
// Left aligns left
|
||||
Left BlockAlignment = "left"
|
||||
// Right aligns right
|
||||
Right BlockAlignment = "right"
|
||||
// Break adds a line break
|
||||
Break Overflow = "break"
|
||||
// Hide hides the block
|
||||
Hide Overflow = "hide"
|
||||
)
|
||||
|
||||
// Block defines a part of the prompt with optional segments
|
||||
type Block struct {
|
||||
Type BlockType `json:"type,omitempty" toml:"type,omitempty"`
|
||||
Alignment BlockAlignment `json:"alignment,omitempty" toml:"alignment,omitempty"`
|
||||
Segments []*Segment `json:"segments,omitempty" toml:"segments,omitempty"`
|
||||
Newline bool `json:"newline,omitempty" toml:"newline,omitempty"`
|
||||
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
|
||||
Overflow Overflow `json:"overflow,omitempty" toml:"overflow,omitempty"`
|
||||
|
||||
LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"`
|
||||
TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"`
|
||||
|
||||
// Deprecated: keep the logic for legacy purposes
|
||||
HorizontalOffset int `json:"horizontal_offset,omitempty" toml:"horizontal_offset,omitempty"`
|
||||
VerticalOffset int `json:"vertical_offset,omitempty" toml:"vertical_offset,omitempty"`
|
||||
|
||||
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
|
||||
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
|
||||
|
||||
env platform.Environment
|
||||
}
|
||||
|
||||
func (b *Block) Init(env platform.Environment) {
|
||||
b.env = env
|
||||
b.executeSegmentLogic()
|
||||
}
|
||||
|
||||
func (b *Block) InitPlain(env platform.Environment, config *Config) {
|
||||
terminal.Init(shell.GENERIC)
|
||||
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, config.TerminalBackground)
|
||||
terminal.AnsiColors = config.MakeColors()
|
||||
|
||||
b.env = env
|
||||
b.executeSegmentLogic()
|
||||
}
|
||||
|
||||
func (b *Block) Enabled() bool {
|
||||
if b.Type == LineBreak {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, segment := range b.Segments {
|
||||
if segment.Enabled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Block) executeSegmentLogic() {
|
||||
if shouldHideForWidth(b.env, b.MinWidth, b.MaxWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
b.setEnabledSegments()
|
||||
b.setSegmentsText()
|
||||
}
|
||||
|
||||
func (b *Block) setEnabledSegments() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.SetEnabled(b.env)
|
||||
}(segment)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) setSegmentsText() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.setText()
|
||||
}(segment)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
75
src/config/config.go
Normal file
75
src/config/config.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
)
|
||||
|
||||
const (
|
||||
JSON string = "json"
|
||||
YAML string = "yaml"
|
||||
TOML string = "toml"
|
||||
|
||||
Version = 2
|
||||
)
|
||||
|
||||
// Config holds all the theme for rendering the prompt
|
||||
type Config struct {
|
||||
Version int `json:"version" toml:"version"`
|
||||
FinalSpace bool `json:"final_space,omitempty" toml:"final_space,omitempty"`
|
||||
ConsoleTitleTemplate string `json:"console_title_template,omitempty" toml:"console_title_template,omitempty"`
|
||||
TerminalBackground string `json:"terminal_background,omitempty" toml:"terminal_background,omitempty"`
|
||||
AccentColor string `json:"accent_color,omitempty" toml:"accent_color,omitempty"`
|
||||
Blocks []*Block `json:"blocks,omitempty" toml:"blocks,omitempty"`
|
||||
Tooltips []*Segment `json:"tooltips,omitempty" toml:"tooltips,omitempty"`
|
||||
TransientPrompt *Segment `json:"transient_prompt,omitempty" toml:"transient_prompt,omitempty"`
|
||||
ValidLine *Segment `json:"valid_line,omitempty" toml:"valid_line,omitempty"`
|
||||
ErrorLine *Segment `json:"error_line,omitempty" toml:"error_line,omitempty"`
|
||||
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty" toml:"secondary_prompt,omitempty"`
|
||||
DebugPrompt *Segment `json:"debug_prompt,omitempty" toml:"debug_prompt,omitempty"`
|
||||
Palette terminal.Palette `json:"palette,omitempty" toml:"palette,omitempty"`
|
||||
Palettes *terminal.Palettes `json:"palettes,omitempty" toml:"palettes,omitempty"`
|
||||
Cycle terminal.Cycle `json:"cycle,omitempty" toml:"cycle,omitempty"`
|
||||
ShellIntegration bool `json:"shell_integration,omitempty" toml:"shell_integration,omitempty"`
|
||||
PWD string `json:"pwd,omitempty" toml:"pwd,omitempty"`
|
||||
Var map[string]any `json:"var,omitempty" toml:"var,omitempty"`
|
||||
DisableCursorPositioning bool `json:"disable_cursor_positioning,omitempty" toml:"disable_cursor_positioning,omitempty"`
|
||||
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty" toml:"patch_pwsh_bleed,omitempty"`
|
||||
DisableNotice bool `json:"disable_notice,omitempty" toml:"disable_notice,omitempty"`
|
||||
AutoUpgrade bool `json:"auto_upgrade,omitempty" toml:"auto_upgrade,omitempty"`
|
||||
ITermFeatures terminal.ITermFeatures `json:"iterm_features,omitempty" toml:"iterm_features,omitempty"`
|
||||
|
||||
// Deprecated
|
||||
OSC99 bool `json:"osc99,omitempty" toml:"osc99,omitempty"`
|
||||
|
||||
Output string `json:"-" toml:"-"`
|
||||
MigrateGlyphs bool `json:"-" toml:"-"`
|
||||
Format string `json:"-" toml:"-"`
|
||||
|
||||
origin string
|
||||
// eval bool
|
||||
updated bool
|
||||
env platform.Environment
|
||||
}
|
||||
|
||||
func (cfg *Config) MakeColors() terminal.ColorString {
|
||||
cacheDisabled := cfg.env.Getenv("OMP_CACHE_DISABLED") == "1"
|
||||
return terminal.MakeColors(cfg.getPalette(), !cacheDisabled, cfg.AccentColor, cfg.env)
|
||||
}
|
||||
|
||||
func (cfg *Config) getPalette() terminal.Palette {
|
||||
if cfg.Palettes == nil {
|
||||
return cfg.Palette
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: cfg.Palettes.Template,
|
||||
Env: cfg.env,
|
||||
}
|
||||
if palette, err := tmpl.Render(); err == nil {
|
||||
if p, ok := cfg.Palettes.List[palette]; ok {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return cfg.Palette
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
@ -11,25 +11,6 @@ import (
|
|||
mock2 "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestEscapeGlyphs(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Expected string
|
||||
}{
|
||||
{Input: "", Expected: "\\udb80\\ude4b"},
|
||||
{Input: "a", Expected: "a"},
|
||||
{Input: "\ue0b4", Expected: "\\ue0b4"},
|
||||
{Input: "\ufd03", Expected: "\\ufd03"},
|
||||
{Input: "}", Expected: "}"},
|
||||
{Input: "🏚", Expected: "🏚"},
|
||||
{Input: "\U000F011B", Expected: "\\udb80\\udd1b"},
|
||||
{Input: "", Expected: "\\udb80\\udd1b"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.Expected, escapeGlyphs(tc.Input, false), tc.Input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPalette(t *testing.T) {
|
||||
palette := terminal.Palette{
|
||||
"red": "#ff0000",
|
199
src/config/default.go
Normal file
199
src/config/default.go
Normal file
|
@ -0,0 +1,199 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
)
|
||||
|
||||
func Default(env platform.Environment, warning bool) *Config {
|
||||
exitBackgroundTemplate := "{{ if gt .Code 0 }}p:red{{ end }}"
|
||||
exitTemplate := " {{ if gt .Code 0 }}\uf00d{{ else }}\uf00c{{ end }} "
|
||||
if warning {
|
||||
exitBackgroundTemplate = "p:red"
|
||||
exitTemplate = " CONFIG ERROR "
|
||||
}
|
||||
cfg := &Config{
|
||||
Version: 2,
|
||||
FinalSpace: true,
|
||||
Blocks: []*Block{
|
||||
{
|
||||
Type: Prompt,
|
||||
Alignment: Left,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: SESSION,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b6",
|
||||
TrailingDiamond: "\ue0b0",
|
||||
Background: "p:yellow",
|
||||
Foreground: "p:black",
|
||||
Template: " {{ if .SSHSession }}\ueba9 {{ end }}{{ .UserName }} ",
|
||||
},
|
||||
{
|
||||
Type: PATH,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:orange",
|
||||
Foreground: "p:white",
|
||||
Properties: properties.Map{
|
||||
properties.Style: "folder",
|
||||
},
|
||||
Template: " \uea83 {{ path .Path .Location }} ",
|
||||
},
|
||||
{
|
||||
Type: GIT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:green",
|
||||
BackgroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:yellow{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:red{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}#49416D{{ end }}",
|
||||
"{{ if gt .Behind 0 }}#7A306C{{ end }}",
|
||||
},
|
||||
Foreground: "p:black",
|
||||
ForegroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:black{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:white{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}p:white{{ end }}",
|
||||
},
|
||||
Properties: properties.Map{
|
||||
segments.BranchMaxLength: 25,
|
||||
segments.FetchStatus: true,
|
||||
segments.FetchUpstreamIcon: true,
|
||||
},
|
||||
Template: " {{ if .UpstreamURL }}{{ url .UpstreamIcon .UpstreamURL }} {{ end }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} \uf044 {{ .Working.String }}{{ end }}{{ if .Staging.Changed }} \uf046 {{ .Staging.String }}{{ end }} ", //nolint:lll
|
||||
},
|
||||
{
|
||||
Type: ROOT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:yellow",
|
||||
Foreground: "p:white",
|
||||
Template: " \uf0e7 ",
|
||||
},
|
||||
{
|
||||
Type: STATUS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "<transparent,background>\ue0b0</>",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:blue",
|
||||
BackgroundTemplates: []string{
|
||||
exitBackgroundTemplate,
|
||||
},
|
||||
Foreground: "p:white",
|
||||
Properties: properties.Map{
|
||||
properties.AlwaysEnabled: true,
|
||||
},
|
||||
Template: exitTemplate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: RPrompt,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: NODE,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:green",
|
||||
Template: "\ue718 ",
|
||||
Properties: properties.Map{
|
||||
segments.HomeEnabled: false,
|
||||
segments.FetchPackageManager: false,
|
||||
segments.DisplayMode: "files",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: GOLANG,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:blue",
|
||||
Template: "\ue626 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: PYTHON,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:yellow",
|
||||
Template: "\ue235 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
segments.DisplayMode: "files",
|
||||
segments.FetchVirtualEnv: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: SHELL,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:white",
|
||||
Template: "in <p:blue><b>{{ .Name }}</b></> ",
|
||||
},
|
||||
{
|
||||
Type: TIME,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:white",
|
||||
Template: "at <p:blue><b>{{ .CurrentDate | date \"15:04:05\" }}</b></>",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ConsoleTitleTemplate: "{{ .Shell }} in {{ .Folder }}",
|
||||
Palette: terminal.Palette{
|
||||
"black": "#262B44",
|
||||
"blue": "#4B95E9",
|
||||
"green": "#59C9A5",
|
||||
"orange": "#F07623",
|
||||
"red": "#D81E5B",
|
||||
"white": "#E0DEF4",
|
||||
"yellow": "#F3AE35",
|
||||
},
|
||||
SecondaryPrompt: &Segment{
|
||||
Background: "transparent",
|
||||
Foreground: "p:black",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> > </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
TransientPrompt: &Segment{
|
||||
Background: "transparent",
|
||||
Foreground: "p:black",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> {{ .Folder }} </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
Tooltips: []*Segment{
|
||||
{
|
||||
Type: AWS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:orange",
|
||||
Foreground: "p:white",
|
||||
Template: " \ue7ad {{ .Profile }}{{ if .Region }}@{{ .Region }}{{ end }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"aws"},
|
||||
},
|
||||
{
|
||||
Type: AZ,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:blue",
|
||||
Foreground: "p:white",
|
||||
Template: " \uebd8 {{ .Name }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"az"},
|
||||
},
|
||||
},
|
||||
}
|
||||
cfg.env = env
|
||||
return cfg
|
||||
}
|
95
src/config/load.go
Normal file
95
src/config/load.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gookit/goutil/jsonutil"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
// LoadConfig returns the default configuration including possible user overrides
|
||||
func Load(env platform.Environment) *Config {
|
||||
cfg := loadConfig(env)
|
||||
|
||||
// only migrate automatically when the switch isn't set
|
||||
if !env.Flags().Migrate && cfg.Version < Version {
|
||||
cfg.BackupAndMigrate()
|
||||
}
|
||||
|
||||
if !cfg.ShellIntegration {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// bash - ok
|
||||
// fish - ok
|
||||
// pwsh - ok
|
||||
// zsh - ok
|
||||
// cmd - ok, as of v1.4.25 (chrisant996/clink#457, fixed in chrisant996/clink@8a5d7ea)
|
||||
// nu - built-in (and bugged) feature - nushell/nushell#5585, https://www.nushell.sh/blog/2022-08-16-nushell-0_67.html#shell-integration-fdncred-and-tyriar
|
||||
// elv - broken OSC sequences
|
||||
// xonsh - broken OSC sequences
|
||||
// tcsh - overall broken, FTCS_COMMAND_EXECUTED could be added to POSH_POSTCMD in the future
|
||||
switch env.Shell() {
|
||||
case shell.ELVISH, shell.XONSH, shell.TCSH, shell.NU:
|
||||
cfg.ShellIntegration = false
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func loadConfig(env platform.Environment) *Config {
|
||||
defer env.Trace(time.Now())
|
||||
configFile := env.Flags().Config
|
||||
|
||||
if len(configFile) == 0 {
|
||||
env.Debug("no config file specified, using default")
|
||||
return Default(env, false)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
cfg.origin = configFile
|
||||
cfg.Format = strings.TrimPrefix(filepath.Ext(configFile), ".")
|
||||
cfg.env = env
|
||||
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
env.DebugF("error reading config file: %s", err)
|
||||
return Default(env, true)
|
||||
}
|
||||
|
||||
switch cfg.Format {
|
||||
case "yml", "yaml":
|
||||
cfg.Format = YAML
|
||||
err = yaml.Unmarshal(data, &cfg)
|
||||
case "jsonc", "json":
|
||||
cfg.Format = JSON
|
||||
|
||||
str := jsonutil.StripComments(string(data))
|
||||
data = []byte(str)
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
err = decoder.Decode(&cfg)
|
||||
case "toml", "tml":
|
||||
cfg.Format = TOML
|
||||
err = toml.Unmarshal(data, &cfg)
|
||||
default:
|
||||
err = fmt.Errorf("unsupported config file format: %s", cfg.Format)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
env.DebugF("error decoding config file: %s", err)
|
||||
return Default(env, true)
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -30,7 +30,7 @@ func (cfg *Config) Migrate() {
|
|||
cfg.ConsoleTitleTemplate = strings.ReplaceAll(cfg.ConsoleTitleTemplate, ".Path", ".PWD")
|
||||
}
|
||||
cfg.updated = true
|
||||
cfg.Version = configVersion
|
||||
cfg.Version = Version
|
||||
}
|
||||
|
||||
func (segment *Segment) migrate(env platform.Environment, version int) {
|
||||
|
@ -43,7 +43,7 @@ func (segment *Segment) migrate(env platform.Environment, version int) {
|
|||
}
|
||||
|
||||
func (segment *Segment) migrationOne(env platform.Environment) {
|
||||
if err := segment.mapSegmentWithWriter(env); err != nil {
|
||||
if err := segment.MapSegmentWithWriter(env); err != nil {
|
||||
return
|
||||
}
|
||||
// General properties that need replacement
|
||||
|
@ -163,7 +163,7 @@ func (segment *Segment) migrationOne(env platform.Environment) {
|
|||
}
|
||||
|
||||
func (segment *Segment) migrationTwo(env platform.Environment) {
|
||||
if err := segment.mapSegmentWithWriter(env); err != nil {
|
||||
if err := segment.MapSegmentWithWriter(env); err != nil {
|
||||
return
|
||||
}
|
||||
if !segment.hasProperty(segmentTemplate) {
|
140
src/config/migrate_glyphs.go
Normal file
140
src/config/migrate_glyphs.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform/net"
|
||||
)
|
||||
|
||||
type ConnectionError struct {
|
||||
reason string
|
||||
}
|
||||
|
||||
func (f *ConnectionError) Error() string {
|
||||
return f.reason
|
||||
}
|
||||
|
||||
type codePoints map[uint64]uint64
|
||||
|
||||
func getGlyphCodePoints() (codePoints, error) {
|
||||
var codePoints = make(codePoints)
|
||||
|
||||
ctx, cncl := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
|
||||
defer cncl()
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://ohmyposh.dev/codepoints.csv", nil)
|
||||
if err != nil {
|
||||
return codePoints, &ConnectionError{reason: err.Error()}
|
||||
}
|
||||
|
||||
response, err := net.HTTPClient.Do(request)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
bytes, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(bytes), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ",")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
oldGlyph, err := strconv.ParseUint(fields[0], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newGlyph, err := strconv.ParseUint(fields[1], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
codePoints[oldGlyph] = newGlyph
|
||||
}
|
||||
|
||||
return codePoints, nil
|
||||
}
|
||||
|
||||
func escapeGlyphs(s string, migrate bool) string {
|
||||
shouldExclude := func(r rune) bool {
|
||||
if r < 0x1000 { // Basic Multilingual Plane
|
||||
return true
|
||||
}
|
||||
if r > 0x1F600 && r < 0x1F64F { // Emoticons
|
||||
return true
|
||||
}
|
||||
if r > 0x1F300 && r < 0x1F5FF { // Misc Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F680 && r < 0x1F6FF { // Transport and Map
|
||||
return true
|
||||
}
|
||||
if r > 0x2600 && r < 0x26FF { // Misc symbols
|
||||
return true
|
||||
}
|
||||
if r > 0x2700 && r < 0x27BF { // Dingbats
|
||||
return true
|
||||
}
|
||||
if r > 0xFE00 && r < 0xFE0F { // Variation Selectors
|
||||
return true
|
||||
}
|
||||
if r > 0x1F900 && r < 0x1F9FF { // Supplemental Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F1E6 && r < 0x1F1FF { // Flags
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var cp codePoints
|
||||
var err error
|
||||
if migrate {
|
||||
cp, err = getGlyphCodePoints()
|
||||
if err != nil {
|
||||
migrate = false
|
||||
}
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
for _, r := range s {
|
||||
// exclude regular characters and emojis
|
||||
if shouldExclude(r) {
|
||||
builder.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
if migrate {
|
||||
if val, OK := cp[uint64(r)]; OK {
|
||||
r = rune(val)
|
||||
}
|
||||
}
|
||||
|
||||
if r > 0x10000 {
|
||||
// calculate surrogate pairs
|
||||
one := 0xd800 + (((r - 0x10000) >> 10) & 0x3ff)
|
||||
two := 0xdc00 + ((r - 0x10000) & 0x3ff)
|
||||
quoted := fmt.Sprintf("\\u%04x\\u%04x", one, two)
|
||||
builder.WriteString(quoted)
|
||||
continue
|
||||
}
|
||||
|
||||
quoted := fmt.Sprintf("\\u%04x", r)
|
||||
builder.WriteString(quoted)
|
||||
}
|
||||
return builder.String()
|
||||
}
|
35
src/config/migrate_glyphs_test.go
Normal file
35
src/config/migrate_glyphs_test.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetCodePoints(t *testing.T) {
|
||||
codepoints, err := getGlyphCodePoints()
|
||||
if connectionError, ok := err.(*ConnectionError); ok {
|
||||
t.Log(connectionError.Error())
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 1939, len(codepoints))
|
||||
}
|
||||
|
||||
func TestEscapeGlyphs(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Expected string
|
||||
}{
|
||||
{Input: "", Expected: "\\udb80\\ude4b"},
|
||||
{Input: "a", Expected: "a"},
|
||||
{Input: "\ue0b4", Expected: "\\ue0b4"},
|
||||
{Input: "\ufd03", Expected: "\\ufd03"},
|
||||
{Input: "}", Expected: "}"},
|
||||
{Input: "🏚", Expected: "🏚"},
|
||||
{Input: "\U000F011B", Expected: "\\udb80\\udd1b"},
|
||||
{Input: "", Expected: "\\udb80\\udd1b"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.Expected, escapeGlyphs(tc.Input, false), tc.Input)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import "github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
260
src/config/segment.go
Normal file
260
src/config/segment.go
Normal file
|
@ -0,0 +1,260 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
||||
c "golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// SegmentStyle the style of segment, for more information, see the constants
|
||||
type SegmentStyle string
|
||||
|
||||
func (s *SegmentStyle) resolve(env platform.Environment, context any) SegmentStyle {
|
||||
txtTemplate := &template.Text{
|
||||
Context: context,
|
||||
Env: env,
|
||||
}
|
||||
txtTemplate.Template = string(*s)
|
||||
value, err := txtTemplate.Render()
|
||||
// default to Plain
|
||||
if err != nil || len(value) == 0 {
|
||||
return Plain
|
||||
}
|
||||
return SegmentStyle(value)
|
||||
}
|
||||
|
||||
type Segment struct {
|
||||
Type SegmentType `json:"type,omitempty" toml:"type,omitempty"`
|
||||
Tips []string `json:"tips,omitempty" toml:"tips,omitempty"`
|
||||
Style SegmentStyle `json:"style,omitempty" toml:"style,omitempty"`
|
||||
PowerlineSymbol string `json:"powerline_symbol,omitempty" toml:"powerline_symbol,omitempty"`
|
||||
LeadingPowerlineSymbol string `json:"leading_powerline_symbol,omitempty" toml:"leading_powerline_symbol,omitempty"`
|
||||
InvertPowerline bool `json:"invert_powerline,omitempty" toml:"invert_powerline,omitempty"`
|
||||
Foreground string `json:"foreground,omitempty" toml:"foreground,omitempty"`
|
||||
ForegroundTemplates template.List `json:"foreground_templates,omitempty" toml:"foreground_templates,omitempty"`
|
||||
Background string `json:"background,omitempty" toml:"background,omitempty"`
|
||||
BackgroundTemplates template.List `json:"background_templates,omitempty" toml:"background_templates,omitempty"`
|
||||
LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"`
|
||||
TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"`
|
||||
Template string `json:"template,omitempty" toml:"template,omitempty"`
|
||||
Templates template.List `json:"templates,omitempty" toml:"templates,omitempty"`
|
||||
TemplatesLogic template.Logic `json:"templates_logic,omitempty" toml:"templates_logic,omitempty"`
|
||||
Properties properties.Map `json:"properties,omitempty" toml:"properties,omitempty"`
|
||||
Interactive bool `json:"interactive,omitempty" toml:"interactive,omitempty"`
|
||||
Alias string `json:"alias,omitempty" toml:"alias,omitempty"`
|
||||
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
|
||||
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
|
||||
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
|
||||
|
||||
Enabled bool `json:"-" toml:"-"`
|
||||
|
||||
Colors *terminal.Colors
|
||||
Text string
|
||||
|
||||
env platform.Environment
|
||||
writer SegmentWriter
|
||||
styleCache SegmentStyle
|
||||
name string
|
||||
|
||||
// debug info
|
||||
Duration time.Duration
|
||||
NameLength int
|
||||
}
|
||||
|
||||
func (segment *Segment) Name() string {
|
||||
if len(segment.name) != 0 {
|
||||
return segment.name
|
||||
}
|
||||
|
||||
name := segment.Alias
|
||||
if len(name) == 0 {
|
||||
name = c.Title(language.English).String(string(segment.Type))
|
||||
}
|
||||
|
||||
segment.name = name
|
||||
return name
|
||||
}
|
||||
|
||||
func (segment *Segment) SetEnabled(env platform.Environment) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
// display a message explaining omp failed(with the err)
|
||||
message := fmt.Sprintf("\noh-my-posh fatal error rendering %s segment:%s\n\n%s\n", segment.Type, err, debug.Stack())
|
||||
fmt.Println(message)
|
||||
segment.Enabled = true
|
||||
}()
|
||||
|
||||
// segment timings for debug purposes
|
||||
var start time.Time
|
||||
if env.Flags().Debug {
|
||||
start = time.Now()
|
||||
segment.NameLength = len(segment.Name())
|
||||
defer func() {
|
||||
segment.Duration = time.Since(start)
|
||||
}()
|
||||
}
|
||||
|
||||
err := segment.MapSegmentWithWriter(env)
|
||||
if err != nil || !segment.shouldIncludeFolder() {
|
||||
return
|
||||
}
|
||||
|
||||
segment.env.DebugF("Segment: %s", segment.Name())
|
||||
|
||||
// validate toggles
|
||||
if toggles, OK := segment.env.Cache().Get(platform.TOGGLECACHE); OK && len(toggles) > 0 {
|
||||
list := strings.Split(toggles, ",")
|
||||
for _, toggle := range list {
|
||||
if SegmentType(toggle) == segment.Type || toggle == segment.Alias {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shouldHideForWidth(segment.env, segment.MinWidth, segment.MaxWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
if segment.writer.Enabled() {
|
||||
segment.Enabled = true
|
||||
env.TemplateCache().AddSegmentData(segment.Name(), segment.writer)
|
||||
}
|
||||
}
|
||||
|
||||
func (segment *Segment) setText() {
|
||||
if !segment.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
segment.Text = segment.string()
|
||||
segment.Enabled = len(strings.ReplaceAll(segment.Text, " ", "")) > 0
|
||||
|
||||
if !segment.Enabled {
|
||||
segment.env.TemplateCache().RemoveSegmentData(segment.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (segment *Segment) string() string {
|
||||
var templatesResult string
|
||||
if !segment.Templates.Empty() {
|
||||
templatesResult = segment.Templates.Resolve(segment.writer, segment.env, "", segment.TemplatesLogic)
|
||||
if len(segment.Template) == 0 {
|
||||
return templatesResult
|
||||
}
|
||||
}
|
||||
|
||||
if len(segment.Template) == 0 {
|
||||
segment.Template = segment.writer.Template()
|
||||
}
|
||||
|
||||
tmpl := &template.Text{
|
||||
Template: segment.Template,
|
||||
Context: segment.writer,
|
||||
Env: segment.env,
|
||||
TemplatesResult: templatesResult,
|
||||
}
|
||||
|
||||
text, err := tmpl.Render()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func (segment *Segment) shouldIncludeFolder() bool {
|
||||
if segment.env == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
cwdIncluded := segment.cwdIncluded()
|
||||
cwdExcluded := segment.cwdExcluded()
|
||||
|
||||
return cwdIncluded && !cwdExcluded
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdIncluded() bool {
|
||||
value, ok := segment.Properties[properties.IncludeFolders]
|
||||
if !ok {
|
||||
// IncludeFolders isn't specified, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
list := properties.ParseStringArray(value)
|
||||
|
||||
if len(list) == 0 {
|
||||
// IncludeFolders is an empty array, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdExcluded() bool {
|
||||
value, ok := segment.Properties[properties.ExcludeFolders]
|
||||
if !ok {
|
||||
value = segment.Properties[properties.IgnoreFolders]
|
||||
}
|
||||
|
||||
list := properties.ParseStringArray(value)
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) ResolveForeground() string {
|
||||
if segment.Colors == nil {
|
||||
segment.Colors = &terminal.Colors{}
|
||||
}
|
||||
|
||||
if len(segment.Colors.Foreground) == 0 {
|
||||
segment.Colors.Foreground = segment.ForegroundTemplates.FirstMatch(segment.writer, segment.env, segment.Foreground)
|
||||
}
|
||||
|
||||
return segment.Colors.Foreground
|
||||
}
|
||||
|
||||
func (segment *Segment) ResolveBackground() string {
|
||||
if segment.Colors == nil {
|
||||
segment.Colors = &terminal.Colors{}
|
||||
}
|
||||
|
||||
if len(segment.Colors.Background) == 0 {
|
||||
segment.Colors.Background = segment.BackgroundTemplates.FirstMatch(segment.writer, segment.env, segment.Background)
|
||||
}
|
||||
|
||||
return segment.Colors.Background
|
||||
}
|
||||
|
||||
func (segment *Segment) ResolveStyle() SegmentStyle {
|
||||
if len(segment.styleCache) != 0 {
|
||||
return segment.styleCache
|
||||
}
|
||||
|
||||
segment.styleCache = segment.Style.resolve(segment.env, segment.writer)
|
||||
|
||||
return segment.styleCache
|
||||
}
|
||||
|
||||
func (segment *Segment) IsPowerline() bool {
|
||||
style := segment.ResolveStyle()
|
||||
return style == Powerline || style == Accordion
|
||||
}
|
||||
|
||||
func (segment *Segment) HasEmptyDiamondAtEnd() bool {
|
||||
if segment.ResolveStyle() != Diamond {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(segment.TrailingDiamond) == 0
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -22,7 +22,7 @@ func TestMapSegmentWriterCanMap(t *testing.T) {
|
|||
Type: SESSION,
|
||||
}
|
||||
env := new(mock.MockedEnvironment)
|
||||
err := sc.mapSegmentWithWriter(env)
|
||||
err := sc.MapSegmentWithWriter(env)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, sc.writer)
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func TestMapSegmentWriterCannotMap(t *testing.T) {
|
|||
Type: "nilwriter",
|
||||
}
|
||||
env := new(mock.MockedEnvironment)
|
||||
err := sc.mapSegmentWithWriter(env)
|
||||
err := sc.MapSegmentWithWriter(env)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
@ -157,13 +157,13 @@ func TestGetColors(t *testing.T) {
|
|||
if tc.Background {
|
||||
segment.Background = tc.DefaultColor
|
||||
segment.BackgroundTemplates = tc.Templates
|
||||
color := segment.background()
|
||||
color := segment.ResolveBackground()
|
||||
assert.Equal(t, tc.ExpectedColor, color, tc.Case)
|
||||
continue
|
||||
}
|
||||
segment.Foreground = tc.DefaultColor
|
||||
segment.ForegroundTemplates = tc.Templates
|
||||
color := segment.foreground()
|
||||
color := segment.ResolveForeground()
|
||||
assert.Equal(t, tc.ExpectedColor, color, tc.Case)
|
||||
}
|
||||
}
|
|
@ -1,59 +1,15 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
||||
c "golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Segment represent a single segment and it's configuration
|
||||
type Segment struct {
|
||||
Type SegmentType `json:"type,omitempty" toml:"type,omitempty"`
|
||||
Tips []string `json:"tips,omitempty" toml:"tips,omitempty"`
|
||||
Style SegmentStyle `json:"style,omitempty" toml:"style,omitempty"`
|
||||
PowerlineSymbol string `json:"powerline_symbol,omitempty" toml:"powerline_symbol,omitempty"`
|
||||
LeadingPowerlineSymbol string `json:"leading_powerline_symbol,omitempty" toml:"leading_powerline_symbol,omitempty"`
|
||||
InvertPowerline bool `json:"invert_powerline,omitempty" toml:"invert_powerline,omitempty"`
|
||||
Foreground string `json:"foreground,omitempty" toml:"foreground,omitempty"`
|
||||
ForegroundTemplates template.List `json:"foreground_templates,omitempty" toml:"foreground_templates,omitempty"`
|
||||
Background string `json:"background,omitempty" toml:"background,omitempty"`
|
||||
BackgroundTemplates template.List `json:"background_templates,omitempty" toml:"background_templates,omitempty"`
|
||||
LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"`
|
||||
TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"`
|
||||
Template string `json:"template,omitempty" toml:"template,omitempty"`
|
||||
Templates template.List `json:"templates,omitempty" toml:"templates,omitempty"`
|
||||
TemplatesLogic template.Logic `json:"templates_logic,omitempty" toml:"templates_logic,omitempty"`
|
||||
Properties properties.Map `json:"properties,omitempty" toml:"properties,omitempty"`
|
||||
Interactive bool `json:"interactive,omitempty" toml:"interactive,omitempty"`
|
||||
Alias string `json:"alias,omitempty" toml:"alias,omitempty"`
|
||||
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
|
||||
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
|
||||
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
|
||||
|
||||
Enabled bool `json:"-" toml:"-"`
|
||||
|
||||
colors *terminal.Colors
|
||||
env platform.Environment
|
||||
writer SegmentWriter
|
||||
text string
|
||||
styleCache SegmentStyle
|
||||
name string
|
||||
|
||||
// debug info
|
||||
duration time.Duration
|
||||
nameLength int
|
||||
}
|
||||
// SegmentType the type of segment, for more information, see the constants
|
||||
type SegmentType string
|
||||
|
||||
// SegmentWriter is the interface used to define what and if to write to the prompt
|
||||
type SegmentWriter interface {
|
||||
|
@ -62,26 +18,6 @@ type SegmentWriter interface {
|
|||
Init(props properties.Properties, env platform.Environment)
|
||||
}
|
||||
|
||||
// SegmentStyle the style of segment, for more information, see the constants
|
||||
type SegmentStyle string
|
||||
|
||||
func (s *SegmentStyle) Resolve(env platform.Environment, context any) SegmentStyle {
|
||||
txtTemplate := &template.Text{
|
||||
Context: context,
|
||||
Env: env,
|
||||
}
|
||||
txtTemplate.Template = string(*s)
|
||||
value, err := txtTemplate.Render()
|
||||
// default to Plain
|
||||
if err != nil || len(value) == 0 {
|
||||
return Plain
|
||||
}
|
||||
return SegmentStyle(value)
|
||||
}
|
||||
|
||||
// SegmentType the type of segment, for more information, see the constants
|
||||
type SegmentType string
|
||||
|
||||
const (
|
||||
// Plain writes it without ornaments
|
||||
Plain SegmentStyle = "plain"
|
||||
|
@ -372,102 +308,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
|
|||
YTM: func() SegmentWriter { return &segments.Ytm{} },
|
||||
}
|
||||
|
||||
func (segment *Segment) style() SegmentStyle {
|
||||
if len(segment.styleCache) != 0 {
|
||||
return segment.styleCache
|
||||
}
|
||||
|
||||
segment.styleCache = segment.Style.Resolve(segment.env, segment.writer)
|
||||
|
||||
return segment.styleCache
|
||||
}
|
||||
|
||||
func (segment *Segment) shouldIncludeFolder() bool {
|
||||
if segment.env == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
cwdIncluded := segment.cwdIncluded()
|
||||
cwdExcluded := segment.cwdExcluded()
|
||||
|
||||
return cwdIncluded && !cwdExcluded
|
||||
}
|
||||
|
||||
func (segment *Segment) isPowerline() bool {
|
||||
style := segment.style()
|
||||
return style == Powerline || style == Accordion
|
||||
}
|
||||
|
||||
func (segment *Segment) hasEmptyDiamondAtEnd() bool {
|
||||
if segment.style() != Diamond {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(segment.TrailingDiamond) == 0
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdIncluded() bool {
|
||||
value, ok := segment.Properties[properties.IncludeFolders]
|
||||
if !ok {
|
||||
// IncludeFolders isn't specified, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
list := properties.ParseStringArray(value)
|
||||
|
||||
if len(list) == 0 {
|
||||
// IncludeFolders is an empty array, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdExcluded() bool {
|
||||
value, ok := segment.Properties[properties.ExcludeFolders]
|
||||
if !ok {
|
||||
value = segment.Properties[properties.IgnoreFolders]
|
||||
}
|
||||
|
||||
list := properties.ParseStringArray(value)
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) shouldInvokeWithTip(tip string) bool {
|
||||
for _, t := range segment.Tips {
|
||||
if t == tip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (segment *Segment) foreground() string {
|
||||
if segment.colors == nil {
|
||||
segment.colors = &terminal.Colors{}
|
||||
}
|
||||
|
||||
if len(segment.colors.Foreground) == 0 {
|
||||
segment.colors.Foreground = segment.ForegroundTemplates.FirstMatch(segment.writer, segment.env, segment.Foreground)
|
||||
}
|
||||
|
||||
return segment.colors.Foreground
|
||||
}
|
||||
|
||||
func (segment *Segment) background() string {
|
||||
if segment.colors == nil {
|
||||
segment.colors = &terminal.Colors{}
|
||||
}
|
||||
|
||||
if len(segment.colors.Background) == 0 {
|
||||
segment.colors.Background = segment.BackgroundTemplates.FirstMatch(segment.writer, segment.env, segment.Background)
|
||||
}
|
||||
|
||||
return segment.colors.Background
|
||||
}
|
||||
|
||||
func (segment *Segment) mapSegmentWithWriter(env platform.Environment) error {
|
||||
func (segment *Segment) MapSegmentWithWriter(env platform.Environment) error {
|
||||
segment.env = env
|
||||
|
||||
if segment.Properties == nil {
|
||||
|
@ -487,107 +328,3 @@ func (segment *Segment) mapSegmentWithWriter(env platform.Environment) error {
|
|||
|
||||
return errors.New("unable to map writer")
|
||||
}
|
||||
|
||||
func (segment *Segment) string() string {
|
||||
var templatesResult string
|
||||
if !segment.Templates.Empty() {
|
||||
templatesResult = segment.Templates.Resolve(segment.writer, segment.env, "", segment.TemplatesLogic)
|
||||
if len(segment.Template) == 0 {
|
||||
return templatesResult
|
||||
}
|
||||
}
|
||||
|
||||
if len(segment.Template) == 0 {
|
||||
segment.Template = segment.writer.Template()
|
||||
}
|
||||
|
||||
tmpl := &template.Text{
|
||||
Template: segment.Template,
|
||||
Context: segment.writer,
|
||||
Env: segment.env,
|
||||
TemplatesResult: templatesResult,
|
||||
}
|
||||
|
||||
text, err := tmpl.Render()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func (segment *Segment) Name() string {
|
||||
if len(segment.name) != 0 {
|
||||
return segment.name
|
||||
}
|
||||
|
||||
name := segment.Alias
|
||||
if len(name) == 0 {
|
||||
name = c.Title(language.English).String(string(segment.Type))
|
||||
}
|
||||
|
||||
segment.name = name
|
||||
return name
|
||||
}
|
||||
|
||||
func (segment *Segment) SetEnabled(env platform.Environment) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
// display a message explaining omp failed(with the err)
|
||||
message := fmt.Sprintf("\noh-my-posh fatal error rendering %s segment:%s\n\n%s\n", segment.Type, err, debug.Stack())
|
||||
fmt.Println(message)
|
||||
segment.Enabled = true
|
||||
}()
|
||||
|
||||
// segment timings for debug purposes
|
||||
var start time.Time
|
||||
if env.Flags().Debug {
|
||||
start = time.Now()
|
||||
segment.nameLength = len(segment.Name())
|
||||
defer func() {
|
||||
segment.duration = time.Since(start)
|
||||
}()
|
||||
}
|
||||
|
||||
err := segment.mapSegmentWithWriter(env)
|
||||
if err != nil || !segment.shouldIncludeFolder() {
|
||||
return
|
||||
}
|
||||
|
||||
segment.env.DebugF("Segment: %s", segment.Name())
|
||||
|
||||
// validate toggles
|
||||
if toggles, OK := segment.env.Cache().Get(platform.TOGGLECACHE); OK && len(toggles) > 0 {
|
||||
list := strings.Split(toggles, ",")
|
||||
for _, toggle := range list {
|
||||
if SegmentType(toggle) == segment.Type || toggle == segment.Alias {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shouldHideForWidth(segment.env, segment.MinWidth, segment.MaxWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
if segment.writer.Enabled() {
|
||||
segment.Enabled = true
|
||||
env.TemplateCache().AddSegmentData(segment.Name(), segment.writer)
|
||||
}
|
||||
}
|
||||
|
||||
func (segment *Segment) SetText() {
|
||||
if !segment.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
segment.text = segment.string()
|
||||
segment.Enabled = len(strings.ReplaceAll(segment.text, " ", "")) > 0
|
||||
|
||||
if !segment.Enabled {
|
||||
segment.env.TemplateCache().RemoveSegmentData(segment.Name())
|
||||
}
|
||||
}
|
|
@ -1,328 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/regex"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
)
|
||||
|
||||
// BlockType type of block
|
||||
type BlockType string
|
||||
|
||||
// BlockAlignment aligment of a Block
|
||||
type BlockAlignment string
|
||||
|
||||
// Overflow defines how to handle a right block that overflows with the previous block
|
||||
type Overflow string
|
||||
|
||||
const (
|
||||
// Prompt writes one or more Segments
|
||||
Prompt BlockType = "prompt"
|
||||
// LineBreak creates a line break in the prompt
|
||||
LineBreak BlockType = "newline"
|
||||
// RPrompt is a right aligned prompt
|
||||
RPrompt BlockType = "rprompt"
|
||||
// Left aligns left
|
||||
Left BlockAlignment = "left"
|
||||
// Right aligns right
|
||||
Right BlockAlignment = "right"
|
||||
// Break adds a line break
|
||||
Break Overflow = "break"
|
||||
// Hide hides the block
|
||||
Hide Overflow = "hide"
|
||||
)
|
||||
|
||||
// Block defines a part of the prompt with optional segments
|
||||
type Block struct {
|
||||
Type BlockType `json:"type,omitempty" toml:"type,omitempty"`
|
||||
Alignment BlockAlignment `json:"alignment,omitempty" toml:"alignment,omitempty"`
|
||||
Segments []*Segment `json:"segments,omitempty" toml:"segments,omitempty"`
|
||||
Newline bool `json:"newline,omitempty" toml:"newline,omitempty"`
|
||||
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
|
||||
Overflow Overflow `json:"overflow,omitempty" toml:"overflow,omitempty"`
|
||||
|
||||
LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"`
|
||||
TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"`
|
||||
|
||||
// Deprecated: keep the logic for legacy purposes
|
||||
HorizontalOffset int `json:"horizontal_offset,omitempty" toml:"horizontal_offset,omitempty"`
|
||||
VerticalOffset int `json:"vertical_offset,omitempty" toml:"vertical_offset,omitempty"`
|
||||
|
||||
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
|
||||
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
|
||||
|
||||
env platform.Environment
|
||||
activeSegment *Segment
|
||||
previousActiveSegment *Segment
|
||||
}
|
||||
|
||||
func (b *Block) Init(env platform.Environment) {
|
||||
b.env = env
|
||||
b.executeSegmentLogic()
|
||||
}
|
||||
|
||||
func (b *Block) InitPlain(env platform.Environment, config *Config) {
|
||||
terminal.Init(shell.GENERIC)
|
||||
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, config.TerminalBackground)
|
||||
terminal.AnsiColors = config.MakeColors()
|
||||
|
||||
b.env = env
|
||||
b.executeSegmentLogic()
|
||||
}
|
||||
|
||||
func (b *Block) executeSegmentLogic() {
|
||||
if shouldHideForWidth(b.env, b.MinWidth, b.MaxWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
b.setEnabledSegments()
|
||||
b.setSegmentsText()
|
||||
}
|
||||
|
||||
func (b *Block) setActiveSegment(segment *Segment) {
|
||||
b.activeSegment = segment
|
||||
terminal.Interactive = segment.Interactive
|
||||
terminal.SetColors(segment.background(), segment.foreground())
|
||||
}
|
||||
|
||||
func (b *Block) Enabled() bool {
|
||||
if b.Type == LineBreak {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, segment := range b.Segments {
|
||||
if segment.Enabled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Block) setEnabledSegments() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.SetEnabled(b.env)
|
||||
}(segment)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) setSegmentsText() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.SetText()
|
||||
}(segment)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) RenderSegments() (string, int) {
|
||||
b.filterSegments()
|
||||
|
||||
for i, segment := range b.Segments {
|
||||
if colors, newCycle := cycle.Loop(); colors != nil {
|
||||
cycle = &newCycle
|
||||
segment.colors = colors
|
||||
}
|
||||
|
||||
if i == 0 && len(b.LeadingDiamond) > 0 {
|
||||
segment.LeadingDiamond = b.LeadingDiamond
|
||||
}
|
||||
|
||||
if i == len(b.Segments)-1 && len(b.TrailingDiamond) > 0 {
|
||||
segment.TrailingDiamond = b.TrailingDiamond
|
||||
}
|
||||
|
||||
b.setActiveSegment(segment)
|
||||
b.renderActiveSegment()
|
||||
}
|
||||
|
||||
b.writeSeparator(true)
|
||||
|
||||
return terminal.String()
|
||||
}
|
||||
|
||||
func (b *Block) filterSegments() {
|
||||
segments := make([]*Segment, 0)
|
||||
|
||||
for _, segment := range b.Segments {
|
||||
if !segment.Enabled && segment.style() != Accordion {
|
||||
continue
|
||||
}
|
||||
|
||||
segments = append(segments, segment)
|
||||
}
|
||||
|
||||
b.Segments = segments
|
||||
}
|
||||
|
||||
func (b *Block) renderActiveSegment() {
|
||||
b.writeSeparator(false)
|
||||
switch b.activeSegment.style() {
|
||||
case Plain, Powerline:
|
||||
terminal.Write(terminal.Background, terminal.Foreground, b.activeSegment.text)
|
||||
case Diamond:
|
||||
background := terminal.Transparent
|
||||
if b.previousActiveSegment != nil && b.previousActiveSegment.hasEmptyDiamondAtEnd() {
|
||||
background = b.previousActiveSegment.background()
|
||||
}
|
||||
terminal.Write(background, terminal.Background, b.activeSegment.LeadingDiamond)
|
||||
terminal.Write(terminal.Background, terminal.Foreground, b.activeSegment.text)
|
||||
case Accordion:
|
||||
if b.activeSegment.Enabled {
|
||||
terminal.Write(terminal.Background, terminal.Foreground, b.activeSegment.text)
|
||||
}
|
||||
}
|
||||
b.previousActiveSegment = b.activeSegment
|
||||
terminal.SetParentColors(b.previousActiveSegment.background(), b.previousActiveSegment.foreground())
|
||||
}
|
||||
|
||||
func (b *Block) writeSeparator(final bool) {
|
||||
isCurrentDiamond := b.activeSegment.style() == Diamond
|
||||
if final && isCurrentDiamond {
|
||||
terminal.Write(terminal.Transparent, terminal.Background, b.activeSegment.TrailingDiamond)
|
||||
return
|
||||
}
|
||||
|
||||
isPreviousDiamond := b.previousActiveSegment != nil && b.previousActiveSegment.style() == Diamond
|
||||
if isPreviousDiamond {
|
||||
b.adjustTrailingDiamondColorOverrides()
|
||||
}
|
||||
|
||||
if isPreviousDiamond && isCurrentDiamond && len(b.activeSegment.LeadingDiamond) == 0 {
|
||||
terminal.Write(terminal.Background, terminal.ParentBackground, b.previousActiveSegment.TrailingDiamond)
|
||||
return
|
||||
}
|
||||
|
||||
if isPreviousDiamond && len(b.previousActiveSegment.TrailingDiamond) > 0 {
|
||||
terminal.Write(terminal.Transparent, terminal.ParentBackground, b.previousActiveSegment.TrailingDiamond)
|
||||
}
|
||||
|
||||
isPowerline := b.activeSegment.isPowerline()
|
||||
|
||||
shouldOverridePowerlineLeadingSymbol := func() bool {
|
||||
if !isPowerline {
|
||||
return false
|
||||
}
|
||||
|
||||
if isPowerline && len(b.activeSegment.LeadingPowerlineSymbol) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if b.previousActiveSegment != nil && b.previousActiveSegment.isPowerline() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if shouldOverridePowerlineLeadingSymbol() {
|
||||
terminal.Write(terminal.Transparent, terminal.Background, b.activeSegment.LeadingPowerlineSymbol)
|
||||
return
|
||||
}
|
||||
|
||||
resolvePowerlineSymbol := func() string {
|
||||
if isPowerline {
|
||||
return b.activeSegment.PowerlineSymbol
|
||||
}
|
||||
|
||||
if b.previousActiveSegment != nil && b.previousActiveSegment.isPowerline() {
|
||||
return b.previousActiveSegment.PowerlineSymbol
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
symbol := resolvePowerlineSymbol()
|
||||
if len(symbol) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
bgColor := terminal.Background
|
||||
if final || !isPowerline {
|
||||
bgColor = terminal.Transparent
|
||||
}
|
||||
|
||||
if b.activeSegment.style() == Diamond && len(b.activeSegment.LeadingDiamond) == 0 {
|
||||
bgColor = terminal.Background
|
||||
}
|
||||
|
||||
if b.activeSegment.InvertPowerline {
|
||||
terminal.Write(b.getPowerlineColor(), bgColor, symbol)
|
||||
return
|
||||
}
|
||||
|
||||
terminal.Write(bgColor, b.getPowerlineColor(), symbol)
|
||||
}
|
||||
|
||||
func (b *Block) adjustTrailingDiamondColorOverrides() {
|
||||
// as we now already adjusted the activeSegment, we need to change the value
|
||||
// of background and foreground to parentBackground and parentForeground
|
||||
// this will still break when using parentBackground and parentForeground as keywords
|
||||
// in a trailing diamond, but let's fix that when it happens as it requires either a rewrite
|
||||
// of the logic for diamonds or storing grandparents as well like one happy family.
|
||||
if b.previousActiveSegment == nil || len(b.previousActiveSegment.TrailingDiamond) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(b.previousActiveSegment.TrailingDiamond, terminal.Background) && !strings.Contains(b.previousActiveSegment.TrailingDiamond, terminal.Foreground) {
|
||||
return
|
||||
}
|
||||
|
||||
match := regex.FindNamedRegexMatch(terminal.AnchorRegex, b.previousActiveSegment.TrailingDiamond)
|
||||
if len(match) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
adjustOverride := func(anchor, override string) {
|
||||
newOverride := override
|
||||
switch override {
|
||||
case terminal.Foreground:
|
||||
newOverride = terminal.ParentForeground
|
||||
case terminal.Background:
|
||||
newOverride = terminal.ParentBackground
|
||||
}
|
||||
|
||||
if override == newOverride {
|
||||
return
|
||||
}
|
||||
|
||||
newAnchor := strings.Replace(match[terminal.ANCHOR], override, newOverride, 1)
|
||||
b.previousActiveSegment.TrailingDiamond = strings.Replace(b.previousActiveSegment.TrailingDiamond, anchor, newAnchor, 1)
|
||||
}
|
||||
|
||||
if len(match[terminal.BG]) > 0 {
|
||||
adjustOverride(match[terminal.ANCHOR], match[terminal.BG])
|
||||
}
|
||||
if len(match[terminal.FG]) > 0 {
|
||||
adjustOverride(match[terminal.ANCHOR], match[terminal.FG])
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) getPowerlineColor() string {
|
||||
if b.previousActiveSegment == nil {
|
||||
return terminal.Transparent
|
||||
}
|
||||
if b.previousActiveSegment.style() == Diamond && len(b.previousActiveSegment.TrailingDiamond) == 0 {
|
||||
return b.previousActiveSegment.background()
|
||||
}
|
||||
if b.activeSegment.style() == Diamond && len(b.activeSegment.LeadingDiamond) == 0 {
|
||||
return b.previousActiveSegment.background()
|
||||
}
|
||||
if !b.previousActiveSegment.isPowerline() {
|
||||
return terminal.Transparent
|
||||
}
|
||||
return b.previousActiveSegment.background()
|
||||
}
|
|
@ -1,525 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/gookit/goutil/jsonutil"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
JSON string = "json"
|
||||
YAML string = "yaml"
|
||||
TOML string = "toml"
|
||||
|
||||
configVersion = 2
|
||||
)
|
||||
|
||||
// Config holds all the theme for rendering the prompt
|
||||
type Config struct {
|
||||
Version int `json:"version" toml:"version"`
|
||||
FinalSpace bool `json:"final_space,omitempty" toml:"final_space,omitempty"`
|
||||
ConsoleTitleTemplate string `json:"console_title_template,omitempty" toml:"console_title_template,omitempty"`
|
||||
TerminalBackground string `json:"terminal_background,omitempty" toml:"terminal_background,omitempty"`
|
||||
AccentColor string `json:"accent_color,omitempty" toml:"accent_color,omitempty"`
|
||||
Blocks []*Block `json:"blocks,omitempty" toml:"blocks,omitempty"`
|
||||
Tooltips []*Segment `json:"tooltips,omitempty" toml:"tooltips,omitempty"`
|
||||
TransientPrompt *Segment `json:"transient_prompt,omitempty" toml:"transient_prompt,omitempty"`
|
||||
ValidLine *Segment `json:"valid_line,omitempty" toml:"valid_line,omitempty"`
|
||||
ErrorLine *Segment `json:"error_line,omitempty" toml:"error_line,omitempty"`
|
||||
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty" toml:"secondary_prompt,omitempty"`
|
||||
DebugPrompt *Segment `json:"debug_prompt,omitempty" toml:"debug_prompt,omitempty"`
|
||||
Palette terminal.Palette `json:"palette,omitempty" toml:"palette,omitempty"`
|
||||
Palettes *terminal.Palettes `json:"palettes,omitempty" toml:"palettes,omitempty"`
|
||||
Cycle terminal.Cycle `json:"cycle,omitempty" toml:"cycle,omitempty"`
|
||||
ShellIntegration bool `json:"shell_integration,omitempty" toml:"shell_integration,omitempty"`
|
||||
PWD string `json:"pwd,omitempty" toml:"pwd,omitempty"`
|
||||
Var map[string]any `json:"var,omitempty" toml:"var,omitempty"`
|
||||
DisableCursorPositioning bool `json:"disable_cursor_positioning,omitempty" toml:"disable_cursor_positioning,omitempty"`
|
||||
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty" toml:"patch_pwsh_bleed,omitempty"`
|
||||
DisableNotice bool `json:"disable_notice,omitempty" toml:"disable_notice,omitempty"`
|
||||
AutoUpgrade bool `json:"auto_upgrade,omitempty" toml:"auto_upgrade,omitempty"`
|
||||
ITermFeatures terminal.ITermFeatures `json:"iterm_features,omitempty" toml:"iterm_features,omitempty"`
|
||||
|
||||
// Deprecated
|
||||
OSC99 bool `json:"osc99,omitempty" toml:"osc99,omitempty"`
|
||||
|
||||
Output string `json:"-" toml:"-"`
|
||||
MigrateGlyphs bool `json:"-" toml:"-"`
|
||||
Format string `json:"-" toml:"-"`
|
||||
|
||||
origin string
|
||||
// eval bool
|
||||
updated bool
|
||||
env platform.Environment
|
||||
}
|
||||
|
||||
// MakeColors creates instance of AnsiColors to use in AnsiWriter according to
|
||||
// environment and configuration.
|
||||
func (cfg *Config) MakeColors() terminal.ColorString {
|
||||
cacheDisabled := cfg.env.Getenv("OMP_CACHE_DISABLED") == "1"
|
||||
return terminal.MakeColors(cfg.getPalette(), !cacheDisabled, cfg.AccentColor, cfg.env)
|
||||
}
|
||||
|
||||
func (cfg *Config) getPalette() terminal.Palette {
|
||||
if cfg.Palettes == nil {
|
||||
return cfg.Palette
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: cfg.Palettes.Template,
|
||||
Env: cfg.env,
|
||||
}
|
||||
if palette, err := tmpl.Render(); err == nil {
|
||||
if p, ok := cfg.Palettes.List[palette]; ok {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return cfg.Palette
|
||||
}
|
||||
|
||||
// LoadConfig returns the default configuration including possible user overrides
|
||||
func LoadConfig(env platform.Environment) *Config {
|
||||
cfg := loadConfig(env)
|
||||
|
||||
// only migrate automatically when the switch isn't set
|
||||
if !env.Flags().Migrate && cfg.Version < configVersion {
|
||||
cfg.BackupAndMigrate()
|
||||
}
|
||||
|
||||
if !cfg.ShellIntegration {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// bash - ok
|
||||
// fish - ok
|
||||
// pwsh - ok
|
||||
// zsh - ok
|
||||
// cmd - ok, as of v1.4.25 (chrisant996/clink#457, fixed in chrisant996/clink@8a5d7ea)
|
||||
// nu - built-in (and bugged) feature - nushell/nushell#5585, https://www.nushell.sh/blog/2022-08-16-nushell-0_67.html#shell-integration-fdncred-and-tyriar
|
||||
// elv - broken OSC sequences
|
||||
// xonsh - broken OSC sequences
|
||||
// tcsh - overall broken, FTCS_COMMAND_EXECUTED could be added to POSH_POSTCMD in the future
|
||||
switch env.Shell() {
|
||||
case shell.ELVISH, shell.XONSH, shell.TCSH, shell.NU:
|
||||
cfg.ShellIntegration = false
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func loadConfig(env platform.Environment) *Config {
|
||||
defer env.Trace(time.Now())
|
||||
configFile := env.Flags().Config
|
||||
|
||||
if len(configFile) == 0 {
|
||||
env.Debug("no config file specified, using default")
|
||||
return defaultConfig(env, false)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
cfg.origin = configFile
|
||||
cfg.Format = strings.TrimPrefix(filepath.Ext(configFile), ".")
|
||||
cfg.env = env
|
||||
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
env.DebugF("error reading config file: %s", err)
|
||||
return defaultConfig(env, true)
|
||||
}
|
||||
|
||||
switch cfg.Format {
|
||||
case "yml", "yaml":
|
||||
cfg.Format = YAML
|
||||
err = yaml.Unmarshal(data, &cfg)
|
||||
case "jsonc", "json":
|
||||
cfg.Format = JSON
|
||||
|
||||
str := jsonutil.StripComments(string(data))
|
||||
data = []byte(str)
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
err = decoder.Decode(&cfg)
|
||||
case "toml", "tml":
|
||||
cfg.Format = TOML
|
||||
err = toml.Unmarshal(data, &cfg)
|
||||
default:
|
||||
err = fmt.Errorf("unsupported config file format: %s", cfg.Format)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
env.DebugF("error decoding config file: %s", err)
|
||||
return defaultConfig(env, true)
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
|
||||
func (cfg *Config) Export(format string) string {
|
||||
if len(format) != 0 {
|
||||
cfg.Format = format
|
||||
}
|
||||
|
||||
var result bytes.Buffer
|
||||
|
||||
switch cfg.Format {
|
||||
case YAML:
|
||||
prefix := "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
|
||||
yamlEncoder := yaml.NewEncoder(&result)
|
||||
|
||||
err := yamlEncoder.Encode(cfg)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return prefix + result.String()
|
||||
case JSON:
|
||||
jsonEncoder := json.NewEncoder(&result)
|
||||
jsonEncoder.SetEscapeHTML(false)
|
||||
jsonEncoder.SetIndent("", " ")
|
||||
_ = jsonEncoder.Encode(cfg)
|
||||
prefix := "{\n \"$schema\": \"https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\","
|
||||
data := strings.Replace(result.String(), "{", prefix, 1)
|
||||
return escapeGlyphs(data, cfg.MigrateGlyphs)
|
||||
case TOML:
|
||||
prefix := "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
|
||||
tomlEncoder := toml.NewEncoder(&result)
|
||||
tomlEncoder.SetIndentTables(true)
|
||||
|
||||
err := tomlEncoder.Encode(cfg)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return prefix + result.String()
|
||||
}
|
||||
|
||||
// unsupported format
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cfg *Config) BackupAndMigrate() {
|
||||
cfg.Backup()
|
||||
cfg.Migrate()
|
||||
cfg.Write(cfg.Format)
|
||||
}
|
||||
|
||||
func (cfg *Config) Write(format string) {
|
||||
content := cfg.Export(format)
|
||||
if len(content) == 0 {
|
||||
// we are unable to perform the export
|
||||
os.Exit(65)
|
||||
return
|
||||
}
|
||||
|
||||
destination := cfg.Output
|
||||
if len(destination) == 0 {
|
||||
destination = cfg.origin
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(destination, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
_, err = f.WriteString(content)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) Backup() {
|
||||
dst := cfg.origin + ".bak"
|
||||
source, err := os.Open(cfg.origin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer source.Close()
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer destination.Close()
|
||||
_, err = io.Copy(destination, source)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func escapeGlyphs(s string, migrate bool) string {
|
||||
shouldExclude := func(r rune) bool {
|
||||
if r < 0x1000 { // Basic Multilingual Plane
|
||||
return true
|
||||
}
|
||||
if r > 0x1F600 && r < 0x1F64F { // Emoticons
|
||||
return true
|
||||
}
|
||||
if r > 0x1F300 && r < 0x1F5FF { // Misc Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F680 && r < 0x1F6FF { // Transport and Map
|
||||
return true
|
||||
}
|
||||
if r > 0x2600 && r < 0x26FF { // Misc symbols
|
||||
return true
|
||||
}
|
||||
if r > 0x2700 && r < 0x27BF { // Dingbats
|
||||
return true
|
||||
}
|
||||
if r > 0xFE00 && r < 0xFE0F { // Variation Selectors
|
||||
return true
|
||||
}
|
||||
if r > 0x1F900 && r < 0x1F9FF { // Supplemental Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F1E6 && r < 0x1F1FF { // Flags
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var cp codePoints
|
||||
var err error
|
||||
if migrate {
|
||||
cp, err = getGlyphCodePoints()
|
||||
if err != nil {
|
||||
migrate = false
|
||||
}
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
for _, r := range s {
|
||||
// exclude regular characters and emojis
|
||||
if shouldExclude(r) {
|
||||
builder.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
if migrate {
|
||||
if val, OK := cp[uint64(r)]; OK {
|
||||
r = rune(val)
|
||||
}
|
||||
}
|
||||
|
||||
if r > 0x10000 {
|
||||
// calculate surrogate pairs
|
||||
one := 0xd800 + (((r - 0x10000) >> 10) & 0x3ff)
|
||||
two := 0xdc00 + ((r - 0x10000) & 0x3ff)
|
||||
quoted := fmt.Sprintf("\\u%04x\\u%04x", one, two)
|
||||
builder.WriteString(quoted)
|
||||
continue
|
||||
}
|
||||
|
||||
quoted := fmt.Sprintf("\\u%04x", r)
|
||||
builder.WriteString(quoted)
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func defaultConfig(env platform.Environment, warning bool) *Config {
|
||||
exitBackgroundTemplate := "{{ if gt .Code 0 }}p:red{{ end }}"
|
||||
exitTemplate := " {{ if gt .Code 0 }}\uf00d{{ else }}\uf00c{{ end }} "
|
||||
if warning {
|
||||
exitBackgroundTemplate = "p:red"
|
||||
exitTemplate = " CONFIG ERROR "
|
||||
}
|
||||
cfg := &Config{
|
||||
Version: 2,
|
||||
FinalSpace: true,
|
||||
Blocks: []*Block{
|
||||
{
|
||||
Type: Prompt,
|
||||
Alignment: Left,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: SESSION,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b6",
|
||||
TrailingDiamond: "\ue0b0",
|
||||
Background: "p:yellow",
|
||||
Foreground: "p:black",
|
||||
Template: " {{ if .SSHSession }}\ueba9 {{ end }}{{ .UserName }} ",
|
||||
},
|
||||
{
|
||||
Type: PATH,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:orange",
|
||||
Foreground: "p:white",
|
||||
Properties: properties.Map{
|
||||
properties.Style: "folder",
|
||||
},
|
||||
Template: " \uea83 {{ path .Path .Location }} ",
|
||||
},
|
||||
{
|
||||
Type: GIT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:green",
|
||||
BackgroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:yellow{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:red{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}#49416D{{ end }}",
|
||||
"{{ if gt .Behind 0 }}#7A306C{{ end }}",
|
||||
},
|
||||
Foreground: "p:black",
|
||||
ForegroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:black{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:white{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}p:white{{ end }}",
|
||||
},
|
||||
Properties: properties.Map{
|
||||
segments.BranchMaxLength: 25,
|
||||
segments.FetchStatus: true,
|
||||
segments.FetchUpstreamIcon: true,
|
||||
},
|
||||
Template: " {{ if .UpstreamURL }}{{ url .UpstreamIcon .UpstreamURL }} {{ end }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} \uf044 {{ .Working.String }}{{ end }}{{ if .Staging.Changed }} \uf046 {{ .Staging.String }}{{ end }} ", //nolint:lll
|
||||
},
|
||||
{
|
||||
Type: ROOT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:yellow",
|
||||
Foreground: "p:white",
|
||||
Template: " \uf0e7 ",
|
||||
},
|
||||
{
|
||||
Type: STATUS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "<transparent,background>\ue0b0</>",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:blue",
|
||||
BackgroundTemplates: []string{
|
||||
exitBackgroundTemplate,
|
||||
},
|
||||
Foreground: "p:white",
|
||||
Properties: properties.Map{
|
||||
properties.AlwaysEnabled: true,
|
||||
},
|
||||
Template: exitTemplate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: RPrompt,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: NODE,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:green",
|
||||
Template: "\ue718 ",
|
||||
Properties: properties.Map{
|
||||
segments.HomeEnabled: false,
|
||||
segments.FetchPackageManager: false,
|
||||
segments.DisplayMode: "files",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: GOLANG,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:blue",
|
||||
Template: "\ue626 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: PYTHON,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:yellow",
|
||||
Template: "\ue235 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
segments.DisplayMode: "files",
|
||||
segments.FetchVirtualEnv: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: SHELL,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:white",
|
||||
Template: "in <p:blue><b>{{ .Name }}</b></> ",
|
||||
},
|
||||
{
|
||||
Type: TIME,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:white",
|
||||
Template: "at <p:blue><b>{{ .CurrentDate | date \"15:04:05\" }}</b></>",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ConsoleTitleTemplate: "{{ .Shell }} in {{ .Folder }}",
|
||||
Palette: terminal.Palette{
|
||||
"black": "#262B44",
|
||||
"blue": "#4B95E9",
|
||||
"green": "#59C9A5",
|
||||
"orange": "#F07623",
|
||||
"red": "#D81E5B",
|
||||
"white": "#E0DEF4",
|
||||
"yellow": "#F3AE35",
|
||||
},
|
||||
SecondaryPrompt: &Segment{
|
||||
Background: "transparent",
|
||||
Foreground: "p:black",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> > </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
TransientPrompt: &Segment{
|
||||
Background: "transparent",
|
||||
Foreground: "p:black",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> {{ .Folder }} </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
Tooltips: []*Segment{
|
||||
{
|
||||
Type: AWS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:orange",
|
||||
Foreground: "p:white",
|
||||
Template: " \ue7ad {{ .Profile }}{{ if .Region }}@{{ .Region }}{{ end }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"aws"},
|
||||
},
|
||||
{
|
||||
Type: AZ,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:blue",
|
||||
Foreground: "p:white",
|
||||
Template: " \uebd8 {{ .Name }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"az"},
|
||||
},
|
||||
},
|
||||
}
|
||||
cfg.env = env
|
||||
return cfg
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||
)
|
||||
|
||||
|
@ -21,14 +22,14 @@ func (e *Engine) PrintDebug(startTime time.Time, version string) string {
|
|||
titleStartTime := time.Now()
|
||||
e.Env.Debug("Segment: Title")
|
||||
title := e.getTitleTemplateText()
|
||||
consoleTitle := &Segment{
|
||||
name: "ConsoleTitle",
|
||||
nameLength: 12,
|
||||
consoleTitle := &config.Segment{
|
||||
Alias: "ConsoleTitle",
|
||||
NameLength: 12,
|
||||
Enabled: len(e.Config.ConsoleTitleTemplate) > 0,
|
||||
text: title,
|
||||
duration: time.Since(titleStartTime),
|
||||
Text: title,
|
||||
Duration: time.Since(titleStartTime),
|
||||
}
|
||||
largestSegmentNameLength := consoleTitle.nameLength
|
||||
largestSegmentNameLength := consoleTitle.NameLength
|
||||
|
||||
// render prompt
|
||||
e.write(log.Text("\nPrompt:\n\n").Green().Bold().Plain().String())
|
||||
|
@ -36,14 +37,14 @@ func (e *Engine) PrintDebug(startTime time.Time, version string) string {
|
|||
|
||||
e.write(log.Text("\n\nSegments:\n\n").Green().Bold().Plain().String())
|
||||
|
||||
var segments []*Segment
|
||||
var segments []*config.Segment
|
||||
segments = append(segments, consoleTitle)
|
||||
|
||||
for _, block := range e.Config.Blocks {
|
||||
for _, segment := range block.Segments {
|
||||
segments = append(segments, segment)
|
||||
if segment.nameLength > largestSegmentNameLength {
|
||||
largestSegmentNameLength = segment.nameLength
|
||||
if segment.NameLength > largestSegmentNameLength {
|
||||
largestSegmentNameLength = segment.NameLength
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +52,7 @@ func (e *Engine) PrintDebug(startTime time.Time, version string) string {
|
|||
// 22 is the color for false/true and 7 is the reset color
|
||||
largestSegmentNameLength += 22 + 7
|
||||
for _, segment := range segments {
|
||||
duration := segment.duration.Milliseconds()
|
||||
duration := segment.Duration.Milliseconds()
|
||||
var active log.Text
|
||||
if segment.Enabled {
|
||||
active = log.Text("true").Yellow()
|
||||
|
@ -65,11 +66,12 @@ func (e *Engine) PrintDebug(startTime time.Time, version string) string {
|
|||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Run duration:").Green().Bold().Plain(), time.Since(startTime)))
|
||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Cache path:").Green().Bold().Plain(), e.Env.CachePath()))
|
||||
|
||||
config := e.Env.Flags().Config
|
||||
if len(config) == 0 {
|
||||
config = "no --config set, using default built-in configuration"
|
||||
cfg := e.Env.Flags().Config
|
||||
if len(cfg) == 0 {
|
||||
cfg = "no --config set, using default built-in configuration"
|
||||
}
|
||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Config path:").Green().Bold().Plain(), config))
|
||||
|
||||
e.write(fmt.Sprintf("\n%s %s\n", log.Text("Config path:").Green().Bold().Plain(), cfg))
|
||||
|
||||
e.write(log.Text("\nLogs:\n\n").Green().Bold().Plain().String())
|
||||
e.write(e.Env.Logs())
|
||||
|
|
|
@ -3,7 +3,9 @@ package engine
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/regex"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
@ -14,7 +16,7 @@ var (
|
|||
)
|
||||
|
||||
type Engine struct {
|
||||
Config *Config
|
||||
Config *config.Config
|
||||
Env platform.Environment
|
||||
Plain bool
|
||||
|
||||
|
@ -22,6 +24,9 @@ type Engine struct {
|
|||
currentLineLength int
|
||||
rprompt string
|
||||
rpromptLength int
|
||||
|
||||
activeSegment *config.Segment
|
||||
previousActiveSegment *config.Segment
|
||||
}
|
||||
|
||||
func (e *Engine) write(text string) {
|
||||
|
@ -175,13 +180,13 @@ func (e *Engine) getTitleTemplateText() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
|
||||
func (e *Engine) renderBlock(block *config.Block, cancelNewline bool) bool {
|
||||
defer e.patchPowerShellBleed()
|
||||
|
||||
// This is deprecated but we leave it in to not break configs
|
||||
// It is encouraged to used "newline": true on block level
|
||||
// rather than the standalone the linebreak block
|
||||
if block.Type == LineBreak {
|
||||
if block.Type == config.LineBreak {
|
||||
// do not print a newline to avoid a leading space
|
||||
// when we're printin the first primary prompt in
|
||||
// the shell
|
||||
|
@ -193,7 +198,7 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
|
|||
|
||||
// when in bash, for rprompt blocks we need to write plain
|
||||
// and wrap in escaped mode or the prompt will not render correctly
|
||||
if e.Env.Shell() == shell.BASH && block.Type == RPrompt {
|
||||
if e.Env.Shell() == shell.BASH && block.Type == config.RPrompt {
|
||||
block.InitPlain(e.Env, e.Config)
|
||||
} else {
|
||||
block.Init(e.Env)
|
||||
|
@ -210,7 +215,7 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
|
|||
e.newline()
|
||||
}
|
||||
|
||||
text, length := block.RenderSegments()
|
||||
text, length := e.renderBlockSegments(block)
|
||||
|
||||
// do not print anything when we don't have any text
|
||||
if length == 0 {
|
||||
|
@ -218,18 +223,18 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
|
|||
}
|
||||
|
||||
switch block.Type { //nolint:exhaustive
|
||||
case Prompt:
|
||||
case config.Prompt:
|
||||
if block.VerticalOffset != 0 {
|
||||
e.write(terminal.ChangeLine(block.VerticalOffset))
|
||||
}
|
||||
|
||||
if block.Alignment == Left {
|
||||
if block.Alignment == config.Left {
|
||||
e.currentLineLength += length
|
||||
e.write(text)
|
||||
return true
|
||||
}
|
||||
|
||||
if block.Alignment != Right {
|
||||
if block.Alignment != config.Right {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -237,9 +242,9 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
|
|||
// we can't print the right block as there's not enough room available
|
||||
if !OK {
|
||||
switch block.Overflow {
|
||||
case Break:
|
||||
case config.Break:
|
||||
e.newline()
|
||||
case Hide:
|
||||
case config.Hide:
|
||||
// make sure to fill if needed
|
||||
if padText, OK := e.shouldFill(block.Filler, space, 0); OK {
|
||||
e.write(padText)
|
||||
|
@ -269,7 +274,7 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
|
|||
|
||||
prompt += text
|
||||
e.write(prompt)
|
||||
case RPrompt:
|
||||
case config.RPrompt:
|
||||
e.rprompt = text
|
||||
e.rpromptLength = length
|
||||
}
|
||||
|
@ -281,7 +286,7 @@ func (e *Engine) patchPowerShellBleed() {
|
|||
// when in PowerShell, we need to clear the line after the prompt
|
||||
// to avoid the background being printed on the next line
|
||||
// when at the end of the buffer.
|
||||
// See https://github.com/JanDeDobbeleer/oh-my-posh/issues/65
|
||||
// See https://githue.com/JanDeDobbeleer/oh-my-posh/issues/65
|
||||
if e.Env.Shell() != shell.PWSH && e.Env.Shell() != shell.PWSH5 {
|
||||
return
|
||||
}
|
||||
|
@ -293,3 +298,216 @@ func (e *Engine) patchPowerShellBleed() {
|
|||
|
||||
e.write(terminal.ClearAfter())
|
||||
}
|
||||
|
||||
func (e *Engine) renderBlockSegments(block *config.Block) (string, int) {
|
||||
e.filterSegments(block)
|
||||
|
||||
for i, segment := range block.Segments {
|
||||
if colors, newCycle := cycle.Loop(); colors != nil {
|
||||
cycle = &newCycle
|
||||
segment.Colors = colors
|
||||
}
|
||||
|
||||
if i == 0 && len(block.LeadingDiamond) > 0 {
|
||||
segment.LeadingDiamond = block.LeadingDiamond
|
||||
}
|
||||
|
||||
if i == len(block.Segments)-1 && len(block.TrailingDiamond) > 0 {
|
||||
segment.TrailingDiamond = block.TrailingDiamond
|
||||
}
|
||||
|
||||
e.setActiveSegment(segment)
|
||||
e.renderActiveSegment()
|
||||
}
|
||||
|
||||
e.writeSeparator(true)
|
||||
|
||||
return terminal.String()
|
||||
}
|
||||
|
||||
func (e *Engine) filterSegments(block *config.Block) {
|
||||
segments := make([]*config.Segment, 0)
|
||||
|
||||
for _, segment := range block.Segments {
|
||||
if !segment.Enabled && segment.ResolveStyle() != config.Accordion {
|
||||
continue
|
||||
}
|
||||
|
||||
segments = append(segments, segment)
|
||||
}
|
||||
|
||||
block.Segments = segments
|
||||
}
|
||||
|
||||
func (e *Engine) setActiveSegment(segment *config.Segment) {
|
||||
e.activeSegment = segment
|
||||
terminal.Interactive = segment.Interactive
|
||||
terminal.SetColors(segment.ResolveBackground(), segment.ResolveForeground())
|
||||
}
|
||||
|
||||
func (e *Engine) renderActiveSegment() {
|
||||
e.writeSeparator(false)
|
||||
switch e.activeSegment.ResolveStyle() {
|
||||
case config.Plain, config.Powerline:
|
||||
terminal.Write(terminal.Background, terminal.Foreground, e.activeSegment.Text)
|
||||
case config.Diamond:
|
||||
background := terminal.Transparent
|
||||
|
||||
if e.previousActiveSegment != nil && e.previousActiveSegment.HasEmptyDiamondAtEnd() {
|
||||
background = e.previousActiveSegment.ResolveBackground()
|
||||
}
|
||||
|
||||
terminal.Write(background, terminal.Background, e.activeSegment.LeadingDiamond)
|
||||
terminal.Write(terminal.Background, terminal.Foreground, e.activeSegment.Text)
|
||||
case config.Accordion:
|
||||
if e.activeSegment.Enabled {
|
||||
terminal.Write(terminal.Background, terminal.Foreground, e.activeSegment.Text)
|
||||
}
|
||||
}
|
||||
e.previousActiveSegment = e.activeSegment
|
||||
|
||||
terminal.SetParentColors(e.previousActiveSegment.ResolveBackground(), e.previousActiveSegment.ResolveForeground())
|
||||
}
|
||||
|
||||
func (e *Engine) writeSeparator(final bool) {
|
||||
isCurrentDiamond := e.activeSegment.ResolveStyle() == config.Diamond
|
||||
if final && isCurrentDiamond {
|
||||
terminal.Write(terminal.Transparent, terminal.Background, e.activeSegment.TrailingDiamond)
|
||||
return
|
||||
}
|
||||
|
||||
isPreviousDiamond := e.previousActiveSegment != nil && e.previousActiveSegment.ResolveStyle() == config.Diamond
|
||||
if isPreviousDiamond {
|
||||
e.adjustTrailingDiamondColorOverrides()
|
||||
}
|
||||
|
||||
if isPreviousDiamond && isCurrentDiamond && len(e.activeSegment.LeadingDiamond) == 0 {
|
||||
terminal.Write(terminal.Background, terminal.ParentBackground, e.previousActiveSegment.TrailingDiamond)
|
||||
return
|
||||
}
|
||||
|
||||
if isPreviousDiamond && len(e.previousActiveSegment.TrailingDiamond) > 0 {
|
||||
terminal.Write(terminal.Transparent, terminal.ParentBackground, e.previousActiveSegment.TrailingDiamond)
|
||||
}
|
||||
|
||||
isPowerline := e.activeSegment.IsPowerline()
|
||||
|
||||
shouldOverridePowerlineLeadingSymbol := func() bool {
|
||||
if !isPowerline {
|
||||
return false
|
||||
}
|
||||
|
||||
if isPowerline && len(e.activeSegment.LeadingPowerlineSymbol) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if e.previousActiveSegment != nil && e.previousActiveSegment.IsPowerline() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if shouldOverridePowerlineLeadingSymbol() {
|
||||
terminal.Write(terminal.Transparent, terminal.Background, e.activeSegment.LeadingPowerlineSymbol)
|
||||
return
|
||||
}
|
||||
|
||||
resolvePowerlineSymbol := func() string {
|
||||
if isPowerline {
|
||||
return e.activeSegment.PowerlineSymbol
|
||||
}
|
||||
|
||||
if e.previousActiveSegment != nil && e.previousActiveSegment.IsPowerline() {
|
||||
return e.previousActiveSegment.PowerlineSymbol
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
symbol := resolvePowerlineSymbol()
|
||||
if len(symbol) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
bgColor := terminal.Background
|
||||
if final || !isPowerline {
|
||||
bgColor = terminal.Transparent
|
||||
}
|
||||
|
||||
if e.activeSegment.ResolveStyle() == config.Diamond && len(e.activeSegment.LeadingDiamond) == 0 {
|
||||
bgColor = terminal.Background
|
||||
}
|
||||
|
||||
if e.activeSegment.InvertPowerline {
|
||||
terminal.Write(e.getPowerlineColor(), bgColor, symbol)
|
||||
return
|
||||
}
|
||||
|
||||
terminal.Write(bgColor, e.getPowerlineColor(), symbol)
|
||||
}
|
||||
|
||||
func (e *Engine) getPowerlineColor() string {
|
||||
if e.previousActiveSegment == nil {
|
||||
return terminal.Transparent
|
||||
}
|
||||
|
||||
if e.previousActiveSegment.ResolveStyle() == config.Diamond && len(e.previousActiveSegment.TrailingDiamond) == 0 {
|
||||
return e.previousActiveSegment.ResolveBackground()
|
||||
}
|
||||
|
||||
if e.activeSegment.ResolveStyle() == config.Diamond && len(e.activeSegment.LeadingDiamond) == 0 {
|
||||
return e.previousActiveSegment.ResolveBackground()
|
||||
}
|
||||
|
||||
if !e.previousActiveSegment.IsPowerline() {
|
||||
return terminal.Transparent
|
||||
}
|
||||
|
||||
return e.previousActiveSegment.ResolveBackground()
|
||||
}
|
||||
|
||||
func (e *Engine) adjustTrailingDiamondColorOverrides() {
|
||||
// as we now already adjusted the activeSegment, we need to change the value
|
||||
// of background and foreground to parentBackground and parentForeground
|
||||
// this will still break when using parentBackground and parentForeground as keywords
|
||||
// in a trailing diamond, but let's fix that when it happens as it requires either a rewrite
|
||||
// of the logic for diamonds or storing grandparents as well like one happy family.
|
||||
if e.previousActiveSegment == nil || len(e.previousActiveSegment.TrailingDiamond) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(e.previousActiveSegment.TrailingDiamond, terminal.Background) && !strings.Contains(e.previousActiveSegment.TrailingDiamond, terminal.Foreground) {
|
||||
return
|
||||
}
|
||||
|
||||
match := regex.FindNamedRegexMatch(terminal.AnchorRegex, e.previousActiveSegment.TrailingDiamond)
|
||||
if len(match) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
adjustOverride := func(anchor, override string) {
|
||||
newOverride := override
|
||||
switch override {
|
||||
case terminal.Foreground:
|
||||
newOverride = terminal.ParentForeground
|
||||
case terminal.Background:
|
||||
newOverride = terminal.ParentBackground
|
||||
}
|
||||
|
||||
if override == newOverride {
|
||||
return
|
||||
}
|
||||
|
||||
newAnchor := strings.Replace(match[terminal.ANCHOR], override, newOverride, 1)
|
||||
e.previousActiveSegment.TrailingDiamond = strings.Replace(e.previousActiveSegment.TrailingDiamond, anchor, newAnchor, 1)
|
||||
}
|
||||
|
||||
if len(match[terminal.BG]) > 0 {
|
||||
adjustOverride(match[terminal.ANCHOR], match[terminal.BG])
|
||||
}
|
||||
|
||||
if len(match[terminal.FG]) > 0 {
|
||||
adjustOverride(match[terminal.ANCHOR], match[terminal.FG])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
|
@ -89,7 +90,7 @@ func TestPrintPWD(t *testing.T) {
|
|||
|
||||
engine := &Engine{
|
||||
Env: env,
|
||||
Config: &Config{
|
||||
Config: &config.Config{
|
||||
PWD: tc.Config,
|
||||
OSC99: tc.OSC99,
|
||||
},
|
||||
|
@ -113,7 +114,7 @@ func engineRender() {
|
|||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
cfg := LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
terminal.Init(shell.GENERIC)
|
||||
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground)
|
||||
|
@ -189,7 +190,7 @@ func TestGetTitle(t *testing.T) {
|
|||
terminal.Init(shell.GENERIC)
|
||||
|
||||
engine := &Engine{
|
||||
Config: &Config{
|
||||
Config: &config.Config{
|
||||
ConsoleTitleTemplate: tc.Template,
|
||||
},
|
||||
Env: env,
|
||||
|
@ -250,7 +251,7 @@ func TestGetConsoleTitleIfGethostnameReturnsError(t *testing.T) {
|
|||
terminal.Init(shell.GENERIC)
|
||||
|
||||
engine := &Engine{
|
||||
Config: &Config{
|
||||
Config: &config.Config{
|
||||
ConsoleTitleTemplate: tc.Template,
|
||||
},
|
||||
Env: env,
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform/net"
|
||||
)
|
||||
|
||||
type codePoints map[uint64]uint64
|
||||
|
||||
func getGlyphCodePoints() (codePoints, error) {
|
||||
var codePoints = make(codePoints)
|
||||
|
||||
ctx, cncl := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
|
||||
defer cncl()
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://ohmyposh.dev/codepoints.csv", nil)
|
||||
if err != nil {
|
||||
return codePoints, &ConnectionError{reason: err.Error()}
|
||||
}
|
||||
|
||||
response, err := net.HTTPClient.Do(request)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
bytes, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(bytes), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ",")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
oldGlyph, err := strconv.ParseUint(fields[0], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newGlyph, err := strconv.ParseUint(fields[1], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
codePoints[oldGlyph] = newGlyph
|
||||
}
|
||||
|
||||
return codePoints, nil
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetCodePoints(t *testing.T) {
|
||||
codepoints, err := getGlyphCodePoints()
|
||||
if connectionError, ok := err.(*ConnectionError); ok {
|
||||
t.Log(connectionError.Error())
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 1939, len(codepoints))
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
@ -15,7 +16,7 @@ func New(flags *platform.Flags) *Engine {
|
|||
}
|
||||
|
||||
env.Init()
|
||||
cfg := LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
if cfg.PatchPwshBleed {
|
||||
patchPowerShellBleed(env.Shell(), flags)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
@ -49,7 +50,7 @@ func (e *Engine) Primary() string {
|
|||
renderRPrompt = false
|
||||
}
|
||||
|
||||
if block.Type == RPrompt && !renderRPrompt {
|
||||
if block.Type == config.RPrompt && !renderRPrompt {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -121,7 +122,7 @@ func (e *Engine) Primary() string {
|
|||
func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
|
||||
// populate env with latest context
|
||||
e.Env.LoadTemplateCache()
|
||||
var prompt *Segment
|
||||
var prompt *config.Segment
|
||||
switch promptType {
|
||||
case Debug:
|
||||
prompt = e.Config.DebugPrompt
|
||||
|
@ -136,7 +137,7 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
|
|||
}
|
||||
|
||||
if prompt == nil {
|
||||
prompt = &Segment{}
|
||||
prompt = &config.Segment{}
|
||||
}
|
||||
|
||||
getTemplate := func(template string) string {
|
||||
|
@ -214,9 +215,9 @@ func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
|
|||
}
|
||||
|
||||
func (e *Engine) RPrompt() string {
|
||||
filterRPromptBlock := func(blocks []*Block) *Block {
|
||||
filterRPromptBlock := func(blocks []*config.Block) *config.Block {
|
||||
for _, block := range blocks {
|
||||
if block.Type == RPrompt {
|
||||
if block.Type == config.RPrompt {
|
||||
return block
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +234,7 @@ func (e *Engine) RPrompt() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
text, length := block.RenderSegments()
|
||||
text, length := e.renderBlockSegments(block)
|
||||
e.rpromptLength = length
|
||||
return text
|
||||
}
|
||||
|
|
|
@ -3,24 +3,23 @@ package engine
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
)
|
||||
|
||||
func (e *Engine) Tooltip(tip string) string {
|
||||
tip = strings.Trim(tip, " ")
|
||||
tooltips := make([]*Segment, 0, 1)
|
||||
tooltips := make([]*config.Segment, 0, 1)
|
||||
|
||||
for _, tooltip := range e.Config.Tooltips {
|
||||
if !tooltip.shouldInvokeWithTip(tip) {
|
||||
if !e.shouldInvokeWithTip(tooltip, tip) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := tooltip.mapSegmentWithWriter(e.Env); err != nil {
|
||||
continue
|
||||
}
|
||||
tooltip.SetEnabled(e.Env)
|
||||
|
||||
if !tooltip.writer.Enabled() {
|
||||
if !tooltip.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -32,8 +31,8 @@ func (e *Engine) Tooltip(tip string) string {
|
|||
}
|
||||
|
||||
// little hack to reuse the current logic
|
||||
block := &Block{
|
||||
Alignment: Right,
|
||||
block := &config.Block{
|
||||
Alignment: config.Right,
|
||||
Segments: tooltips,
|
||||
}
|
||||
|
||||
|
@ -43,7 +42,7 @@ func (e *Engine) Tooltip(tip string) string {
|
|||
if !block.Enabled() {
|
||||
return ""
|
||||
}
|
||||
text, _ := block.RenderSegments()
|
||||
text, _ := e.renderBlockSegments(block)
|
||||
return text
|
||||
case shell.PWSH, shell.PWSH5:
|
||||
block.InitPlain(e.Env, e.Config)
|
||||
|
@ -56,7 +55,7 @@ func (e *Engine) Tooltip(tip string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
text, length := block.RenderSegments()
|
||||
text, length := e.renderBlockSegments(block)
|
||||
|
||||
space := consoleWidth - e.Env.Flags().Column - length
|
||||
if space <= 0 {
|
||||
|
@ -72,3 +71,13 @@ func (e *Engine) Tooltip(tip string) string {
|
|||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *Engine) shouldInvokeWithTip(segment *config.Segment, tip string) bool {
|
||||
for _, t := range segment.Tips {
|
||||
if t == tip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue