refactor: strip restating comments across the codebase

Removed doc/inline comments that only paraphrased the name or next
line of code, across Go source, shell init scripts (bash, zsh, fish,
pwsh, nu, elvish, xonsh, yash, Clink lua), and website JS. Comments
encoding non-obvious workarounds, race conditions, or external bug
references were kept and trimmed to just that point.

Moves the minimal-comment rule from the golang skill into AGENTS.md
so it applies repo-wide instead of to Go only.

Entire-Checkpoint: 7a66b31a1ad7
This commit is contained in:
Jan De Dobbeleer
2026-07-30 19:44:41 +02:00
committed by Jan De Dobbeleer
parent e4a1c37524
commit 928140efa3
160 changed files with 465 additions and 1273 deletions
+16
View File
@@ -110,6 +110,22 @@ cache logic. It supports TTL-based key/value storage, file-based persistence, an
caching. Do not introduce new cache packages unless `src/cache/` genuinely cannot meet the
requirement.
## Comments
Applies to every language in this repository (Go, shell scripts, PowerShell, JavaScript/TypeScript,
Lua, etc.) - not just the primary language of whatever file you're touching.
- Default to no comment. Add one only when the code cannot say it on its own.
- Never restate what a function/type/variable already makes obvious from its name, signature,
and body. A comment that just paraphrases the name is noise - delete it.
- Only comment the WHY: a hidden constraint, a non-obvious invariant, a workaround for a specific
bug, an external requirement, or a caveat that would surprise a reader. If there's nothing like
that to say, leave the declaration uncommented - even exported/public ones.
- When a comment is warranted, keep it to the minimum needed to convey that non-obvious point.
Don't pad it with restating context the code already shows.
- Language-specific skills (e.g. `golang`) may add formatting conventions (complete sentences,
doc-comment placement) on top of this rule as a stricter minimum, but must not relax it.
## Go Conventions
Follow the `golang` skill for project-specific Go standards.
+3 -4
View File
@@ -6,10 +6,9 @@ import (
"time"
)
// ErrLocked is returned by openFile when the cache file is held exclusively
// by another process (e.g. a Windows sharing violation that persisted past
// the retry window). Callers must treat this as "leave the file alone":
// operate purely in-memory for this run and do not recreate/truncate the
// Returned when the cache file is held exclusively by another process (e.g. a
// Windows sharing violation that persisted past the retry window). Callers
// must operate purely in-memory for this run and not recreate/truncate the
// file on close.
var ErrLocked = errors.New("cache file is locked by another process")
-6
View File
@@ -10,12 +10,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/log"
)
// Clear removes cache files from the cache directory.
//
// If force is true, the entire cache directory is removed.
// If force is false, only cache files older than 7 days that match certain patterns are deleted.
// The excludedFiles parameter allows you to specify file names that should not be deleted,
// even if they would otherwise be eligible for removal.
func Clear(force bool, excludedFiles ...string) error {
defer log.Trace(time.Now())
+26 -33
View File
@@ -7,18 +7,14 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/maps"
)
// commandPathKeyPrefix namespaces persisted command lookups in the session
// store so they can't collide with unrelated cache keys.
const commandPathKeyPrefix = "command_path_"
// pathEnvHash returns an FNV-1a hash of the environment that determines how
// exec.LookPath resolves a command: PATH, plus PATHEXT on Windows (unset and
// therefore a no-op elsewhere). Computed on every call, NOT memoized: the
// serve daemon lives across prompts and applies each request's env overlay
// (PATH included) in-process, so a per-process hash would keep matching
// entries persisted under an earlier PATH and pin e.g. `python` to a
// previous virtual environment's interpreter for the daemon's lifetime -
// the old binary still exists, so the os.Stat revalidation never catches it.
// Hash of PATH (+ PATHEXT on Windows). Computed on every call, NOT memoized:
// the serve daemon lives across prompts and applies each request's env
// overlay (PATH included) in-process, so a per-process hash would keep
// matching entries persisted under an earlier PATH and pin e.g. `python` to a
// previous virtual environment's interpreter for the daemon's lifetime - the
// old binary still exists, so the os.Stat revalidation never catches it.
func pathEnvHash() uint64 {
h := fnv.New64a()
_, _ = h.Write([]byte(os.Getenv("PATH")))
@@ -35,15 +31,14 @@ const (
CommandPathNegativeTTL = Duration("5m")
)
// commandPathEntry is the value persisted in the session store for a single
// command lookup. Found distinguishes a cached "does not exist" result
// (negative cache) from a resolved path. PathHash records the PATH(+PATHEXT)
// environment the lookup was resolved under; entries from a different
// environment are treated as misses so a PATH change mid-session (nvm use,
// venv activate, installing a tool earlier in PATH, ...) re-resolves
// immediately instead of serving a stale result for the TTL duration.
// Storing the hash in the entry (not the key) means a PATH change overwrites
// the entry in place rather than accumulating dead keys in the session store.
// Found distinguishes a cached "does not exist" result (negative cache) from
// a resolved path. PathHash records the PATH(+PATHEXT) environment the
// lookup was resolved under; entries from a different environment are
// treated as misses so a PATH change mid-session (nvm use, venv activate,
// installing a tool earlier in PATH, ...) re-resolves immediately instead of
// serving a stale result for the TTL duration. Storing the hash in the entry
// (not the key) means a PATH change overwrites the entry in place rather
// than accumulating dead keys in the session store.
type commandPathEntry struct {
Path string
PathHash uint64
@@ -60,8 +55,8 @@ func commandPathKey(command string) string {
return commandPathKeyPrefix + command
}
// Set stores a resolved command path in the in-memory (L1) cache only. This
// keeps existing in-process callers/semantics identical.
// Stores to the in-memory (L1) cache only, keeping existing in-process
// callers/semantics identical.
func (c *Command) Set(command, path string) {
c.Commands.Set(command, path)
}
@@ -76,13 +71,13 @@ func (c *Command) Get(command string) (string, bool) {
return cacheCommand, true
}
// Persist stores a positive (path resolved) or negative (command not found)
// command lookup in the session (L2) store so subsequent prompt processes in
// the same session skip re-running exec.LookPath. Positive entries use a
// modest TTL and are revalidated with a cheap os.Stat on read; negative
// entries use a much shorter TTL so a command installed mid-session is
// picked up quickly. Entries are tagged with the current PATH(+PATHEXT)
// hash so they're only served back under the same lookup environment.
// Stores a positive (path resolved) or negative (command not found) lookup
// in the session (L2) store so subsequent prompt processes in the same
// session skip re-running exec.LookPath. Positive entries use a modest TTL
// and are revalidated with a cheap os.Stat on read; negative entries use a
// much shorter TTL so a command installed mid-session is picked up quickly.
// Entries are tagged with the current PATH(+PATHEXT) hash so they're only
// served back under the same lookup environment.
func PersistCommandPath(command, path string, found bool) {
entry := commandPathEntry{Path: path, PathHash: pathEnvHash(), Found: found}
@@ -94,11 +89,9 @@ func PersistCommandPath(command, path string, found bool) {
Set(Session, commandPathKey(command), entry, ttl)
}
// GetPersistedCommandPath returns a previously persisted command lookup
// result from the session store, if any. An entry persisted under a
// different PATH(+PATHEXT) environment is treated as a miss so the caller
// falls through to a fresh exec.LookPath (which overwrites the entry with
// the current environment's hash).
// An entry persisted under a different PATH(+PATHEXT) environment is treated
// as a miss, so the caller falls through to a fresh exec.LookPath (which
// overwrites the entry with the current environment's hash).
func GetPersistedCommandPath(command string) (path string, found, ok bool) {
entry, exists := Get[commandPathEntry](Session, commandPathKey(command))
if !exists {
+9 -17
View File
@@ -10,13 +10,11 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/log"
)
// Configuration constants
const (
minStringSize = 50 * 1024 // 50KB minimum string size
maxStringSize = 10 * 1024 * 1024 // 10MB maximum string size
)
// Windows API constants
const (
fileMapAllAccess = 0x001f001f
pageReadwrite = 0x04
@@ -37,7 +35,6 @@ const (
sharingViolationSleep = 5 * time.Millisecond
)
// Windows API functions
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
createFileW = kernel32.NewProc("CreateFileW")
@@ -50,7 +47,6 @@ var (
getFileSizeEx = kernel32.NewProc("GetFileSizeEx")
)
// PersistentSharedString represents a memory-mapped file for storing a single string
type PersistentSharedString struct {
filePath string
fileHandle uintptr
@@ -88,13 +84,12 @@ func createOrOpenPersistentStringWithSize(filePath string, requiredSize int) (*P
return createNewFileWithSize(filePath, requiredSize)
}
// openExistingFileWithSize attempts to open an existing memory-mapped file.
// The file is opened with FILE_SHARE_READ|FILE_SHARE_WRITE so concurrent
// oh-my-posh processes (split panes, tooltip renders, etc.) don't lock each
// other out. If the file is momentarily locked (ERROR_SHARING_VIOLATION) we
// retry briefly; if it's still locked after that we return ErrLocked so the
// caller can fall back to an in-memory-only store instead of recreating the
// file (which would truncate the other process's data).
// Opened with FILE_SHARE_READ|FILE_SHARE_WRITE so concurrent oh-my-posh
// processes (split panes, tooltip renders, etc.) don't lock each other out.
// If the file is momentarily locked (ERROR_SHARING_VIOLATION) it retries
// briefly; if still locked after that, returns ErrLocked so the caller can
// fall back to an in-memory-only store instead of recreating the file (which
// would truncate the other process's data).
func openExistingFileWithSize(filePath string, requiredSize int) (*PersistentSharedString, error) {
filePathPtr, err := syscall.UTF16PtrFromString(filePath)
if err != nil {
@@ -160,9 +155,8 @@ func openExistingFileWithSize(filePath string, requiredSize int) (*PersistentSha
return createMappingFromFileWithSize(filePath, fileHandle, actualSize)
}
// createNewFileWithSize creates a new memory-mapped file with the specified size.
// The file is created with FILE_SHARE_READ|FILE_SHARE_WRITE so subsequent
// concurrent opens by other processes don't fail with a sharing violation.
// Created with FILE_SHARE_READ|FILE_SHARE_WRITE so subsequent concurrent
// opens by other processes don't fail with a sharing violation.
func createNewFileWithSize(filePath string, size int) (*PersistentSharedString, error) {
filePathPtr, err := syscall.UTF16PtrFromString(filePath)
if err != nil {
@@ -208,7 +202,6 @@ func createNewFileWithSize(filePath string, size int) (*PersistentSharedString,
return pss, nil
}
// createMappingFromFileWithSize creates a memory mapping from an open file handle with specified size
func createMappingFromFileWithSize(filePath string, fileHandle uintptr, size int) (*PersistentSharedString, error) {
totalSize := size + 5 // 4 bytes length + size + 1 null terminator
@@ -249,7 +242,7 @@ func createMappingFromFileWithSize(filePath string, fileHandle uintptr, size int
}, nil
}
// SetString stores a string in the memory-mapped file (automatically persisted)
// Automatically persisted; no explicit flush required.
func (pss *PersistentSharedString) SetString(value string) error {
strBytes := []byte(value)
@@ -305,7 +298,6 @@ func (pss *PersistentSharedString) bytes() []byte {
return result
}
// Close closes the memory-mapped file and handles
func (pss *PersistentSharedString) close() error {
var err error
+3 -5
View File
@@ -9,7 +9,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/log"
)
// persistentStringRWCloser implements io.ReadWriteCloser for PersistentSharedString
type persistentStringRWCloser struct {
pss *PersistentSharedString
buf *bytes.Buffer
@@ -93,10 +92,9 @@ func openFile(filePath string) (io.ReadWriteCloser, error) {
return NewPersistentStringRWCloser(pss), nil
}
// openFileForWrite mirrors openFile on Windows: the memory-mapped file
// already handles safe read-modify-write (including growth) internally, so
// there's no separate atomic-write path needed here (see file_unix.go for
// why POSIX needs one).
// The memory-mapped file already handles safe read-modify-write (including
// growth) internally, so there's no separate atomic-write path needed here
// (see file_unix.go for why POSIX needs one).
func openFileForWrite(filePath string) (io.WriteCloser, error) {
return openFile(filePath)
}
+3 -7
View File
@@ -46,7 +46,6 @@ func (s Store) new() *store {
}
}
// getStore returns the appropriate store based on the Store identifier
func (s Store) get() *store {
switch s {
case Device:
@@ -64,7 +63,6 @@ func (s Store) get() *store {
}
}
// Init initializes a store with the given file path
func (s Store) init(filePath string, persist bool) {
defer log.Trace(time.Now(), string(s), filePath)
@@ -116,8 +114,9 @@ func (s Store) init(filePath string, persist bool) {
}
}
// touchSessionFile updates the session file's modification time if it's older than 1 hour.
// This prevents stale session cache files from being cleaned up while reducing steady-state overhead.
// Updates the modification time if older than 1 hour, preventing stale
// session cache files from being cleaned up while reducing steady-state
// overhead.
func touchSessionFile(filePath string) {
info, err := os.Stat(filePath)
if err != nil {
@@ -184,7 +183,6 @@ func (s Store) close() {
}
}
// Get retrieves a typed value from the specified store
func Get[T any](s Store, key string) (T, bool) {
var zero T
defer log.Trace(time.Now(), string(s), key)
@@ -218,7 +216,6 @@ func Get[T any](s Store, key string) (T, bool) {
return zero, false
}
// Set stores a typed value in the specified store
func Set[T any](s Store, key string, value T, duration Duration) {
defer log.Trace(time.Now(), string(s), key)
@@ -244,7 +241,6 @@ func Set[T any](s Store, key string, value T, duration Duration) {
store.dirty = true
}
// Delete removes a key from the specified store
func Delete(s Store, key string) {
defer log.Trace(time.Now(), string(s), key)
+3 -5
View File
@@ -91,11 +91,9 @@ func TestStore(t *testing.T) {
}
}
// TestStoreCloseTouchesSessionFileMTime verifies that closing a dirty,
// persisting session store bumps the on-disk file's mtime after a real
// persist. This guards against #7340: on Windows the mmap-backed write path
// doesn't reliably update the file's last-write-time on its own, which could
// cause an actively-used session cache to look stale and get swept up by
// Guards against #7340: on Windows the mmap-backed write path doesn't
// reliably update the file's last-write-time on its own, which could cause
// an actively-used session cache to look stale and get swept up by
// cache.Clear().
func TestStoreCloseTouchesSessionFileMTime(t *testing.T) {
origSession := session
-1
View File
@@ -25,7 +25,6 @@ const (
done
)
// ErrorGetter is implemented by auth models to get the error.
type ErrorGetter interface {
GetError() error
}
-2
View File
@@ -24,7 +24,6 @@ const (
CopilotTokenKey = "copilot_token"
)
// DeviceCodeResponse represents the response from GitHub's device code endpoint.
type DeviceCodeResponse struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
@@ -33,7 +32,6 @@ type DeviceCodeResponse struct {
Interval int `json:"interval"`
}
// AccessTokenResponse represents the response from GitHub's access token endpoint.
type AccessTokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
-1
View File
@@ -14,7 +14,6 @@ var (
session bool
)
// cacheCmd represents the cache command
var cacheCmd = &cobra.Command{
Use: "cache [path|clear|ttl|show]",
Short: "Interact with the oh-my-posh cache",
-1
View File
@@ -10,7 +10,6 @@ import (
"github.com/spf13/cobra"
)
// configCmd represents the config command
var configCmd = &cobra.Command{
Use: "config edit",
Short: "Interact with the config",
-1
View File
@@ -19,7 +19,6 @@ var (
output string
)
// exportCmd represents the export command
var exportCmd = &cobra.Command{
Use: "export",
Short: "Export your config",
+1 -5
View File
@@ -18,7 +18,6 @@ import (
var outputData string
// dataCmd represents the "config export data" command
var dataCmd = &cobra.Command{
Use: "data",
Short: "Export a template data file for your config",
@@ -105,10 +104,7 @@ Prints the recorded data to stdout.`,
},
}
// buildDataDocument builds the recorder's output document from an
// already-rendered config: the template cache's simple fields (env) plus
// every enabled segment's writer, keyed by DataKey (segments). Extracted
// from dataCmd's Run so it can be unit tested without a real environment.
// Extracted from dataCmd's Run so it can be unit tested without a real environment.
func buildDataDocument(cfg *config.Config) ([]byte, error) {
envRaw, err := json.Marshal(template.Cache.SimpleTemplate)
if err != nil {
+1 -4
View File
@@ -16,10 +16,7 @@ import (
"github.com/stretchr/testify/require"
)
// newRecordedSessionSegment builds a config.Segment with a real *segments.Session
// writer attached (via MapSegmentWithWriter, exactly like normal execution
// does), so buildDataDocument has something concrete to marshal. It does not
// run Execute/Enabled(), so the caller controls Enabled and the writer's
// Does not run Execute/Enabled(), so the caller controls Enabled and the writer's
// fields directly, keeping the test hermetic (no real environment probing).
func newRecordedSessionSegment(t *testing.T, alias string) *config.Segment {
t.Helper()
-1
View File
@@ -24,7 +24,6 @@ var (
imageTerminalWidth int
)
// imageCmd represents the image command
var imageCmd = &cobra.Command{
Use: "image",
Short: "Export your config to an image",
+5 -10
View File
@@ -5,18 +5,13 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
)
// dataPath is the shared --data flag value used by printCmd and imageCmd to
// render deterministically from a recorded template data file instead of
// the live environment.
// Shared between printCmd and imageCmd.
var dataPath string
// applyDataFile loads the template data file referenced by --data (if any)
// and routes it onto flags. It is a no-op when dataPath is empty.
//
// SegmentData and EnvData are copied onto flags verbatim - the rest of the
// pipeline (segment replay, template cache overlay) consumes them from
// there. The env keys that map directly onto runtime.Flags fields (see
// routedEnvDataKeys in the template package) are routed here explicitly,
// No-op when dataPath is empty. SegmentData and EnvData are copied onto flags
// verbatim - the rest of the pipeline (segment replay, template cache overlay)
// consumes them from there. Env keys that map directly onto runtime.Flags fields
// (see routedEnvDataKeys in the template package) are routed here explicitly,
// with precedence explicit CLI flag > data file > live environment: changed
// reports whether the corresponding CLI flag (by name) was set explicitly,
// in which case the data file's value is skipped.
+2 -8
View File
@@ -12,8 +12,6 @@ import (
"github.com/stretchr/testify/require"
)
// writeDataFile writes a JSON template data file to a temp dir and returns
// its path.
func writeDataFile(t *testing.T, content string) string {
t.Helper()
@@ -23,9 +21,7 @@ func writeDataFile(t *testing.T, content string) string {
return path
}
// withDataPath sets the package-level dataPath var for the duration of the
// test and restores it afterwards - dataPath is shared with the print/image
// commands so tests must not leak it across each other.
// dataPath is shared with the print/image commands so tests must not leak it across each other.
func withDataPath(t *testing.T, path string) {
t.Helper()
@@ -35,11 +31,9 @@ func withDataPath(t *testing.T, path string) {
t.Cleanup(func() { dataPath = previous })
}
// noneChanged reports that no CLI flag was explicitly set.
func noneChanged(string) bool { return false }
// changedSet returns a "Changed" func that reports true only for the given
// flag names, mimicking cmd.Flags().Changed for an explicitly-set flag.
// Mimics cmd.Flags().Changed for an explicitly-set flag.
func changedSet(names ...string) func(string) bool {
set := make(map[string]bool, len(names))
for _, name := range names {
-1
View File
@@ -18,7 +18,6 @@ import (
"github.com/spf13/cobra"
)
// debugCmd represents the debug command
var (
debugCmd = createDebugCmd()
startTime = time.Now()
-1
View File
@@ -6,7 +6,6 @@ import (
"github.com/spf13/cobra"
)
// disableCmd represents the disable command
var disableCmd = &cobra.Command{
Use: fmt.Sprintf(toggleUse, "disable"),
Short: "Disable a feature",
-1
View File
@@ -23,7 +23,6 @@ var (
toggleLong = strings.Join(append([]string{toggleHelpText}, toggleArgs...), "\n- ")
)
// enableCmd represents the enable command
var enableCmd = &cobra.Command{
Use: fmt.Sprintf(toggleUse, "enable"),
Short: "Enable a feature",
-8
View File
@@ -17,7 +17,6 @@ func init() {
gob.Register([]*Asset{})
}
// Font describes a font file and the various metadata associated with it.
type Font struct {
Name string `json:"name,omitempty" jsonschema:"title=Font name,description=The name of the font"`
Family string `json:"-"`
@@ -31,8 +30,6 @@ func (f *Font) Apply() error {
return err
}
// downloadAndInstall resolves a font by name or URL, downloads it, and installs it.
// It returns the resolved font name and any error encountered.
func downloadAndInstall(font, zipFolder string) (string, error) {
asset, err := ResolveFontAsset(font)
if err != nil {
@@ -64,16 +61,11 @@ func (f *Font) Resolve() (*Font, bool) {
return nil, false
}
// fontExtensions is a list of file extensions that denote fonts.
// Only files ending with these extensions will be installed.
var fontExtensions = map[string]bool{
".otf": true,
".ttf": true,
}
// newFont creates a newFont Font struct.
// fileName is the font's file name, and data is a byte slice containing the font file data.
// It returns a FontData struct describing the font, or an error.
func newFont(fileName string, data []byte) (*Font, error) {
if _, ok := fontExtensions[strings.ToLower(path.Ext(fileName))]; !ok {
return nil, fmt.Errorf("not a font: %v", fileName)
-1
View File
@@ -13,7 +13,6 @@ import (
"github.com/spf13/cobra"
)
// getCmd represents the get command
var getCmd = &cobra.Command{
Use: "get [shell|millis|accent|toggles|width]",
Short: "Get a value from oh-my-posh",
+4 -8
View File
@@ -8,12 +8,9 @@ import (
"strings"
)
// defaultColumns is the fixed canvas text width, in columns, used when
// Settings.Columns is not set (zero value). It matches the default
// --terminal-width the prompt engine renders with.
// Matches the default --terminal-width the prompt engine renders with.
const defaultColumns = 120
// Settings represents the structure for base 16 color overrides and other image settings.
// Expected JSON format:
//
// {
@@ -32,10 +29,9 @@ type Settings struct {
BackgroundColor string `json:"background_color"`
Fonts *Fonts `json:"fonts"`
Cursor string `json:"cursor,omitempty"`
// Columns is the fixed canvas text width, in columns. It should match
// the --terminal-width the config was rendered with so alignment
// padding produced by the prompt engine lines up with the image.
// A zero value falls back to defaultColumns.
// Should match the --terminal-width the config was rendered with so
// alignment padding produced by the prompt engine lines up with the
// image. Zero falls back to defaultColumns.
Columns int `json:"columns,omitempty"`
}
-2
View File
@@ -159,13 +159,11 @@ func TestLoadSettings(t *testing.T) {
}
}
// Helper interface for testing types that have TempDir method
type testingInterface interface {
TempDir() string
Helper()
}
// Helper function to create a temporary file with given content
func createTempFile(t testingInterface, content string) string {
t.Helper()
tempDir := t.TempDir()
+3 -7
View File
@@ -311,9 +311,8 @@ func (ir *Renderer) fontHeight() float64 {
return float64(ir.regular.Metrics().Height >> 6)
}
// resolvedColumns returns the fixed canvas text width, in columns, falling
// back to defaultColumns when Settings.Columns is unset (zero or negative),
// e.g. when loading a settings file written before Columns existed.
// Falls back to defaultColumns when Settings.Columns is unset (zero or
// negative), e.g. when loading a settings file written before Columns existed.
func (ir *Renderer) resolvedColumns() int {
if ir.Columns <= 0 {
return defaultColumns
@@ -362,9 +361,7 @@ var doubleWidthRunes = []RuneRange{
{Start: '\uea60', End: '\uebeb'},
}
// This is getting how many additional characters of width to allocate when drawing
// e.g. for characters that are 2 or more wide. A standard character will return 0
// Nerd Font glyphs will return 1, since most are double width
// Standard characters return 0; Nerd Font glyphs return 1, since most are double width.
func (ir *Renderer) runeAdditionalWidth(r rune) int {
for _, runeRange := range doubleWidthRunes {
if runeRange.Start <= r && r <= runeRange.End {
@@ -883,7 +880,6 @@ func (ir *Renderer) setBase16Color(colorStr string) {
ir.backgroundColor = tempColor
}
// colorNameFromCode maps ANSI color codes to color names
func colorNameFromCode(colorInt int) string {
switch colorInt {
case 30, 40:
-1
View File
@@ -11,7 +11,6 @@ import (
"github.com/spf13/cobra"
)
// noticeCmd represents the notice command
var noticeCmd = &cobra.Command{
Use: "notice",
Short: "Print the upgrade notice when a new version is available.",
-1
View File
@@ -34,7 +34,6 @@ var (
interrupted bool
)
// printCmd represents the print command
var printCmd = createPrintCmd()
func init() {
+8 -12
View File
@@ -16,21 +16,18 @@ import (
"github.com/spf13/cobra"
)
// requestPipe is the path to a named pipe (fifo) to read requests from
// instead of stdin. Unix only - used by shells that cannot hold a child's
// stdin open across prompts (fish).
// Unix only - used by shells that cannot hold a child's stdin open across
// prompts (fish).
var requestPipe string
// serveCmd represents the serve command
var serveCmd = createServeCmd()
func init() {
RootCmd.AddCommand(serveCmd)
}
// serveRequest mirrors the request protocol documented in the implementation
// plan: one JSON object per line on stdin. Unknown fields are ignored by
// encoding/json by default, which gives us forward compatibility for free.
// One JSON object per line on stdin. Unknown fields are ignored by
// encoding/json by default, giving forward compatibility for free.
type serveRequest struct {
Env map[string]string `json:"env"`
Command string `json:"command"`
@@ -110,11 +107,10 @@ func createServeCmd() *cobra.Command {
return serveCmd
}
// openServeInput returns the request source: stdin by default, or the given
// named pipe (fifo) opened read-write. O_RDWR is the load-bearing detail: the
// daemon itself keeps a writer on the fifo, so a client doing
// open-write-close per request (the only write primitive fish has) never
// EOFs the read side. Unix only - the shell owns the fifo's lifecycle.
// O_RDWR is the load-bearing detail: the daemon itself keeps a writer on the
// fifo, so a client doing open-write-close per request (the only write
// primitive fish has) never EOFs the read side. Unix only - the shell owns
// the fifo's lifecycle.
//
// Clients must write each request in a single write(2) call; requests from a
// single sequential writer (one shell session) never interleave regardless
-1
View File
@@ -10,7 +10,6 @@ import (
"github.com/spf13/cobra"
)
// shellCmd represents the shell command
var shellCmd = &cobra.Command{
Use: "shell get",
Short: "Get the shell name",
-1
View File
@@ -79,7 +79,6 @@ func statuslineRun[T any](shellConst, cacheKey string, sessionID func(*T) string
}
}
// processStatuslineData parses stdin JSON into T and stores it in the session cache.
func processStatuslineData[T any](stdinData []byte, shellConst, cacheKey string, sessionID func(*T) string) {
if len(stdinData) == 0 {
cache.Init(shellConst, cache.Persist, cache.NoSession)
-1
View File
@@ -12,7 +12,6 @@ import (
"github.com/spf13/cobra"
)
// streamCmd represents the stream command
var streamCmd = createStreamCmd()
func init() {
-1
View File
@@ -9,7 +9,6 @@ import (
"github.com/spf13/cobra"
)
// toggleCmd represents the toggle command
var toggleCmd = &cobra.Command{
Use: "toggle segment1 segment2 ...",
Short: "Toggle one or more segments on/off",
-1
View File
@@ -23,7 +23,6 @@ var (
auto bool
)
// upgradeCmd represents the upgrade command
var upgradeCmd = &cobra.Command{
Use: "upgrade",
Short: "Upgrade when a new version is available.",
-4
View File
@@ -18,10 +18,6 @@ To enable automated upgrades, run: 'oh-my-posh enable upgrade'.
`
)
// Returns the upgrade notice if a new version is available
// that should be displayed to the user.
//
// The upgrade check is only performed every other week.
func (cfg *Config) Notice() (string, bool) {
if !http.IsConnected() {
return "", false
-1
View File
@@ -11,7 +11,6 @@ var (
verbose bool
)
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version",
+3 -10
View File
@@ -29,11 +29,8 @@ const (
var TrueColor = true
// String is the interface that wraps ToColor method.
//
// ToColor gets the ANSI color code for a given color string.
// This can include a valid hex color in the format `#FFFFFF`,
// but also a name of one of the first 16 ANSI colors like `lightBlue`.
// String converts a color string — a hex color like `#FFFFFF`, or one of the
// first 16 ANSI color names like `lightBlue` — to an ANSI code.
type String interface {
ToAnsi(colorString Ansi, isBackground bool) Ansi
Resolve(colorString Ansi) (Ansi, error)
@@ -239,7 +236,6 @@ type RGB struct {
R, G, B uint8
}
// Defaults is the default AnsiColors implementation.
type Defaults struct {
accent *Set
}
@@ -344,8 +340,6 @@ func (d *Defaults) Resolve(colorString Ansi) (Ansi, error) {
return colorString, nil
}
// getAnsiColorFromName returns the color code for a given color name if the name is
// known ANSI color name.
func getAnsiColorFromName(colorValue Ansi, isBackground bool) (Ansi, error) {
if colorCodes, found := ansiColorCodes[colorValue]; found {
return colorCodes[generics.ToInt[int](isBackground)], nil
@@ -398,8 +392,7 @@ func (p *PaletteColors) Resolve(colorString Ansi) (Ansi, error) {
}
// Cached is the AnsiColors Decorator that does simple color lookup caching.
// ToColor calls are cheap, but not free, and having a simple cache in
// has measurable positive effect on performance.
// ToAnsi calls are cheap but not free, and caching has a measurable positive effect on performance.
type Cached struct {
ansiColors String
colorCache map[cachedColorKey]Ansi
+2 -3
View File
@@ -67,9 +67,8 @@ func TestMakeColors(t *testing.T) {
assert.IsType(t, &Defaults{}, colors.(*Cached).ansiColors.(*PaletteColors).ansiColors)
}
// TestGradientPassesThroughAnsiColorDecorators verifies a gradient string is never mangled
// by hex/256 parsing or palette resolution; it must round-trip untouched through every
// String decorator so the terminal writer can render it per cell.
// A gradient string must round-trip untouched through every String decorator, never mangled
// by hex/256 parsing or palette resolution, so the terminal writer can render it per cell.
func TestGradientPassesThroughAnsiColorDecorators(t *testing.T) {
gradient := Ansi("linear-gradient(#FF0000, #0000FF)")
+7 -10
View File
@@ -17,7 +17,6 @@ const (
gradientSuffix = ")"
)
// gradientPrefixes lists every recognized gradient prefix, checked in order.
var gradientPrefixes = [...]string{linearGradientPrefix, darkGradientPrefix, lightGradientPrefix}
// IsGradient reports whether c is a gradient definition: a multi-stop
@@ -28,7 +27,6 @@ func (c Ansi) IsGradient() bool {
return ok
}
// gradientPrefix returns the gradient prefix c starts with, and whether it matched any.
func (c Ansi) gradientPrefix() (string, bool) {
s := c.String()
@@ -118,8 +116,7 @@ func (c Ansi) WithGradientStops(stops []Ansi) Ansi {
return Ansi(prefix + strings.Join(parts, ", ") + gradientSuffix)
}
// GradientFirst returns the first stop of the gradient. It returns c unchanged when c is not
// a gradient, or when the gradient syntax is invalid.
// Returns c unchanged when c is not a gradient, or the gradient syntax is invalid.
func (c Ansi) GradientFirst() Ansi {
stops := c.GradientStops()
if len(stops) == 0 {
@@ -129,12 +126,12 @@ func (c Ansi) GradientFirst() Ansi {
return stops[0]
}
// GradientLast returns the last stop of the gradient. It returns c unchanged when c is not
// a gradient, or when the gradient syntax is invalid. Equivalent to GradientLastForCells(0):
// a dark-gradient/light-gradient shades using the narrowest (gentlest) auto-shade step, since
// the segment's actual width isn't known here. Callers that know it — the segment's own
// separators, diamond caps, and inline overrides — should call GradientLastForCells instead,
// so the edge matches the actual last cell GradientCells renders rather than the fallback.
// Returns c unchanged when c is not a gradient, or the gradient syntax is invalid.
// Equivalent to GradientLastForCells(0): a dark-gradient/light-gradient shades using the
// narrowest (gentlest) auto-shade step, since the segment's actual width isn't known here.
// Callers that know it — the segment's own separators, diamond caps, and inline overrides —
// should call GradientLastForCells instead, so the edge matches the actual last cell
// GradientCells renders rather than the fallback.
func (c Ansi) GradientLast() Ansi {
return c.GradientLastForCells(0)
}
+19 -29
View File
@@ -126,8 +126,7 @@ func TestGradientCellsInterpolation(t *testing.T) {
}
}
// TestGradientCellsMonotonicProgression checks the red channel never regresses across a
// black-to-white ramp, where R, G and B move in lockstep.
// R, G and B move in lockstep across the ramp, so checking the red channel alone suffices.
func TestGradientCellsMonotonicProgression(t *testing.T) {
result := GradientCells("linear-gradient(#000000, #FFFFFF)", 5, &Defaults{}, false, nil, nil)
assert.Len(t, result, 5)
@@ -165,12 +164,9 @@ func TestGradientCellsInvalidReturnsNil(t *testing.T) {
}
}
// TestGradientCellsAutoShade verifies dark-gradient(#color)/light-gradient(#color) — a
// single explicit stop — spreads into a two-stop gradient running from the exact
// configured color to a darker/lighter shade of it, instead of collapsing like an
// ordinary invalid (< 2 stop) linear-gradient. The first cell must be the unmodified
// configured color (matching GradientFirst) and the last must match
// GradientLastForCells for the SAME cell count, so separators/caps line up.
// A single-stop dark-gradient/light-gradient spreads into a two-stop gradient running from
// the exact configured color to a darker/lighter shade of it, instead of collapsing like an
// ordinary invalid (< 2 stop) linear-gradient.
func TestGradientCellsAutoShade(t *testing.T) {
cases := []struct {
Case string
@@ -194,11 +190,8 @@ func TestGradientCellsAutoShade(t *testing.T) {
}
}
// TestGradientCellsAutoShadeScalesWithWidth verifies the total base-to-shade delta
// grows with the segment's cell count instead of staying fixed: a wide segment must
// end further from its base color than a narrow one, so a wide gradient still reads
// as a clear effect instead of fading into an imperceptibly fine ramp - the report
// behind this fix was that a wide segment's gradient looked completely flat.
// A wide segment must end further from its base color than a narrow one; the bug behind
// this fix was that a wide segment's gradient looked completely flat.
func TestGradientCellsAutoShadeScalesWithWidth(t *testing.T) {
narrow := GradientCells("dark-gradient(#179299)", 3, &Defaults{}, true, nil, nil)
wide := GradientCells("dark-gradient(#179299)", 15, &Defaults{}, true, nil, nil)
@@ -215,17 +208,16 @@ func TestGradientCellsAutoShadeScalesWithWidth(t *testing.T) {
assert.True(t, base.DistanceLab(wideLast) > base.DistanceLab(narrowLast), "a 15-cell segment must end further from the base color than a 3-cell one")
}
// TestGradientCellsAutoShadeSingleCell verifies a single-cell segment (too narrow to
// show any blend) renders the configured color unmodified, not a shaded endpoint.
// A single-cell segment (too narrow to blend) renders the configured color unmodified, not a shaded endpoint.
func TestGradientCellsAutoShadeSingleCell(t *testing.T) {
result := GradientCells("dark-gradient(#3465A4)", 1, &Defaults{}, false, nil, nil)
assert.Equal(t, []Ansi{"38;2;52;101;164"}, result)
}
// TestGradientLastAutoShade verifies GradientLast (width unknown, the gentlest single-
// step shade) and GradientLastForCells (matching GradientCells for a given cell count)
// darken for dark-gradient and lighten for light-gradient, and fall back to the raw
// stop for one that can't be shaded without a resolver (keyword, palette reference).
// GradientLast (width unknown, the gentlest single-step shade) and GradientLastForCells
// (matching GradientCells for a given cell count) darken for dark-gradient and lighten for
// light-gradient, and fall back to the raw stop for one that can't be shaded without a
// resolver (keyword, palette reference).
func TestGradientLastAutoShade(t *testing.T) {
unshadeable := "can't be shaded without a resolver, so it passes through unchanged"
@@ -240,9 +232,9 @@ func TestGradientLastAutoShade(t *testing.T) {
assert.Equal(t, Ansi("#3465A4"), Ansi("linear-gradient(#3465A4)").GradientLast(), "a single-stop linear-gradient is not auto-shaded")
}
// TestWithGradientStops verifies the rebuilt string keeps c's own prefix, so a palette
// reference resolved inside a dark-gradient/light-gradient stays that same kind instead
// of silently becoming a plain linear-gradient.
// The rebuilt string keeps c's own prefix, so a palette reference resolved inside a
// dark-gradient/light-gradient stays that same kind instead of silently becoming a plain
// linear-gradient.
func TestWithGradientStops(t *testing.T) {
cases := []struct {
Case string
@@ -274,10 +266,9 @@ func TestGradientCellsColor256Fallback(t *testing.T) {
}
}
// TestGradientCellsKeywordStops verifies keyword stops resolve against the segment
// context before interpolation: parentBackground picks up the parent's color (a parent
// gradient collapses to its last stop), and keywords without a hex resolution
// invalidate the stop.
// Keyword stops resolve against the segment context before interpolation: parentBackground
// picks up the parent's color (a parent gradient collapses to its last stop), and keywords
// without a hex resolution invalidate the stop.
func TestGradientCellsKeywordStops(t *testing.T) {
parents := []*Set{{Background: "#112233", Foreground: "#445566"}}
gradientParents := []*Set{{Background: "linear-gradient(#FF0000, #0000FF)", Foreground: "#445566"}}
@@ -323,9 +314,8 @@ func TestGradientCellsKeywordStops(t *testing.T) {
assert.Nil(t, GradientCells("linear-gradient(transparent, #FFFFFF)", 2, &Defaults{}, false, current, parents), "transparent is never a valid stop")
}
// TestGradientCellsAccentStop verifies the accent keyword works as a stop: it resolves
// through ToAnsi to a truecolor payload, which parseTrueColor recovers. An unresolved
// accent (empty Set) invalidates the stop instead of erroring hard.
// The accent keyword resolves through ToAnsi to a truecolor payload, which parseTrueColor
// recovers. An unresolved accent (empty Set) invalidates the stop instead of erroring hard.
func TestGradientCellsAccentStop(t *testing.T) {
resolver := &Defaults{
accent: &Set{
+4 -10
View File
@@ -3,18 +3,12 @@ package color
import "slices"
const (
// Transparent implies a transparent color
Transparent Ansi = "transparent"
// Accent is the OS accent color
Accent Ansi = "accent"
// ParentBackground takes the previous segment's background color
Transparent Ansi = "transparent"
Accent Ansi = "accent"
ParentBackground Ansi = "parentBackground"
// ParentForeground takes the previous segment's color
ParentForeground Ansi = "parentForeground"
// Background takes the current segment's background color
Background Ansi = "background"
// Foreground takes the current segment's foreground color
Foreground Ansi = "foreground"
Background Ansi = "background"
Foreground Ansi = "foreground"
)
func (color Ansi) isKeyword() bool {
+2 -3
View File
@@ -44,9 +44,8 @@ func TestResolveCurrentKeywordKeepsGradientIntact(t *testing.T) {
}
}
// TestResolveParentGradientKeywordStop pins the review fix: a parent gradient whose
// last stop is a keyword resolves against the PARENT's colors, never the child's,
// and unresolvable self-references degrade to transparent.
// A parent gradient whose last stop is a keyword resolves against the PARENT's colors, never
// the child's; unresolvable self-references degrade to transparent.
func TestResolveParentGradientKeywordStop(t *testing.T) {
cases := []struct {
Case string
+2 -7
View File
@@ -17,8 +17,7 @@ const (
paletteRecursiveKeyError = "palette: recursive resolution of color %s returned palette reference %s and reached recursion depth %d"
)
// ResolveColor gets a color value from the palette using given colorName.
// If colorName is not a palette reference, it is returned as is.
// Returns colorName unchanged if it is not a palette reference.
func (p Palette) ResolveColor(colorName Ansi) (Ansi, error) {
return p.resolveColor(colorName, 1, &colorName)
}
@@ -71,7 +70,6 @@ func isPaletteKey(colorName Ansi) (Ansi, bool) {
return paletteKeyPrefix, strings.HasPrefix(colorName.String(), paletteKeyPrefix)
}
// PaletteKeyError records the missing Palette key.
type PaletteKeyError struct {
palette Palette
Key Ansi
@@ -88,8 +86,6 @@ func (p *PaletteKeyError) Error() string {
return errorStr
}
// PaletteRecursiveKeyError records the Palette key and resolved color value (which
// is also a Palette key)
type PaletteRecursiveKeyError struct {
Key Ansi
Value Ansi
@@ -118,8 +114,7 @@ func (p Palette) resolveShade(colorString Ansi) (Ansi, error) {
return withShadeCall(dir, resolved, percent), nil
}
// MaybeResolveColor wraps resolveColor and silences possible errors, returning
// Transparent color by default, as a Block does not know how to handle color errors.
// Returns emptyColor instead of surfacing errors, since a Block has no way to handle color errors.
func (p Palette) MaybeResolveColor(colorName Ansi) Ansi {
color, err := p.ResolveColor(colorName)
if err != nil {
+8 -16
View File
@@ -2,31 +2,24 @@ package config
import "fmt"
// BlockType type of block
type BlockType string
// BlockAlignment alignment 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"
// RPrompt is a right aligned prompt
Prompt BlockType = "prompt"
RPrompt BlockType = "rprompt"
// Left aligns left
Left BlockAlignment = "left"
// Right aligns right
Left BlockAlignment = "left"
Right BlockAlignment = "right"
// Break adds a line break
Break Overflow = "break"
// Hide hides the block
Hide Overflow = "hide"
Hide Overflow = "hide"
)
// Block defines a part of the prompt with optional segments
type Block struct {
presentFields map[string]bool
Type BlockType `json:"type,omitempty" toml:"type,omitempty" yaml:"type,omitempty"`
@@ -50,10 +43,9 @@ func (b *Block) key() any {
return fmt.Sprintf("%s-%s", b.Type, b.Alignment)
}
// fieldPresent reports whether name (a json tag key) was present in the
// source block entry. A nil presentFields map means presence was never
// recorded, in which case every field is treated as present, preserving
// merge's legacy unconditional-overwrite behavior for such blocks.
// A nil presentFields map means presence was never recorded, in which case every
// field is treated as present, preserving merge's legacy unconditional-overwrite
// behavior for such blocks. name is the json tag key.
func (b *Block) fieldPresent(name string) bool {
if b.presentFields == nil {
return true
+5 -10
View File
@@ -48,7 +48,6 @@ const (
Extend Action = "extend"
)
// Config holds all the theme for rendering the prompt
type Config struct {
Palette color.Palette `json:"palette,omitempty" toml:"palette,omitempty" yaml:"palette,omitempty"`
DebugPrompt *Segment `json:"debug_prompt,omitempty" toml:"debug_prompt,omitempty" yaml:"debug_prompt,omitempty"`
@@ -253,11 +252,10 @@ func (cfg *Config) Hash() uint64 {
return cfg.hash
}
// fieldPresent reports whether name (a json tag key) was present in the
// source config file. A nil presentFields map means presence was never
// recorded (e.g. a struct literal built without read()), in which case every
// field is treated as present, preserving merge's legacy unconditional-
// overwrite behavior for such configs.
// A nil presentFields map means presence was never recorded (e.g. a struct
// literal built without read()), in which case every field is treated as
// present, preserving merge's legacy unconditional-overwrite behavior for
// such configs. name is the json tag key.
func (cfg *Config) fieldPresent(name string) bool {
if cfg.presentFields == nil {
return true
@@ -266,8 +264,7 @@ func (cfg *Config) fieldPresent(name string) bool {
return cfg.presentFields[name]
}
// migrateSegmentProperties migrates the deprecated Properties field to Options for all segments.
// This is needed for TOML configs since go-toml/v2 doesn't support custom unmarshalers.
// Needed for TOML configs since go-toml/v2 doesn't support custom unmarshalers.
func (cfg *Config) migrateSegmentProperties() {
for _, block := range cfg.Blocks {
for _, segment := range block.Segments {
@@ -276,8 +273,6 @@ func (cfg *Config) migrateSegmentProperties() {
}
}
// toggleSegments processes all segments in all blocks and adds segments
// with Toggled == true to the toggle cache, effectively toggling them off.
func (cfg *Config) toggleSegments() {
currentToggleSet, _ := cache.Get[map[string]bool](cache.Session, cache.TOGGLECACHE)
if currentToggleSet == nil {
+1 -2
View File
@@ -31,8 +31,7 @@ type EnvData struct {
Executed *bool
}
// LoadData reads and parses a template data file. The format is derived
// from the file extension: .json/.jsonc, .yaml/.yml, or .toml.
// The format is derived from the file extension: .json/.jsonc, .yaml/.yml, or .toml.
func LoadData(path string) (*Data, error) {
raw, err := os.ReadFile(path)
if err != nil {
+2 -3
View File
@@ -224,9 +224,8 @@ func CopilotCLI() *Config {
return statuslineCLIConfig(1234567891, COPILOTCLI, " \uec1e {{ .Model.DisplayName }} \uf2d0 {{ .TokenGauge }} ")
}
// statuslineCLIConfig builds the shared default config for AI CLI statusline integrations
// (e.g. Claude, Copilot CLI). The left block is always PATH + GIT; the right block
// contains a single segment of the given type and template.
// The left block is always PATH + GIT; the right block contains a single
// segment of the given type and template.
func statuslineCLIConfig(hash uint64, segmentType SegmentType, template string) *Config {
return &Config{
hash: hash,
+1 -4
View File
@@ -24,7 +24,6 @@ import (
yaml "go.yaml.in/yaml/v3"
)
// Custom error types for config validation
type Error struct {
message string
}
@@ -352,13 +351,11 @@ func getData(configFile string) ([]byte, error) {
return http.Download(configFile, true)
}
// isCygwin checks if we're running in Cygwin environment
func isCygwin() bool {
return runtimelib.GOOS == "windows" && len(os.Getenv("OSTYPE")) > 0
}
// themes maps a theme's short name to its file name; it's a package-level
// var (instead of a local literal) so isTheme doesn't rebuild it on every call.
// Package-level var (instead of a local literal) so isTheme doesn't rebuild it on every call.
var themes = map[string]string{
"1_shell": "1_shell.omp.json",
"m365princess": "M365Princess.omp.json",
-5
View File
@@ -17,10 +17,6 @@ func writeExtendsFixture(t *testing.T, path, contents string) {
require.NoError(t, os.WriteFile(path, []byte(contents), 0o644))
}
// TestParseExtendsChainThreeLevels proves that a multi-hop extends chain
// (A extends B extends C), with every config in the same directory, still
// merges every level correctly.
//
// The distinguishing fields are all strings: merge() only skips a field when
// it is the Go zero value (see isZeroValue in merge.go), and for bool/int
// fields the zero value (false/0) is indistinguishable from "not set in this
@@ -128,7 +124,6 @@ func TestParseExtendsCycleDoesNotHang(t *testing.T) {
}
}
// writeConfigFile writes contents to name inside dir and fails the test on error.
func writeConfigFile(t *testing.T, dir, name, contents string) string {
t.Helper()
-1
View File
@@ -175,7 +175,6 @@ func merge(override, base any, skipFields ...string) error {
return nil
}
// skipField decides whether overrideField should be left alone during merge.
// For scalar kinds (bool/int/uint/float) whose zero value is ambiguous between
// "explicitly set" and "absent from the source", it consults override's
// field-presence data (when available) instead of isZeroValue. All other
+4 -10
View File
@@ -23,7 +23,6 @@ import (
"golang.org/x/text/language"
)
// SegmentStyle the style of segment, for more information, see the constants
type SegmentStyle string
func (s *SegmentStyle) resolve(context any) SegmentStyle {
@@ -94,10 +93,9 @@ type Segment struct {
evaluated bool
}
// fieldPresent reports whether name (a json tag key) was present in the
// source segment entry. A nil presentFields map means presence was never
// recorded, in which case every field is treated as present, preserving
// merge's legacy unconditional-overwrite behavior for such segments.
// A nil presentFields map means presence was never recorded, in which case every
// field is treated as present, preserving merge's legacy unconditional-overwrite
// behavior for such segments. name is the json tag key.
func (segment *Segment) fieldPresent(name string) bool {
if segment.presentFields == nil {
return true
@@ -156,8 +154,7 @@ func (segment *Segment) UnmarshalYAML(node *yaml.Node) error {
return modifiedNode.Decode((*segmentAlias)(segment))
}
// MigratePropertiesToOptions migrates the deprecated Properties field to Options.
// This is needed for TOML configs since go-toml/v2 doesn't support custom unmarshalers.
// Needed for TOML configs since go-toml/v2 doesn't support custom unmarshalers.
func (segment *Segment) MigratePropertiesToOptions() {
if len(segment.Properties) > 0 && len(segment.Options) == 0 {
segment.Options = segment.Properties
@@ -412,8 +409,6 @@ func (segment *Segment) hasCache() bool {
return segment.Cache != nil && !segment.Cache.Duration.IsEmpty()
}
// DataKey returns the identity used to look up segment data: the segment's
// alias if set, falling back to its type.
func (segment *Segment) DataKey() string {
if segment.Alias != "" {
return segment.Alias
@@ -422,7 +417,6 @@ func (segment *Segment) DataKey() string {
return string(segment.Type)
}
// Writer returns the segment's underlying SegmentWriter.
func (segment *Segment) Writer() SegmentWriter {
return segment.writer
}
-1
View File
@@ -390,7 +390,6 @@ const (
ZVM SegmentType = "zvm"
)
// Segments contains all available prompt segment writers.
// Consumers of the library can also add their own segment writer.
var Segments = map[SegmentType]func() SegmentWriter{
ANGULAR: func() SegmentWriter { return &segments.Angular{} },
-2
View File
@@ -2,12 +2,10 @@ package generics
import "fmt"
// ParseStringSlice converts any slice to a string slice
func ParseStringSlice(param any) []string {
return parseSlice(param, func(v any) string { return fmt.Sprint(v) })
}
// parseSlice converts any slice type to a typed slice using a converter function
func parseSlice[T any](param any, converter func(any) T) []T {
switch v := param.(type) {
case []any:
+2 -3
View File
@@ -22,9 +22,8 @@ func Enable(plain bool) {
Debugf("logging enabled, raw mode: %t", plain)
}
// Enabled reports whether logging is currently enabled. Call sites that build
// an expensive message (e.g. via fmt.Sprintf) before calling Debug/Trace should
// guard with this check so the formatting is skipped when logging is disabled.
// Call sites that build an expensive message (e.g. via fmt.Sprintf) before calling
// Debug/Trace should guard with this check so the formatting is skipped when logging is disabled.
func Enabled() bool {
return enabled
}
-1
View File
@@ -11,7 +11,6 @@ func NewConcurrent[V any]() *Concurrent[V] {
return &Concurrent[V]{}
}
// Concurrent is a generic type-safe concurrent map
type Concurrent[V any] struct {
m sync.Map
}
-1
View File
@@ -1,6 +1,5 @@
package maps
// Simple is a generic map type that can be specialized for different value types
type Simple[V any] map[string]V
func (m Simple[V]) ToConcurrent() *Concurrent[V] {
+2 -9
View File
@@ -229,10 +229,8 @@ func (e *Engine) getTitleTemplateText() string {
return ""
}
// renderLaunchedBlock renders a block using pre-collected segment results
// (see drainBlockResults). executed must be fully populated for every block
// in the prompt before this is called so that cross-block .Segments.X
// dependencies resolve in both directions.
// executed must be fully populated for every block in the prompt before this is called
// (see drainBlockResults) so that cross-block .Segments.X dependencies resolve in both directions.
func (e *Engine) renderLaunchedBlock(block *config.Block, results []*config.Segment, executed map[string]bool, cancelNewline bool) bool {
var blockText string
var length int
@@ -249,7 +247,6 @@ func (e *Engine) renderLaunchedBlock(block *config.Block, results []*config.Segm
return e.writeBlock(block, blockText, length, cancelNewline)
}
// writeBlock handles the common logic for writing a block to the prompt
func (e *Engine) writeBlock(block *config.Block, blockText string, length int, cancelNewline bool) bool {
defer func() {
e.applyPowerShellBleedPatch()
@@ -319,7 +316,6 @@ func (e *Engine) writeBlock(block *config.Block, blockText string, length int, c
return true
}
// renderBlockFromCache re-renders a block using existing segment data without re-execution
func (e *Engine) renderBlockFromCache(block *config.Block, cancelNewline bool) bool {
if block.RestartCycle {
cycle = &e.Config.Cycle
@@ -802,9 +798,6 @@ func (e *Engine) cancelNewline() bool {
return e.Env.Flags().Cleared || e.Env.Flags().PromptCount == 1 || row == 1
}
// New returns a prompt engine initialized with the
// given configuration options, and is ready to print any
// of the prompt components.
func New(flags *runtime.Flags) *Engine {
env := &runtime.Terminal{}
env.Init(flags)
+1 -2
View File
@@ -179,8 +179,7 @@ func (e *Engine) TransientRPrompt() string {
return str
}
// renderRightTemplate renders the transient prompt's right-aligned template.
// Only shells with a supported native or emulated right prompt can display it.
// Only shells with a supported native or emulated right prompt can display this.
func (e *Engine) renderRightTemplate(prompt *config.Segment, background, foreground color.Ansi) (string, int) {
if len(prompt.RightTemplate) == 0 {
return "", 0
-2
View File
@@ -14,7 +14,6 @@ func (e *Engine) Primary() string {
return e.primaryInternal(false)
}
// primaryInternal handles both regular and streaming prompt rendering
func (e *Engine) primaryInternal(fromCache bool) string {
needsPrimaryRightPrompt := e.needsPrimaryRightPrompt()
@@ -52,7 +51,6 @@ func (e *Engine) writePrimaryPrompt(needsPrimaryRPrompt bool) {
e.writePrimaryPromptInternal(needsPrimaryRPrompt, false)
}
// writePrimaryPromptInternal handles both regular and streaming prompt rendering
func (e *Engine) writePrimaryPromptInternal(needsPrimaryRPrompt, fromCache bool) {
if e.Config.ShellIntegration {
exitCode, _ := e.Env.StatusCodes()
+5 -12
View File
@@ -28,11 +28,9 @@ func (e *Engine) writeBlockSegments(block *config.Block) (string, int) {
return e.renderBlockSegments(results, block, executed)
}
// launchBlockSegments starts execution for every segment in the block and
// returns the channel that will receive their results as they complete.
// Callers may consume the channel immediately or defer consumption to
// allow other blocks' segments to execute concurrently in the meantime.
// Returns nil when the block has no segments.
// Callers may consume the channel immediately or defer consumption to allow
// other blocks' segments to execute concurrently in the meantime. Returns nil
// when the block has no segments.
func (e *Engine) launchBlockSegments(block *config.Block) chan result {
length := len(block.Segments)
@@ -47,9 +45,8 @@ func (e *Engine) launchBlockSegments(block *config.Block) chan result {
return out
}
// drainBlockResults drains a result channel and records each completed segment
// in executed. Calling this for every block before any rendering begins ensures
// the executed map is fully populated, so cross-block .Segments.X dependencies
// Calling this for every block before any rendering begins ensures the
// executed map is fully populated, so cross-block .Segments.X dependencies
// resolve in both directions (an earlier block can reference a later block's
// segment and vice versa).
func drainBlockResults(out chan result, count int, executed map[string]bool) []*config.Segment {
@@ -62,7 +59,6 @@ func drainBlockResults(out chan result, count int, executed map[string]bool) []*
return results
}
// renderBlockSegments renders pre-collected segment results in dependency order.
// Rendering is strictly sequential. For multi-block prompts, executed must be
// fully populated for all blocks before this is called (via drainBlockResults),
// so that cross-block .Segments.X dependencies resolve in both directions.
@@ -86,7 +82,6 @@ func (e *Engine) renderBlockSegments(results []*config.Segment, block *config.Bl
return terminal.String()
}
// writeSegmentsConcurrently uses individual goroutines for each segment
func (e *Engine) writeSegmentsConcurrently(segments []*config.Segment, out chan result) {
for i, segment := range segments {
// In streaming mode, pre-register all segments as pending
@@ -117,7 +112,6 @@ func (e *Engine) writeSegmentsConcurrently(segments []*config.Segment, out chan
}
}
// executeSegmentWithTimeout handles segment execution with timeout logic
func (e *Engine) executeSegmentWithTimeout(segment *config.Segment) {
done := make(chan bool)
gidChan := make(chan uint64, 1)
@@ -207,7 +201,6 @@ func (e *Engine) writeSegment(block *config.Block, segment *config.Segment) {
e.renderActiveSegment()
}
// canRenderSegment now uses map for O(1) lookups instead of O(n) slice search
func (e *Engine) canRenderSegment(segment *config.Segment, executed map[string]bool) bool {
for _, name := range segment.Needs {
if !executed[name] {
-6
View File
@@ -10,8 +10,6 @@ import (
// the transient prompt on Enter needs no additional CLI call.
const TransientMarker = "\x1e"
// StreamPrimary returns a channel that yields prompt updates as segments complete.
//
// The engine + terminal package globals are not thread-safe, so at most one
// StreamPrimary producer goroutine may be rendering at any given time. Callers
// that need to interrupt an in-flight cycle (e.g. a long-lived server handling
@@ -180,7 +178,6 @@ func (e *Engine) Abort() {
}
}
// countPendingSegments counts how many segments are marked as pending
func (e *Engine) countPendingSegments() int {
count := 0
e.pendingSegments.Range(func(_, _ any) bool {
@@ -190,7 +187,6 @@ func (e *Engine) countPendingSegments() int {
return count
}
// renderFromBlocks re-renders the complete prompt using stored block data
func (e *Engine) renderFromBlocks() string {
// Reset prompt builder
e.prompt.Reset()
@@ -203,7 +199,6 @@ func (e *Engine) renderFromBlocks() string {
return e.primaryInternal(true)
}
// trackPendingSegment continues execution for a timed-out segment in the background
func (e *Engine) trackPendingSegment(segment *config.Segment, done chan bool) {
if e.streamingResults == nil {
return
@@ -217,7 +212,6 @@ func (e *Engine) trackPendingSegment(segment *config.Segment, done chan bool) {
}()
}
// notifySegmentCompletion sends completed segment to the streaming results channel
func (e *Engine) notifySegmentCompletion(segment *config.Segment) {
if e.streamingResults == nil {
return
+20 -24
View File
@@ -153,10 +153,10 @@ func TestStreamPrimary_RecoversFromRenderPanic(t *testing.T) {
}
}
// TestStreamPrimary_AbortUnblocksSaturatedProducer guards against the abort
// deadlock: when the record channel fills against a stalled consumer, the
// producer blocks in a send; Abort() must still unblock it (via the
// abort-aware send) instead of waiting forever for the goroutine to exit.
// Guards against the abort deadlock: when the record channel fills against a
// stalled consumer, the producer blocks in a send; Abort() must still unblock
// it (via the abort-aware send) instead of waiting forever for the goroutine
// to exit.
func TestStreamPrimary_AbortUnblocksSaturatedProducer(t *testing.T) {
env := setupStreamingTestEnv()
@@ -429,7 +429,6 @@ func TestSegmentPendingState(t *testing.T) {
assert.NotEqual(t, "...", text, "Non-pending segment should show actual content")
}
// Helper function to collect all output from a channel with timeout
func collectChannelOutput(ch <-chan string, timeout time.Duration) []string {
var results []string
timer := time.NewTimer(timeout)
@@ -697,10 +696,9 @@ func TestStreamPrimary_NoStreamingResults_Channel(t *testing.T) {
assert.Len(t, prompts, 2, "Should get the initial prompt and transient record with no pending segments")
}
// TestStreamPrimary_RaceConditionFix validates that the streaming loop
// correctly handles segments that complete after Primary() but before/during
// the counting phase. This tests the fix for the race where pendingCount
// could get out of sync with actual pending segments.
// Validates that the streaming loop correctly handles segments that complete
// after Primary() but before/during the counting phase - the fix for the race
// where pendingCount could get out of sync with actual pending segments.
func TestStreamPrimary_RaceConditionFix(t *testing.T) {
env := new(mock.Environment)
env.On("Pwd").Return("/test")
@@ -788,11 +786,10 @@ func TestStreamPrimary_RaceConditionFix(t *testing.T) {
assert.Equal(t, 0, count, "All pending segments should be cleared")
}
// TestStreamPrimary_Abort_StopsRenderingAndDrains validates that Abort() stops
// the producer from emitting further records and blocks until the producer
// goroutine has fully exited, even when a segment completes (and tries to
// notify) after the abort was issued - that late notification must not panic
// or deadlock.
// Validates that Abort() stops the producer from emitting further records and
// blocks until the producer goroutine has fully exited, even when a segment
// completes (and tries to notify) after the abort was issued - that late
// notification must not panic or deadlock.
func TestStreamPrimary_Abort_StopsRenderingAndDrains(t *testing.T) {
env := setupStreamingTestEnv()
@@ -855,11 +852,10 @@ func TestStreamPrimary_Abort_StopsRenderingAndDrains(t *testing.T) {
time.Sleep(40 * time.Millisecond)
}
// TestStreamPrimary_NoStreamingTimeout_ChannelCloses guards against the
// pending-segment leak: with the Streaming flag set but no top-level
// "streaming" timeout in the config (Config.Streaming == 0), every segment
// used to be pre-registered in pendingSegments and never cleaned up (the
// cleanup in writeSegmentsConcurrently only ran for Timeout > 0), so
// Guards against the pending-segment leak: with the Streaming flag set but no
// top-level "streaming" timeout in the config (Config.Streaming == 0), every
// segment used to be pre-registered in pendingSegments and never cleaned up
// (the cleanup in writeSegmentsConcurrently only ran for Timeout > 0), so
// countPendingSegments never reached 0, the producer goroutine waited on
// streamingResults forever, and the stream CLI command never exited.
func TestStreamPrimary_NoStreamingTimeout_ChannelCloses(t *testing.T) {
@@ -912,16 +908,16 @@ func TestStreamPrimary_NoStreamingTimeout_ChannelCloses(t *testing.T) {
}
}
// TestStreamPrimary_Abort_BeforeAnyRender validates that Abort can be called
// (and is a safe no-op) when no cycle has ever been started.
// Validates that Abort can be called (and is a safe no-op) when no cycle has
// ever been started.
func TestStreamPrimary_Abort_NoCycleStarted(t *testing.T) {
engine := &Engine{}
assert.NotPanics(t, func() { engine.Abort() })
}
// TestStreamPrimary_Abort_ThenNewCycleWorks validates that a fresh
// StreamPrimary cycle works correctly after a previous cycle was aborted -
// this is the serialization guarantee the serve command depends on.
// Validates that a fresh StreamPrimary cycle works correctly after a previous
// cycle was aborted - this is the serialization guarantee the serve command
// depends on.
func TestStreamPrimary_Abort_ThenNewCycleWorks(t *testing.T) {
engine := setupBasicStreamingTestEnv()
+3 -3
View File
@@ -129,9 +129,9 @@ func FindStringMatch(pattern, text string, index int) (string, bool) {
return match, true
}
// FindStringMatchIndex returns the byte offsets of the submatch at index within text,
// so the caller can splice the match at its actual position rather than searching for
// its text elsewhere in the string.
// Returns byte offsets rather than the matched text so callers can splice at
// the match's actual position instead of searching for the text elsewhere in
// the string.
func FindStringMatchIndex(pattern, text string, index int) (start, end int, ok bool) {
re, err := GetCompiledRegex(pattern)
if err != nil {
+2 -4
View File
@@ -31,7 +31,6 @@ func (m *NoBatteryError) Error() string {
return "no battery"
}
// State type enumerates possible battery states.
type State int
var states = [...]string{
@@ -47,9 +46,8 @@ func (s State) String() string {
return states[s]
}
// Possible state values.
// Unknown can mean either controller returned unknown, or
// not able to retrieve state due to some error.
// Unknown can mean either controller returned unknown, or not able to
// retrieve state due to some error.
const (
Unknown State = iota
Empty
@@ -6,7 +6,6 @@ import (
"math"
)
// battery type represents a single battery entry information.
type battery struct {
// Current battery state.
State State
@@ -37,10 +36,6 @@ func mapMostLogicalState(currentState, newState State) State {
return newState
}
// Get returns information about all batteries in the system.
//
// If error != nil, it will be either ErrFatal or Errors.
// If error is of type Errors, it is guaranteed that length of both returned slices is the same and that i-th error corresponds with i-th battery structure.
func Get() (*Info, error) {
parseBatteryInfo := func(batteries []*battery) *Info {
var info Info
+2 -5
View File
@@ -10,15 +10,12 @@ import (
runjobs "github.com/jandedobbeleer/oh-my-posh/src/runtime/jobs"
)
// Run executes a command while ensuring the OS process is started in its own
// process group; the started process is recorded so callers can request a
// cleanup (KillGoroutineChildren) if they decide to abort waiting for the
// goroutine that spawned it.
// Starts the process in its own process group and records it so callers can request
// cleanup via KillGoroutineChildren if they abort waiting for the spawning goroutine.
func Run(command string, args ...string) (string, error) {
return RunWithEnv(command, nil, args...)
}
// RunWithEnv executes a command with additional environment variables.
func RunWithEnv(command string, envs []string, args ...string) (string, error) {
cmd := exec.CommandContext(context.Background(), command, args...)
if len(envs) > 0 {
+1 -3
View File
@@ -23,9 +23,7 @@ const (
PRIMARY = "primary"
)
// MediaInfo holds a single media session read from the OS media-transport
// layer (e.g. Windows System Media Transport Controls). It is player-agnostic:
// the SMTC mechanism can surface any app that publishes a session.
// Player-agnostic: the OS media-transport layer (e.g. Windows SMTC) can surface any app that publishes a session.
type MediaInfo struct {
// Status is the lowercased playback status: playing/paused/stopped/closed/opened/changing.
Status string
+1 -2
View File
@@ -7,8 +7,7 @@ import (
"time"
)
// IsConnected checks if we can connect to ohmyposh within 200ms.
// Exposed as a variable so it can be replaced in tests.
// Exposed as a var so it can be replaced in tests.
var IsConnected = func() bool {
timeout := 200 * time.Millisecond
dialer := &net.Dialer{
+1 -3
View File
@@ -6,9 +6,7 @@ import (
"strings"
)
// CurrentGID returns the current goroutine's id. We expose this here so
// callers can register PIDs without parsing runtime.Stack in multiple
// places.
// Exposed here so callers can register PIDs without parsing runtime.Stack in multiple places.
func CurrentGID() uint64 {
buf := make([]byte, 64)
n := runtime.Stack(buf, false)
+3 -10
View File
@@ -18,19 +18,15 @@ var (
func CreateJobForGoroutine(_ string) error { return nil }
func AssignPidToGoroutineJob(_ int) error { return nil }
// CloseGoroutineJob is a no-op on non-Windows platforms; there is no Job
// object to release. It exists so callers (e.g. config.Segment.Execute) can
// unconditionally defer the close without platform-specific branching.
// No-op on non-Windows platforms, since there is no Job object to release. Exists so callers
// (e.g. config.Segment.Execute) can unconditionally defer the close without platform-specific branching.
func CloseGoroutineJob() {}
// setProcessGroup ensures the child process runs in its own process group so
// it can be killed with a group kill (negative pid).
// Runs the child in its own process group so it can be killed with a group kill (negative pid).
func SetProcessGroup(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
}
// registerProcessWithGID keeps track of a started child process for the
// given goroutine id.
func RegisterProcess(pid int) {
gid := CurrentGID()
processesMu.Lock()
@@ -55,9 +51,6 @@ func UnregisterProcess(pid int) {
processesMu.Unlock()
}
// KillGoroutineChildren attempts to kill all child processes started by the
// goroutine identified by gid using process groups (PGID). This mirrors the
// previous behavior performed in runtime/cmd.
func KillGoroutineChildren(gid uint64) error {
processesMu.Lock()
pidsMap, ok := processes[gid]
+2 -9
View File
@@ -24,9 +24,7 @@ var (
processes = map[uint64]map[int]struct{}{}
)
// CreateJobForGoroutine creates a Job object for gid and sets the
// JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag so closing/terminating the job
// kills all assigned processes.
// Sets JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so closing/terminating the job kills all assigned processes.
func CreateJobForGoroutine(label string) error {
gid := CurrentGID()
defer log.Trace(time.Now(), fmt.Sprintf("creating job for goroutine(%s): %d", label, gid))
@@ -58,8 +56,6 @@ func CreateJobForGoroutine(label string) error {
return nil
}
// registerProcessWithGID keeps track of a started child process for the
// given goroutine id and attempts to assign it to the Job object if present.
func RegisterProcess(pid int) {
gid := CurrentGID()
processesMu.Lock()
@@ -161,8 +157,6 @@ func CloseGoroutineJob() {
log.Debugf("closed job object for goroutine: %d", gid)
}
// KillGoroutineChildren will first try to terminate a Job if present, and
// otherwise will fall back to taskkill for each recorded pid.
func KillGoroutineChildren(gid uint64) error {
// if Job exists, prefer terminating the Job
jobsMu.Lock()
@@ -220,8 +214,7 @@ func KillGoroutineChildren(gid uint64) error {
return nil
}
// setProcessGroup ensures the child process runs in its own process group
// (CREATE_NEW_PROCESS_GROUP) so it can be terminated as a group.
// Uses CREATE_NEW_PROCESS_GROUP so the child can be terminated as a group.
func SetProcessGroup(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP}
}
+5 -7
View File
@@ -4,10 +4,9 @@ package jobs
import "testing"
// TestCloseGoroutineJob verifies that CloseGoroutineJob removes both the job
// and its recorded pids for the current goroutine, closes the underlying
// handle exactly once, and is a safe no-op if called again (e.g. because
// KillGoroutineChildren already won the race and closed it first).
// Removes both the job and its recorded pids for the current goroutine, closes the underlying
// handle exactly once, and is safe to call again (e.g. if KillGoroutineChildren already won the
// race and closed it first).
func TestCloseGoroutineJob(t *testing.T) {
if err := CreateJobForGoroutine("test"); err != nil {
t.Fatalf("CreateJobForGoroutine returned error: %v", err)
@@ -50,9 +49,8 @@ func TestCloseGoroutineJob(t *testing.T) {
CloseGoroutineJob()
}
// TestCloseGoroutineJobNoJob verifies calling CloseGoroutineJob without a
// prior CreateJobForGoroutine call (e.g. a segment with no timeout) is a
// harmless no-op.
// Calling CloseGoroutineJob without a prior CreateJobForGoroutine call (e.g. a segment with no
// timeout) must be a harmless no-op.
func TestCloseGoroutineJobNoJob(t *testing.T) {
CloseGoroutineJob()
}
+2 -3
View File
@@ -10,9 +10,8 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/text"
)
// Base returns the last element of path.
// Trailing path separators are removed before extracting the last element.
// If the path consists entirely of separators, Base returns a single separator.
// Trailing separators are removed before extracting the last element; if the path consists
// entirely of separators, a single separator is returned.
func Base(input string) string {
volumeName := filepath.VolumeName(input)
// Strip trailing slashes.
+2 -7
View File
@@ -122,8 +122,7 @@ var (
procRtlMoveMemory = kernel32.NewProc("RtlMoveMemory")
)
// hstring is a WinRT HSTRING handle. Pointer-sized opaque value must be
// freed with WindowsDeleteString once the consumer is done with it.
// Pointer-sized opaque value; must be freed with WindowsDeleteString once the consumer is done with it.
type hstring uintptr
func newHString(s string) (hstring, error) {
@@ -170,8 +169,6 @@ func (hs hstring) Close() {
_, _, _ = procWindowsDeleteString.Call(uintptr(hs))
}
// comCall invokes vtable[idx] on the COM object at ptr, with `this` and the
// given arguments.
func comCall(ptr unsafe.Pointer, idx uintptr, args ...uintptr) uintptr {
fn := (*comObj)(ptr).vtable.methods[idx]
all := make([]uintptr, 0, 1+len(args))
@@ -188,9 +185,7 @@ func comRelease(ptr unsafe.Pointer) {
comCall(ptr, iunknownRelease)
}
// awaitAsync polls IAsyncInfo::Status until the async operation reaches a
// terminal state. We poll instead of registering a Completed handler to
// avoid implementing a COM callback object in Go.
// Polls instead of registering a Completed handler, to avoid implementing a COM callback object in Go.
func awaitAsync(asyncOp unsafe.Pointer) error {
var asyncInfo unsafe.Pointer
hr := comCall(asyncOp, iunknownQueryInterface,
-2
View File
@@ -121,8 +121,6 @@ func (term *Terminal) Platform() string {
// The last part of the path is the key to retrieve.
//
// If the path ends in "\", the "(Default)" key in that path is retrieved.
//
// Returns a variant type if successful; nil and an error if not.
func (term *Terminal) WindowsRegistryKeyValue(input string) (*WindowsRegistryValue, error) {
defer log.Trace(time.Now(), input)
-4
View File
@@ -16,7 +16,6 @@ import (
// win32 specific code
// win32 dll load and function definitions
var (
user32 = syscall.NewLazyDLL("user32.dll")
procEnumWindows = user32.NewProc("EnumWindows")
@@ -30,7 +29,6 @@ var (
hGetIfTable2 = iphlpapi.NewProc("GetIfTable2")
)
// enumWindows call enumWindows from user32 and returns all active windows
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows
func enumWindows(enumFunc, lparam uintptr) (err error) {
r1, _, e1 := syscall.SyscallN(procEnumWindows.Addr(), enumFunc, lparam, 0)
@@ -44,7 +42,6 @@ func enumWindows(enumFunc, lparam uintptr) (err error) {
return
}
// getWindowText returns the title and text of a window from a window handle
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtextw
func getWindowText(hwnd syscall.Handle, str *uint16, maxCount int32) (length int32, err error) {
r0, _, e1 := syscall.SyscallN(procGetWindowTextW.Addr(), uintptr(hwnd), uintptr(unsafe.Pointer(str)), uintptr(maxCount))
@@ -73,7 +70,6 @@ func getWindowFileName(handle syscall.Handle) (string, error) {
return strings.ToLower(filename), nil
}
// GetWindowTitle searches for a window attached to the pid
func queryWindowTitles(processName, windowTitleRegex string) (string, error) {
var title string
// callback for EnumWindows
+3 -9
View File
@@ -14,16 +14,11 @@ import (
type Aws struct {
Base
// Settings holds every key/value pair from the active profile in the AWS shared
// config and credentials files. Credential-file entries take precedence over
// config-file entries for the same key, mirroring the AWS SDK's resolution order.
// Templates can read any AWS-recognized setting via {{ .Settings.<key> }}, e.g.
// {{ .Settings.role_arn }} or {{ .Settings.sso_role_name }}.
// Credential-file entries take precedence over config-file entries for the
// same key, mirroring the AWS SDK's resolution order.
Settings map[string]string
// SSOSession holds the resolved [sso-session <name>] section keys when the
// active profile references one via the sso_session key. Use as
// {{ .SSOSession.sso_start_url }}, etc.
// Populated only when the active profile references a session via the sso_session key.
SSOSession map[string]string
Profile string
@@ -35,7 +30,6 @@ type Aws struct {
const (
defaultStr = "default"
// AWS shared config keys we promote to convenience fields.
awsKeyRegion = "region"
awsKeyAccessKeyID = "aws_access_key_id"
awsKeyAccountID = "aws_account_id"
+2 -6
View File
@@ -13,13 +13,9 @@ type Battery struct {
}
const (
// ChargingIcon to display when charging
ChargingIcon options.Option = "charging_icon"
// DischargingIcon o display when discharging
ChargingIcon options.Option = "charging_icon"
DischargingIcon options.Option = "discharging_icon"
// ChargedIcon to display when fully charged
ChargedIcon options.Option = "charged_icon"
// NotChargingIcon to display when on AC power
ChargedIcon options.Option = "charged_icon"
NotChargingIcon options.Option = "not_charging_icon"
)
-1
View File
@@ -8,7 +8,6 @@ type Bazel struct {
}
const (
// Bazel's icon
Icon options.Option = "icon"
)
-2
View File
@@ -13,7 +13,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/segments/options"
)
// segment struct, makes templating easier
type Brewfather struct {
Base
@@ -257,7 +256,6 @@ func (bf *Brewfather) getResult() (*Batch, error) {
return &batch, nil
}
// Unit conversion functions available to template.
func (bf *Brewfather) DegCToF(degreesC float64) float64 {
return math.Round(10*((degreesC*1.8)+32)) / 10 // 1 decimal place
}
+1 -1
View File
@@ -21,7 +21,7 @@ const (
var (
TimeNow = time.Now()
// Create a fake timeline for the fake json, all in Unix milliseconds, to be used in all fake json responses
// All timestamps are in Unix milliseconds, matching what the fake JSON responses use.
FakeBrewDate = TimeNow.Add(-time.Hour * 24 * 20)
FakeFermentationStartDate = FakeBrewDate.Add(time.Hour * 24) // 1 day after brew date = 19 days ago
FakeReading1Date = FakeFermentationStartDate.Add(time.Minute * 35) // first reading 35 minutes
+8 -42
View File
@@ -11,7 +11,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/text"
)
// Claude segment displays Claude Code session information
type Claude struct {
Base
markedChar string
@@ -19,7 +18,6 @@ type Claude struct {
ClaudeData
}
// ClaudeData represents the parsed Claude JSON data
type ClaudeData struct {
Effort *ClaudeEffort `json:"effort"`
Worktree *ClaudeWorktree `json:"worktree"`
@@ -43,13 +41,12 @@ type ClaudeData struct {
FastMode bool `json:"fast_mode"`
}
// AIModel represents the AI model information shared across AI CLI segments.
// Shared across AI CLI segments (e.g. copilot_cli).
type AIModel struct {
ID string `json:"id"`
DisplayName string `json:"display_name"`
}
// ClaudeWorkspace represents workspace directory information
type ClaudeWorkspace struct {
Repo *ClaudeRepo `json:"repo"`
CurrentDir string `json:"current_dir"`
@@ -58,51 +55,44 @@ type ClaudeWorkspace struct {
AddedDirs []string `json:"added_dirs"`
}
// ClaudeRepo represents the repository identity parsed from the origin remote.
// Parsed from the origin remote.
type ClaudeRepo struct {
Host string `json:"host"`
Owner string `json:"owner"`
Name string `json:"name"`
}
// ClaudeOutputStyle represents the current output style.
// Nil when the statusline payload does not report an output style.
type ClaudeOutputStyle struct {
Name string `json:"name"`
}
// ClaudeEffort represents reasoning effort information for the current session.
// Nil when the active model does not support reasoning effort.
type ClaudeEffort struct {
Level string `json:"level"`
}
// ClaudeThinking represents extended thinking state for the current session.
// Nil when the statusline payload does not report thinking state.
type ClaudeThinking struct {
Enabled bool `json:"enabled"`
}
// ClaudeVim represents vim mode state.
// Nil when vim mode is disabled.
type ClaudeVim struct {
Mode string `json:"mode"`
}
// ClaudeAgent represents the active agent.
// Nil when no agent is active.
type ClaudeAgent struct {
Name string `json:"name"`
}
// ClaudePR represents the open pull request for the current branch.
type ClaudePR struct {
Number json.Number `json:"number"`
URL string `json:"url"`
ReviewState string `json:"review_state"`
}
// ClaudeWorktree represents Claude Code --worktree session information.
// Nil when the session is not running inside a Claude Code worktree.
type ClaudeWorktree struct {
Name string `json:"name"`
@@ -112,7 +102,7 @@ type ClaudeWorktree struct {
OriginalBranch string `json:"original_branch"`
}
// DurationMS is a duration in milliseconds that formats as "Xm Ys".
// Formats as "Xm Ys" (see String()).
type DurationMS int64
func (d DurationMS) String() string {
@@ -122,7 +112,6 @@ func (d DurationMS) String() string {
return fmt.Sprintf("%dm %ds", minutes, seconds)
}
// ClaudeCost represents cost and duration information
type ClaudeCost struct {
TotalCostUSD float64 `json:"total_cost_usd"`
TotalDurationMS DurationMS `json:"total_duration_ms"`
@@ -131,19 +120,16 @@ type ClaudeCost struct {
TotalLinesRemoved int `json:"total_lines_removed"`
}
// ClaudeRateLimitWindow represents a single rate limit time window.
type ClaudeRateLimitWindow struct {
UsedPercentage *float64 `json:"used_percentage"`
ResetsAt *int64 `json:"resets_at"`
}
// ClaudeRateLimits represents rate limit information across time windows.
type ClaudeRateLimits struct {
FiveHour *ClaudeRateLimitWindow `json:"five_hour"`
SevenDay *ClaudeRateLimitWindow `json:"seven_day"`
}
// ClaudeContextWindow represents token usage information
type ClaudeContextWindow struct {
UsedPercentage *int `json:"used_percentage"`
RemainingPercentage *int `json:"remaining_percentage"`
@@ -153,7 +139,7 @@ type ClaudeContextWindow struct {
ContextWindowSize int `json:"context_window_size"`
}
// ClaudeCurrentUsage represents current context window usage from the last API call
// Reflects the last API call, not a cumulative total.
type ClaudeCurrentUsage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
@@ -169,7 +155,6 @@ const (
gaugeUnmarkedChar options.Option = "gauge_unmarked_char"
)
// formatTokenCount formats a token count as a human-readable string ("1.2K", "3.4M", or raw).
func formatTokenCount(n int) string {
if n < int(thousand) {
return fmt.Sprintf("%d", n)
@@ -208,7 +193,6 @@ func (c *Claude) Enabled() bool {
return true
}
// TokenUsagePercent returns the percentage of context window used.
// Uses pre-calculated UsedPercentage when available (resets on compact/clear),
// falls back to calculating from CurrentUsage, then to total tokens for backwards compatibility.
func (c *Claude) TokenUsagePercent() text.Percentage {
@@ -256,27 +240,24 @@ func (c *Claude) TokenUsagePercent() text.Percentage {
return text.Percentage(roundedPercent)
}
// TokenGauge returns a 5-block gauge showing remaining context window capacity using the configured characters.
// Shows remaining capacity; see TokenGaugeUsed for the used view.
func (c *Claude) TokenGauge() string {
return c.TokenUsagePercent().GaugeWith(c.markedChar, c.unmarkedChar)
}
// TokenGaugeUsed returns a 5-block gauge showing used context window capacity using the configured characters.
// Shows used capacity, unlike TokenGauge which shows remaining.
func (c *Claude) TokenGaugeUsed() string {
return c.TokenUsagePercent().GaugeUsedWith(c.markedChar, c.unmarkedChar)
}
// FiveHourGauge returns a 5-block gauge showing 5-hour rate limit usage using the configured characters.
func (c *Claude) FiveHourGauge() string {
return c.FiveHourUsage().GaugeUsedWith(c.markedChar, c.unmarkedChar)
}
// SevenDayGauge returns a 5-block gauge showing 7-day rate limit usage using the configured characters.
func (c *Claude) SevenDayGauge() string {
return c.SevenDayUsage().GaugeUsedWith(c.markedChar, c.unmarkedChar)
}
// FormattedCost returns the cost formatted as a currency string
func (c *Claude) FormattedCost() string {
if c.Cost.TotalCostUSD < 0.01 {
return fmt.Sprintf("$%.4f", c.Cost.TotalCostUSD)
@@ -285,17 +266,14 @@ func (c *Claude) FormattedCost() string {
return fmt.Sprintf("$%.2f", c.Cost.TotalCostUSD)
}
// FormattedDuration returns total session duration as "Xm Ys".
func (c *Claude) FormattedDuration() string {
return c.Cost.TotalDurationMS.String()
}
// FormattedAPIDuration returns API wait time as "Xm Ys".
func (c *Claude) FormattedAPIDuration() string {
return c.Cost.TotalAPIDurationMS.String()
}
// rateLimitPercentage extracts a percentage from a rate limit window with nil-safety.
func rateLimitPercentage(limits *ClaudeRateLimits, window func(*ClaudeRateLimits) *ClaudeRateLimitWindow) text.Percentage {
if limits == nil {
return 0
@@ -314,21 +292,18 @@ func rateLimitPercentage(limits *ClaudeRateLimits, window func(*ClaudeRateLimits
return text.Percentage(percent)
}
// FiveHourUsage returns the 5-hour rolling window rate limit usage as a Percentage.
func (c *Claude) FiveHourUsage() text.Percentage {
return rateLimitPercentage(c.RateLimits, func(r *ClaudeRateLimits) *ClaudeRateLimitWindow {
return r.FiveHour
})
}
// SevenDayUsage returns the 7-day window rate limit usage as a Percentage.
func (c *Claude) SevenDayUsage() text.Percentage {
return rateLimitPercentage(c.RateLimits, func(r *ClaudeRateLimits) *ClaudeRateLimitWindow {
return r.SevenDay
})
}
// rateLimitResetsAt extracts the reset time from a rate limit window with nil-safety.
func rateLimitResetsAt(limits *ClaudeRateLimits, window func(*ClaudeRateLimits) *ClaudeRateLimitWindow) time.Time {
if limits == nil || window == nil {
return time.Time{}
@@ -342,22 +317,19 @@ func rateLimitResetsAt(limits *ClaudeRateLimits, window func(*ClaudeRateLimits)
return time.Unix(*w.ResetsAt, 0)
}
// FiveHourResetsAt returns the reset time for the 5-hour rolling window, or zero if unavailable.
func (c *Claude) FiveHourResetsAt() time.Time {
return rateLimitResetsAt(c.RateLimits, func(r *ClaudeRateLimits) *ClaudeRateLimitWindow {
return r.FiveHour
})
}
// SevenDayResetsAt returns the reset time for the 7-day rolling window, or zero if unavailable.
func (c *Claude) SevenDayResetsAt() time.Time {
return rateLimitResetsAt(c.RateLimits, func(r *ClaudeRateLimits) *ClaudeRateLimitWindow {
return r.SevenDay
})
}
// rateLimitResetsIn returns the signed duration until a rate limit window resets.
// Returns 0 when data is unavailable, a negative value when the window already reset, and a positive value otherwise.
// Returns 0 when data is unavailable, negative when the window already reset, positive otherwise.
func rateLimitResetsIn(limits *ClaudeRateLimits, window func(*ClaudeRateLimits) *ClaudeRateLimitWindow) time.Duration {
t := rateLimitResetsAt(limits, window)
if t.IsZero() {
@@ -367,25 +339,19 @@ func rateLimitResetsIn(limits *ClaudeRateLimits, window func(*ClaudeRateLimits)
return time.Until(t)
}
// FiveHourResetsIn returns the signed duration until the 5-hour rolling window resets.
// Returns 0 when unavailable, negative when the window already reset.
func (c *Claude) FiveHourResetsIn() time.Duration {
return rateLimitResetsIn(c.RateLimits, func(r *ClaudeRateLimits) *ClaudeRateLimitWindow {
return r.FiveHour
})
}
// SevenDayResetsIn returns the signed duration until the 7-day rolling window resets.
// Returns 0 when unavailable, negative when the window already reset.
func (c *Claude) SevenDayResetsIn() time.Duration {
return rateLimitResetsIn(c.RateLimits, func(r *ClaudeRateLimits) *ClaudeRateLimitWindow {
return r.SevenDay
})
}
// FormattedTokens returns a human-readable string of current context tokens.
// Uses CurrentUsage (which represents actual context and resets on compact/clear)
// with fallback to total tokens for backwards compatibility.
// Uses CurrentUsage (actual context, resets on compact/clear), falling back to total tokens for backwards compatibility.
func (c *Claude) FormattedTokens() string {
var currentTokens int
-5
View File
@@ -11,7 +11,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/text"
)
// CopilotUsage represents usage statistics for a specific quota type.
type CopilotUsage struct {
Used int `json:"used"`
Limit int `json:"limit"`
@@ -20,7 +19,6 @@ type CopilotUsage struct {
Unlimited bool `json:"unlimited"`
}
// Copilot displays GitHub Copilot usage statistics.
type Copilot struct {
Base
BillingCycleEnd string `json:"billing_cycle_end"`
@@ -33,21 +31,18 @@ const (
copilotAPIURL = "https://api.github.com/copilot_internal/user"
)
// copilotQuotaSnapshot represents a single quota type.
type copilotQuotaSnapshot struct {
Entitlement int `json:"entitlement"`
Remaining int `json:"remaining"`
Unlimited bool `json:"unlimited"`
}
// copilotQuotaSnapshots represents the quota snapshots structure.
type copilotQuotaSnapshots struct {
PremiumInteractions copilotQuotaSnapshot `json:"premium_interactions"`
Completions copilotQuotaSnapshot `json:"completions"`
Chat copilotQuotaSnapshot `json:"chat"`
}
// copilotAPIResponse represents the API response structure.
type copilotAPIResponse struct {
QuotaSnapshots *copilotQuotaSnapshots `json:"quota_snapshots"`
QuotaResetDate string `json:"quota_reset_date"`
+2 -14
View File
@@ -6,7 +6,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/text"
)
// CopilotCLI segment displays GitHub Copilot CLI session information
type CopilotCLI struct {
Base
markedChar string
@@ -14,7 +13,6 @@ type CopilotCLI struct {
CopilotCLIData
}
// CopilotCLIData represents the parsed Copilot CLI JSON data
type CopilotCLIData struct {
Model AIModel `json:"model"`
Workspace CopilotCLIWorkspace `json:"workspace"`
@@ -29,17 +27,14 @@ type CopilotCLIData struct {
Remote CopilotCLIRemote `json:"remote"`
}
// CopilotCLIWorkspace represents workspace directory information
type CopilotCLIWorkspace struct {
CurrentDir string `json:"current_dir"`
}
// CopilotCLIRemote represents remote connection state
type CopilotCLIRemote struct {
Connected bool `json:"connected"`
}
// CopilotCLICost represents cost and duration information
type CopilotCLICost struct {
TotalDurationMS DurationMS `json:"total_duration_ms"`
TotalAPIDurationMS DurationMS `json:"total_api_duration_ms"`
@@ -48,7 +43,6 @@ type CopilotCLICost struct {
TotalPremiumRequests int `json:"total_premium_requests"`
}
// CopilotCLIContextWindow represents token usage information
type CopilotCLIContextWindow struct {
ContextWindowSize *int `json:"context_window_size"`
UsedPercentage *float64 `json:"used_percentage"`
@@ -89,7 +83,6 @@ func (c *CopilotCLI) Enabled() bool {
return true
}
// TokenUsagePercent returns the percentage of context window used.
// Uses pre-calculated UsedPercentage when available; falls back to computing
// from CurrentContextTokens / ContextWindowSize; returns 0 when unavailable.
func (c *CopilotCLI) TokenUsagePercent() text.Percentage {
@@ -129,17 +122,16 @@ func (c *CopilotCLI) TokenUsagePercent() text.Percentage {
return text.Percentage(rounded)
}
// TokenGauge returns a 5-block gauge showing remaining context window capacity using the configured characters.
// Shows remaining capacity; see TokenGaugeUsed for the used view.
func (c *CopilotCLI) TokenGauge() string {
return c.TokenUsagePercent().GaugeWith(c.markedChar, c.unmarkedChar)
}
// TokenGaugeUsed returns a 5-block gauge showing used context window capacity using the configured characters.
// Shows used capacity, unlike TokenGauge which shows remaining.
func (c *CopilotCLI) TokenGaugeUsed() string {
return c.TokenUsagePercent().GaugeUsedWith(c.markedChar, c.unmarkedChar)
}
// FormattedTokens returns a human-readable string of current context tokens.
func (c *CopilotCLI) FormattedTokens() string {
tokens := c.ContextWindow.CurrentContextTokens
if tokens <= 0 {
@@ -149,17 +141,14 @@ func (c *CopilotCLI) FormattedTokens() string {
return formatTokenCount(tokens)
}
// FormattedDuration returns total session duration as "Xm Ys".
func (c *CopilotCLI) FormattedDuration() string {
return c.Cost.TotalDurationMS.String()
}
// FormattedAPIDuration returns API wait time as "Xm Ys".
func (c *CopilotCLI) FormattedAPIDuration() string {
return c.Cost.TotalAPIDurationMS.String()
}
// RemainingPercent returns the percentage of context window remaining (0-100).
func (c *CopilotCLI) RemainingPercent() text.Percentage {
if c.ContextWindow.RemainingPercentage != nil {
v := *c.ContextWindow.RemainingPercentage
@@ -183,7 +172,6 @@ func (c *CopilotCLI) RemainingPercent() text.Percentage {
return text.Percentage(remaining)
}
// RemainingTokensCount returns the number of remaining context window tokens.
func (c *CopilotCLI) RemainingTokensCount() int {
if c.ContextWindow.RemainingTokens != nil {
return *c.ContextWindow.RemainingTokens
+2 -4
View File
@@ -12,11 +12,9 @@ import (
)
const (
// FetchContext is the property used to fetch the current docker context
FetchContext options.Option = "fetch_context"
// DockerCommand is the property used to specify the docker command to use
FetchContext options.Option = "fetch_context"
DockerCommand options.Option = "docker_command"
// Filter is the property used to specify a filter to apply to docker ps results in environment mode, see https://docs.docker.com/reference/cli/docker/container/ls/#filter
// Filter applies to docker ps results in environment mode, see https://docs.docker.com/reference/cli/docker/container/ls/#filter
Filter options.Option = "filter"
)
-1
View File
@@ -14,7 +14,6 @@ type globalJSON struct {
}
const (
// FetchSDKVersion fetches the SDK version in global.json
FetchSDKVersion options.Option = "fetch_sdk_version"
)
+1 -2
View File
@@ -2,7 +2,6 @@ package segments
import "encoding/json"
// DvcStatus represents the status of a DVC repository
type DvcStatus struct {
ScmStatus
}
@@ -65,7 +64,7 @@ func (d *Dvc) CacheKey() (string, bool) {
return dir.Path, true
}
// setStatus parses the output of `dvc status --json`, which has the shape:
// `dvc status --json` has the shape:
//
// {"<stage>": [{"changed outs": {"<file>": "<state>"}}, {"changed deps": {"<file>": "<state>"}}], ...}
//
+11 -13
View File
@@ -16,33 +16,31 @@ type Executiontime struct {
Ms int64
}
// DurationStyle how to display the time
type DurationStyle string
const (
// ThresholdProperty represents minimum duration (milliseconds) required to enable this segment
// Minimum duration in milliseconds required to enable this segment
ThresholdProperty options.Option = "threshold"
// Austin milliseconds short
// Milliseconds short
Austin DurationStyle = "austin"
// Roundrock milliseconds long
// Milliseconds long
Roundrock DurationStyle = "roundrock"
// Dallas milliseconds full
// Milliseconds full
Dallas DurationStyle = "dallas"
// Galveston hour
// Hour
Galveston DurationStyle = "galveston"
// Galveston hour
// Hour
GalvestonMs DurationStyle = "galvestonms"
// Houston hour and milliseconds
// Hour and milliseconds
Houston DurationStyle = "houston"
// Amarillo seconds
// Seconds
Amarillo DurationStyle = "amarillo"
// Round will round the output of the format
Round DurationStyle = "round"
Round DurationStyle = "round"
// Always 7 character width
Lucky7 = "lucky7"
// ISO8601 ISO 8601 duration format (seconds)
// ISO 8601 duration format (seconds)
ISO8601 DurationStyle = "iso8601"
// ISO8601Ms ISO 8601 duration format with milliseconds
// ISO 8601 duration format with milliseconds
ISO8601Ms DurationStyle = "iso8601ms"
second = 1000
-1
View File
@@ -2,7 +2,6 @@ package segments
import "strings"
// FossilStatus represents part of the status of a Svn repository
type FossilStatus struct {
ScmStatus
}
+28 -57
View File
@@ -38,7 +38,6 @@ type User struct {
Email string
}
// GitStatus represents part of the status of a git repository
type GitStatus struct {
ScmStatus
}
@@ -61,66 +60,38 @@ func (s *GitStatus) add(code string) {
}
const (
// FetchStatus fetches the status of the repository
FetchStatus options.Option = "fetch_status"
// FetchPushStatus fetches the push-remote status
FetchPushStatus options.Option = "fetch_push_status"
// IgnoreStatus allows to ignore certain repo's for status information
IgnoreStatus options.Option = "ignore_status"
// FetchUpstreamIcon fetches the upstream icon
FetchStatus options.Option = "fetch_status"
FetchPushStatus options.Option = "fetch_push_status"
IgnoreStatus options.Option = "ignore_status"
FetchUpstreamIcon options.Option = "fetch_upstream_icon"
// FetchBareInfo fetches the bare repo status
FetchBareInfo options.Option = "fetch_bare_info"
// FetchUser fetches the current user for the repo
FetchUser options.Option = "fetch_user"
// UntrackedModes list the optional untracked files mode per repo
UntrackedModes options.Option = "untracked_modes"
// IgnoreSubmodules list the optional ignore-submodules mode per repo
IgnoreSubmodules options.Option = "ignore_submodules"
// MappedBranches allows overriding certain branches with an icon/text
MappedBranches options.Option = "mapped_branches"
// DisableWithJJ disables the git segment when there's a .jj directory in the parent file path
FetchBareInfo options.Option = "fetch_bare_info"
FetchUser options.Option = "fetch_user"
UntrackedModes options.Option = "untracked_modes"
IgnoreSubmodules options.Option = "ignore_submodules"
MappedBranches options.Option = "mapped_branches"
// Disables the git segment when a .jj directory exists in the parent file path
DisableWithJJ options.Option = "disable_with_jj"
// BranchIcon the icon to use as branch indicator
BranchIcon options.Option = "branch_icon"
// BranchIdenticalIcon the icon to display when the remote and local branch are identical
BranchIcon options.Option = "branch_icon"
BranchIdenticalIcon options.Option = "branch_identical_icon"
// BranchAheadIcon the icon to display when the local branch is ahead of the remote
BranchAheadIcon options.Option = "branch_ahead_icon"
// BranchBehindIcon the icon to display when the local branch is behind the remote
BranchBehindIcon options.Option = "branch_behind_icon"
// BranchGoneIcon the icon to use when ther's no remote
BranchGoneIcon options.Option = "branch_gone_icon"
// RebaseIcon shows before the rebase context
RebaseIcon options.Option = "rebase_icon"
// CherryPickIcon shows before the cherry-pick context
CherryPickIcon options.Option = "cherry_pick_icon"
// RevertIcon shows before the revert context
RevertIcon options.Option = "revert_icon"
// CommitIcon shows before the detached context
CommitIcon options.Option = "commit_icon"
// NoCommitsIcon shows when there are no commits in the repo yet
NoCommitsIcon options.Option = "no_commits_icon"
// TagIcon shows before the tag context
TagIcon options.Option = "tag_icon"
// MergeIcon shows before the merge context
MergeIcon options.Option = "merge_icon"
// UpstreamIcons allows to add custom upstream icons
UpstreamIcons options.Option = "upstream_icons"
// GithubIcon shows when upstream is github
GithubIcon options.Option = "github_icon"
// BitbucketIcon shows when upstream is bitbucket
BitbucketIcon options.Option = "bitbucket_icon"
// AzureDevOpsIcon shows when upstream is azure devops
AzureDevOpsIcon options.Option = "azure_devops_icon"
// CodeCommit shows when upstream is aws codecommit
CodeCommit options.Option = "codecommit_icon"
// CodebergIcon shows when upstream is codeberg
CodebergIcon options.Option = "codeberg_icon"
// GitlabIcon shows when upstream is gitlab
GitlabIcon options.Option = "gitlab_icon"
// GitIcon shows when the upstream can't be identified
BranchAheadIcon options.Option = "branch_ahead_icon"
BranchBehindIcon options.Option = "branch_behind_icon"
BranchGoneIcon options.Option = "branch_gone_icon"
RebaseIcon options.Option = "rebase_icon"
CherryPickIcon options.Option = "cherry_pick_icon"
RevertIcon options.Option = "revert_icon"
CommitIcon options.Option = "commit_icon"
NoCommitsIcon options.Option = "no_commits_icon"
TagIcon options.Option = "tag_icon"
MergeIcon options.Option = "merge_icon"
UpstreamIcons options.Option = "upstream_icons"
GithubIcon options.Option = "github_icon"
BitbucketIcon options.Option = "bitbucket_icon"
AzureDevOpsIcon options.Option = "azure_devops_icon"
CodeCommit options.Option = "codecommit_icon"
CodebergIcon options.Option = "codeberg_icon"
GitlabIcon options.Option = "gitlab_icon"
// Fallback icon when the upstream host can't be identified
GitIcon options.Option = "git_icon"
DETACHED = "(detached)"
-1
View File
@@ -4,7 +4,6 @@ package segments
import "path/filepath"
// resolveGitPath resolves path relative to base.
func resolveGitPath(base, path string) string {
if filepath.IsAbs(path) {
return path
-1
View File
@@ -2,7 +2,6 @@ package segments
import "path/filepath"
// resolveGitPath resolves path relative to base.
func resolveGitPath(base, path string) string {
if path == "" {
return base
-4
View File
@@ -38,10 +38,6 @@ func (g *Golang) Enabled() bool {
return g.Language.Enabled()
}
// getVersion returns the version of the Go language
// It first checks if the go.mod file is present and if it is, it parses the file to get the version
// If the go.mod file is not present, it checks if the go.work file is present and if it is, it parses the file to get the version
// If neither file is present, it returns an empty string
func (g *Golang) getVersion() (string, error) {
if g.options.Bool(ParseModFile, false) {
return g.parseModFile()
-2
View File
@@ -13,8 +13,6 @@ import (
"github.com/stretchr/testify/assert"
)
// timeoutCapturingEnv wraps the mock environment to record the timeout argument
// passed to HTTPRequest, so tests can assert it flows through correctly.
type timeoutCapturingEnv struct {
*mock.Environment
capturedTimeout int
+11 -26
View File
@@ -118,25 +118,16 @@ type Language struct {
const (
// DisplayMode sets the display mode (always, when_in_context, never)
DisplayMode options.Option = "display_mode"
// DisplayModeAlways displays the segment always
DisplayModeAlways string = "always"
// DisplayModeFiles displays the segment when the current folder contains certain extensions
DisplayModeFiles string = "files"
// DisplayModeEnvironment displays the segment when the environment has a language's context
DisplayModeEnvironment string = "environment"
// DisplayModeContext displays the segment when the environment or files is active
DisplayModeContext string = "context"
// MissingCommandText sets the text to display when the command is not present in the system
MissingCommandText options.Option = "missing_command_text"
// HomeEnabled displays the segment in the HOME folder or not
HomeEnabled options.Option = "home_enabled"
// LanguageExtensions the list of extensions to validate
LanguageExtensions options.Option = "extensions"
// LanguageFolders the list of folders to validate
LanguageFolders options.Option = "folders"
// LanguageProjectFiles the list of project files to validate
LanguageProjectFiles options.Option = "project_files"
DisplayMode options.Option = "display_mode"
DisplayModeAlways string = "always"
DisplayModeFiles string = "files"
DisplayModeEnvironment string = "environment"
DisplayModeContext string = "context"
MissingCommandText options.Option = "missing_command_text"
HomeEnabled options.Option = "home_enabled"
LanguageExtensions options.Option = "extensions"
LanguageFolders options.Option = "folders"
LanguageProjectFiles options.Option = "project_files"
// Tooling allows enabling additional version fetching tools
Tooling options.Option = "tooling"
// Tools defines custom tools (executable, args, regex) for a configured language
@@ -218,10 +209,7 @@ func (l *Language) Enabled() bool {
return enabled
}
// loadTooling builds the commands list from the tooling map based on the tooling configuration.
// Users can override the default tooling via the Tooling option.
// This allows specifying which tools should be used to fetch versions
// (e.g., "uv" for Python to use UV package manager).
// Users can override the default tooling via the Tooling option (e.g. "uv" for Python to use the UV package manager).
func (l *Language) loadTooling() {
enabledTools := l.options.StringArray(Tooling, l.defaultTooling)
if len(enabledTools) == 0 {
@@ -253,8 +241,6 @@ func (l *Language) hasProjectFiles() bool {
return false
}
// InProjectDir reports whether the working directory is within a project
// matched by one of the segment's projectFiles.
func (l *Language) InProjectDir() bool {
return l.projectRoot != nil
}
@@ -263,7 +249,6 @@ func (l *Language) hasLanguageFolders() bool {
return slices.ContainsFunc(l.folders, l.env.HasFolder)
}
// setVersion parses the version string returned by the command
func (l *Language) setVersion() error {
var lastError error
-1
View File
@@ -20,7 +20,6 @@ type LastFM struct {
}
const (
// LastFM username
Username options.Option = "username"
)
+2 -13
View File
@@ -10,14 +10,12 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/segments/options"
)
// segment struct, makes templating easier
type Nba struct {
Base
NBAData
}
// NBA struct contains parsed API data that care about for the segment
type NBAData struct {
HomeTeam string
AwayTeam string
@@ -59,10 +57,8 @@ const (
NBADateFormat = "02/01/2006"
)
// Custom type for GameStatus
type GameStatus int
// Constants for GameStatus values
const (
Scheduled GameStatus = 1
InProgress GameStatus = 2
@@ -70,8 +66,7 @@ const (
NotFound GameStatus = 4
)
// Int() method for GameStatus to get its integer representation
// This is a helpful method if people want to come up with their own templates
// Lets users reference the numeric value in custom templates.
func (gs GameStatus) Int() int {
return int(gs)
}
@@ -151,7 +146,6 @@ func (nba *Nba) Enabled() bool {
return true
}
// parses through a set of games from the score endpoint and looks for props.team in away or home team
func (nba *Nba) findGameScoreByTeamTricode(games []Game, teamTricode string) (*Game, error) {
for _, game := range games {
if game.HomeTeam.TeamTricode == teamTricode || game.AwayTeam.TeamTricode == teamTricode {
@@ -162,7 +156,6 @@ func (nba *Nba) findGameScoreByTeamTricode(games []Game, teamTricode string) (*G
return nil, errors.New("no game score found for team")
}
// parses through a set of games from the schedule endpoint and looks for props.team in away or home team
func (nba *Nba) findGameSchedulebyTeamTricode(games []ScheduledGame, teamTricode string) (*ScheduledGame, error) {
for _, game := range games {
if game.VtAbbreviation == teamTricode || game.HtAbbreviation == teamTricode {
@@ -173,7 +166,6 @@ func (nba *Nba) findGameSchedulebyTeamTricode(games []ScheduledGame, teamTricode
return nil, errors.New("no scheduled game found for team")
}
// parses the time and date from the schedule endpoint into a UTC time
func (nba *Nba) parseTimetoUTC(timeEST, date string) string {
combinedTime := date + " " + timeEST
timeUTC, err := time.Parse("01/02/2006 03:04 PM", combinedTime)
@@ -184,7 +176,6 @@ func (nba *Nba) parseTimetoUTC(timeEST, date string) string {
return timeUTC.UTC().Format("2006-01-02T15:04:05Z")
}
// retrieves data from the score endpoint
func (nba *Nba) retrieveScoreData(teamName string, httpTimeout int) (*NBAData, error) {
body, err := nba.env.HTTPRequest(NBAScoreURL, nil, httpTimeout)
if err != nil {
@@ -218,7 +209,6 @@ func (nba *Nba) retrieveScoreData(teamName string, httpTimeout int) (*NBAData, e
}, nil
}
// Retrieves the data from the schedule endpoint
func (nba *Nba) retrieveScheduleData(teamName string, httpTimeout int) (*NBAData, error) {
// How many days into the future should we look for a game.
numDaysToSearch := nba.options.Int(DaysOffset, 8)
@@ -270,8 +260,7 @@ func (nba *Nba) retrieveScheduleData(teamName string, httpTimeout int) (*NBAData
return nil, errors.New("no scheduled game found for team within DaysOffset days")
}
// First try to get the data from the score endpoint, if that fails, try the schedule endpoint
// The score endpoint usually goes live within 12 hours of a game starting
// The score endpoint usually goes live within 12 hours of a game starting.
func (nba *Nba) getAvailableGameData(teamName string, httpTimeout int) (*NBAData, error) {
// Get the info from the score endpoint
data, err := nba.retrieveScoreData(teamName, httpTimeout)
-1
View File
@@ -17,7 +17,6 @@ func getTestData(file string) string {
return string(content)
}
// create Test segment for NBA segment
func TestNBASegment(t *testing.T) {
jsonScheduleData := getTestData("nba/schedule.json")
jsonScoreData := getTestData("nba/score.json")
+1 -4
View File
@@ -10,7 +10,6 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/segments/options"
)
// segment struct, makes templating easier
type Nightscout struct {
Base
@@ -32,7 +31,6 @@ const (
DoubleDownIcon options.Option = "doubledown_icon"
)
// NightscoutData struct contains the API data
type NightscoutData struct {
DateString time.Time `json:"dateString"`
SysTime time.Time `json:"sysTime"`
@@ -47,8 +45,7 @@ type NightscoutData struct {
Mills int64 `json:"mills"`
}
// UnmarshalJSON handles both integer and floating-point JSON numbers for the date field.
// Some Nightscout API providers (e.g. T1Pal) return the date as a float.
// Some Nightscout API providers (e.g. T1Pal) return the date field as a float instead of an integer.
func (n *NightscoutData) UnmarshalJSON(data []byte) error {
type Alias NightscoutData
aux := &struct {
+2 -3
View File
@@ -34,9 +34,8 @@ func (n *NixShell) DetectType() string {
}
}
// Hack to detect if we're in a `nix shell` (in contrast to a `nix-shell`).
// A better way to do this will be enabled by https://github.com/NixOS/nix/issues/6677
// so we check if the PATH contains a nix store.
// Hack to detect a `nix shell` (vs a `nix-shell`) by checking if PATH contains a nix store;
// a better way will be enabled by https://github.com/NixOS/nix/issues/6677.
func (n *NixShell) InNewNixShell() bool {
paths := filepath.SplitList(n.env.Getenv("PATH"))

Some files were not shown because too many files have changed in this diff Show More