perf(nushell): collapse git calls and disable language probing in prompt

This commit is contained in:
2026-06-14 21:54:02 +08:00
parent d7377a57c6
commit 8544a7ae7b
+223 -194
View File
@@ -142,14 +142,17 @@ def _pj_palette_default [] {
} }
} }
def _pj_first_version [text: string] { # Disabled along with the language-version probing. See the commented block
let parsed = ($text | parse -r '(?<ver>\d+(?:\.\d+)+)') # near `_pj_lang_segments` to restore.
if (($parsed | length) > 0) { #
($parsed | get ver.0) # def _pj_first_version [text: string] {
} else { # let parsed = ($text | parse -r '(?<ver>\d+(?:\.\d+)+)')
"" # if (($parsed | length) > 0) {
} # ($parsed | get ver.0)
} # } else {
# ""
# }
# }
def _pj_cmd_duration_ms [] { def _pj_cmd_duration_ms [] {
let raw = ($env.CMD_DURATION_MS? | default 0) let raw = ($env.CMD_DURATION_MS? | default 0)
@@ -206,25 +209,32 @@ def _pj_is_path_within [path: string, base: string] {
($path | str starts-with $"($base)/") ($path | str starts-with $"($base)/")
} }
def _pj_git_root_normalized [] { # Single `git rev-parse` call: returns whether we are inside a work tree and the
# normalized repo root (empty string when not in a repo).
def _pj_git_repo_info [] {
if (which ^git | is-empty) { if (which ^git | is-empty) {
return "" return { inside: false, root: "" }
} }
let top = (^git rev-parse --show-toplevel | complete) let res = (^git rev-parse --is-inside-work-tree --show-toplevel | complete)
if ($top.exit_code != 0) { if ($res.exit_code != 0) {
return "" return { inside: false, root: "" }
} }
let raw = (($top.stdout | str trim) | str replace -a "\\" "/") let parts = ($res.stdout | lines)
if ($raw == "/") { let inside_raw = ($parts | get 0? | default "")
"/" let root_raw = ($parts | get 1? | default "")
let root_norm = if ($root_raw == "") {
""
} else { } else {
($raw | str trim -r -c "/") let raw = (($root_raw | str trim) | str replace -a "\\" "/")
if ($raw == "/") { "/" } else { ($raw | str trim -r -c "/") }
} }
{ inside: (($inside_raw | str trim) == "true"), root: $root_norm }
} }
def _pj_truncate_pwd [pwd: string, length: int = 4] { def _pj_truncate_pwd [pwd: string, length: int = 4, repo: record = {inside: false, root: ""}] {
let home = (($nu.home-dir? | default ($nu.home-path? | default ($env.HOME? | default ""))) | into string) let home = (($nu.home-dir? | default ($nu.home-path? | default ($env.HOME? | default ""))) | into string)
let normalized_pwd_raw = (($pwd | into string) | str replace -a "\\" "/") let normalized_pwd_raw = (($pwd | into string) | str replace -a "\\" "/")
let normalized_home_raw = (($home | into string) | str replace -a "\\" "/") let normalized_home_raw = (($home | into string) | str replace -a "\\" "/")
@@ -247,8 +257,8 @@ def _pj_truncate_pwd [pwd: string, length: int = 4] {
(_pj_is_path_within $normalized_pwd $normalized_home) (_pj_is_path_within $normalized_pwd $normalized_home)
} }
let repo_root = (_pj_git_root_normalized) let repo_root = $repo.root
let in_repo = (_pj_is_path_within $normalized_pwd $repo_root) let in_repo = ($repo.inside and (_pj_is_path_within $normalized_pwd $repo_root))
if $in_repo { if $in_repo {
let repo_name = ($repo_root | path basename) let repo_name = ($repo_root | path basename)
@@ -319,87 +329,80 @@ def _pj_truncate_pwd [pwd: string, length: int = 4] {
} }
} }
def _pj_git_info [] { def _pj_git_info [repo: record] {
if (which ^git | is-empty) { let empty = {
return { branch: ""
branch: "" changed: 0
changed: 0 untracked: 0
untracked: 0 stashed: 0
stashed: 0 ahead: 0
ahead: 0 behind: 0
behind: 0 }
} # `repo.inside` can only be true when git exists, so no separate `which`
# check is needed here.
if (not $repo.inside) {
return $empty
} }
let repo_check = (^git rev-parse --is-inside-work-tree | complete) # One `git status` call carries: branch name, ahead/behind, stash count,
if ($repo_check.exit_code != 0 or (($repo_check.stdout | str trim) != "true")) { # and the changed/untracked entries. Previously this was 4 separate git
return { # invocations (rev-parse, branch --show-current, status, stash list).
branch: "" let status = (^git status --porcelain=2 --branch --show-stash | complete)
changed: 0
untracked: 0
stashed: 0
ahead: 0
behind: 0
}
}
let branch_result = (^git branch --show-current | complete)
let branch = ($branch_result.stdout | str trim)
let branch_text = if ($branch != "") {
$branch
} else {
let head_result = (^git rev-parse --short HEAD | complete)
if ($head_result.exit_code == 0) {
($head_result.stdout | str trim)
} else {
""
}
}
let status = (^git status --porcelain=2 --branch | complete)
if ($status.exit_code != 0) { if ($status.exit_code != 0) {
return { return $empty
branch: $branch_text
changed: 0
untracked: 0
stashed: 0
ahead: 0
behind: 0
}
} }
let lines = ($status.stdout | lines) let lines = ($status.stdout | lines)
let changed = ($lines | where {|l| ($l | str starts-with "1 ") or ($l | str starts-with "2 ") or ($l | str starts-with "u ") } | length) let changed = ($lines | where {|l| ($l | str starts-with "1 ") or ($l | str starts-with "2 ") or ($l | str starts-with "u ") } | length)
let untracked = ($lines | where {|l| $l | str starts-with "? " } | length) let untracked = ($lines | where {|l| $l | str starts-with "? " } | length)
let stashed = ((^git stash list | complete).stdout | lines | length)
let stashed = ($lines | where {|l| $l | str starts-with "# stash " } | get 0? | default "")
let stashed_n = if ($stashed == "") {
0
} else {
(($stashed | parse -r '# stash (?<n>\d+)' | get 0? | default {n: "0"} | get n) | into int)
}
let head_line = ($lines | where {|l| $l | str starts-with "# branch.head " } | get 0? | default "")
let head_name = if ($head_line == "") {
""
} else {
($head_line | str replace "# branch.head " "" | str trim)
}
# Detached HEAD: fall back to the short oid (first 7 chars), matching the
# previous `git rev-parse --short HEAD` behaviour.
let branch_text = if ($head_name == "(detached)") {
let oid_line = ($lines | where {|l| $l | str starts-with "# branch.oid " } | get 0? | default "")
let oid = if ($oid_line == "") {
""
} else {
($oid_line | str replace "# branch.oid " "" | str trim)
}
($oid | str substring 0..7)
} else {
$head_name
}
let ab = ($lines | where {|l| $l | str starts-with "# branch.ab " } | get 0?) let ab = ($lines | where {|l| $l | str starts-with "# branch.ab " } | get 0?)
let ab_match = if ($ab == null) {
let ahead = if ($ab == null) { null
0
} else { } else {
let m = (($ab | parse -r '# branch.ab \+(?<a>\d+) -(?<b>\d+)') | get 0?) ($ab | parse -r '# branch.ab \+(?<a>\d+) -(?<b>\d+)' | get 0?)
if ($m == null) { 0 } else { (($m | get a) | into int) }
}
let behind = if ($ab == null) {
0
} else {
let m = (($ab | parse -r '# branch.ab \+(?<a>\d+) -(?<b>\d+)') | get 0?)
if ($m == null) { 0 } else { (($m | get b) | into int) }
} }
let ahead = if ($ab_match == null) { 0 } else { (($ab_match | get a) | into int) }
let behind = if ($ab_match == null) { 0 } else { (($ab_match | get b) | into int) }
{ {
branch: $branch_text branch: $branch_text
changed: $changed changed: $changed
untracked: $untracked untracked: $untracked
stashed: $stashed stashed: $stashed_n
ahead: $ahead ahead: $ahead
behind: $behind behind: $behind
} }
} }
def _pj_git_branch_module [p: record] { def _pj_git_branch_module [p: record, git: record] {
let git = (_pj_git_info)
let branch = ($git.branch | default "") let branch = ($git.branch | default "")
mut status_parts = [] mut status_parts = []
@@ -432,122 +435,146 @@ def _pj_git_branch_module [p: record] {
} }
} }
def _pj_has_glob [pattern: string] { # ---------------------------------------------------------------------------
((glob $pattern | length) > 0) # Programming-language detection and version probing — DISABLED.
} #
# These were the dominant cost in prompt rendering (up to ~660ms/frame in
def _pj_c_version [] { # projects with source files, mostly from spawning cc/c++/python --version).
if (which ^cc | is-empty) { # They are commented out for now. To re-enable, uncomment the block below and
return "" # remove (or rename) the stub `_pj_lang_segments` that returns an empty list.
} #
let out = (^cc --version | complete) # def _pj_has_glob [pattern: string] {
_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim)) # ((glob $pattern | length) > 0)
} # }
#
def _pj_cpp_version [] { # def _pj_c_version [] {
if (which ^'c++' | is-empty) { # if (which ^cc | is-empty) {
return "" # return ""
} # }
let out = (^'c++' --version | complete) # let out = (^cc --version | complete)
_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim)) # _pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim))
} # }
#
def _pj_rust_version [] { # def _pj_cpp_version [] {
if (which ^rustc | is-empty) { # if (which ^'c++' | is-empty) {
return "" # return ""
} # }
let out = (^rustc --version | complete) # let out = (^'c++' --version | complete)
_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim)) # _pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim))
} # }
#
def _pj_zig_version [] { # def _pj_rust_version [] {
if (which ^zig | is-empty) { # if (which ^rustc | is-empty) {
return "" # return ""
} # }
let out = (^zig version | complete) # let out = (^rustc --version | complete)
_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim)) # _pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim))
} # }
#
def _pj_python_version [] { # def _pj_zig_version [] {
if (which ^python | is-not-empty) { # if (which ^zig | is-empty) {
let out = (^python --version | complete) # return ""
return (_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim))) # }
} # let out = (^zig version | complete)
# _pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim))
if (which ^python3 | is-not-empty) { # }
let out = (^python3 --version | complete) #
return (_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim))) # def _pj_python_version [] {
} # if (which ^python | is-not-empty) {
# let out = (^python --version | complete)
"" # return (_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim)))
} # }
#
def _pj_python_venv [] { # if (which ^python3 | is-not-empty) {
let venv = ($env.VIRTUAL_ENV? | default "") # let out = (^python3 --version | complete)
if ($venv != "") { # return (_pj_first_version (($out.stdout | str trim) + " " + ($out.stderr | str trim)))
return ($venv | path basename) # }
} #
($env.CONDA_DEFAULT_ENV? | default "") # ""
} # }
#
# def _pj_python_venv [] {
# let venv = ($env.VIRTUAL_ENV? | default "")
# if ($venv != "") {
# return ($venv | path basename)
# }
# ($env.CONDA_DEFAULT_ENV? | default "")
# }
#
# def _pj_lang_segments [] {
# # One glob sweep replaces 11 separate glob calls. The resulting set of
# # lowercase extensions is checked per-language below. glob is
# # case-insensitive on Windows, so *.CPP is matched by "cpp" too.
# let exts = (glob *.{c,h,cpp,cc,cxx,hpp,hh,hxx,rs,zig,py}
# | each {|f| $f | path parse | get extension | str downcase }
# | uniq)
#
# let has_c = ("c" in $exts or "h" in $exts)
# let has_cpp = (["cpp" "cc" "cxx" "hpp" "hh" "hxx"] | any {|e| $e in $exts })
# let has_rust = (("Cargo.toml" | path exists) or ("rs" in $exts))
# let has_zig = (("build.zig" | path exists) or ("zig" in $exts))
# let has_python = (("pyproject.toml" | path exists) or ("requirements.txt" | path exists) or (".python-version" | path exists) or ("py" in $exts))
#
# mut out = []
# if $has_c {
# let v = (_pj_c_version)
# if ($v != "") {
# $out = ($out | append $" v($v)")
# } else {
# $out = ($out | append "")
# }
# }
#
# if $has_cpp {
# let v = (_pj_cpp_version)
# if ($v != "") {
# $out = ($out | append $" v($v)")
# } else {
# $out = ($out | append "")
# }
# }
#
# if $has_rust {
# let v = (_pj_rust_version)
# if ($v != "") {
# $out = ($out | append $"󱘗 v($v)")
# } else {
# $out = ($out | append "󱘗")
# }
# }
#
# if $has_zig {
# let v = (_pj_zig_version)
# if ($v != "") {
# $out = ($out | append $" v($v)")
# } else {
# $out = ($out | append "")
# }
# }
#
# if $has_python {
# let pyver = (_pj_python_version)
# let venv = (_pj_python_venv)
# let py_base = if ($pyver != "") { $" v($pyver)" } else { "" }
# if ($venv != "") {
# $out = ($out | append $"($py_base) \(($venv)\)")
# } else {
# $out = ($out | append $py_base)
# }
# }
#
# $out
# }
# ---------------------------------------------------------------------------
# Stub: language segments are disabled. Returns an empty list so the prompt
# simply omits the "via <lang>" parts. See the commented block above to
# restore full language detection.
def _pj_lang_segments [] { def _pj_lang_segments [] {
let has_c = ((_pj_has_glob "*.c") or (_pj_has_glob "*.h")) []
let has_cpp = ((_pj_has_glob "*.cpp") or (_pj_has_glob "*.cc") or (_pj_has_glob "*.cxx") or (_pj_has_glob "*.hpp") or (_pj_has_glob "*.hh") or (_pj_has_glob "*.hxx"))
let has_rust = (("Cargo.toml" | path exists) or (_pj_has_glob "*.rs"))
let has_zig = (("build.zig" | path exists) or (_pj_has_glob "*.zig"))
let has_python = (("pyproject.toml" | path exists) or ("requirements.txt" | path exists) or (".python-version" | path exists) or (_pj_has_glob "*.py"))
mut out = []
if $has_c {
let v = (_pj_c_version)
if ($v != "") {
$out = ($out | append $" v($v)")
} else {
$out = ($out | append "")
}
}
if $has_cpp {
let v = (_pj_cpp_version)
if ($v != "") {
$out = ($out | append $" v($v)")
} else {
$out = ($out | append "")
}
}
if $has_rust {
let v = (_pj_rust_version)
if ($v != "") {
$out = ($out | append $"󱘗 v($v)")
} else {
$out = ($out | append "󱘗")
}
}
if $has_zig {
let v = (_pj_zig_version)
if ($v != "") {
$out = ($out | append $" v($v)")
} else {
$out = ($out | append "")
}
}
if $has_python {
let pyver = (_pj_python_version)
let venv = (_pj_python_venv)
let py_base = if ($pyver != "") { $" v($pyver)" } else { "" }
if ($venv != "") {
$out = ($out | append $"($py_base) \(($venv)\)")
} else {
$out = ($out | append $py_base)
}
}
$out
} }
def _pj_prompt_static_base [] { def _pj_prompt_static_base [] {
let user = (try { whoami } catch { "" } | into string | str trim) let user = (try { whoami } catch { "" } | into string | str trim)
let effective_user = if ($user != "") { let effective_user = if ($user != "") {
@@ -584,8 +611,10 @@ def _pj_left_prompt [] {
} }
let shell_part = $"($p.c_shell) ($static.shell_name) ($p.c_reset)" let shell_part = $"($p.c_shell) ($static.shell_name) ($p.c_reset)"
let dir = (_pj_truncate_pwd $env.PWD 4) let repo = (_pj_git_repo_info)
let git_part = (_pj_git_branch_module $p) let dir = (_pj_truncate_pwd $env.PWD 4 $repo)
let git = (_pj_git_info $repo)
let git_part = (_pj_git_branch_module $p $git)
let langs = (_pj_lang_segments) let langs = (_pj_lang_segments)
let lang_parts = ($langs | each {|seg| let lang_parts = ($langs | each {|seg|