From 1beaef61c07cf65341caf5d992abbdca13958f51 Mon Sep 17 00:00:00 2001 From: Jan Klass Date: Sun, 12 Oct 2025 16:42:35 +0200 Subject: [PATCH 1/2] Add `winget upgrades` command `winget upgrade` has two modes: * No parameters (at all) lists available upgrades * Any parameter attempts to match and upgrade a package Much like `winget list`, `winget upgrade` prints a spinner, then a table, and then additionally a footer. The `winget list` completions handle output through `detect columns --guess` and return structured data. Presumably because `detect columns --guess` does not work on `winget upgrade`, no output handling was done until now. 1. Split off trimLoadingSymbol as a common function between two, now three, commands 2. Implement a separate command `winget upgrades` instead of handling `winget upgrade` because of overloaded behavior complicating implementation Now, `winget upgrades` can be used to get structured data of available upgrades, while `winget upgrade` behaves like before, without parameters has text output, and with parameters upgrades packages. While introducing a separate command is not strictly completions [for external commands], this one is very close to the completions, logic, and use cases. Keeping it close will help discovery and future development which may integrate returning structured data into the "normal" completions. --- .../winget/winget-completions.nu | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/custom-completions/winget/winget-completions.nu b/custom-completions/winget/winget-completions.nu index 91d7c3db..5287a7cf 100644 --- a/custom-completions/winget/winget-completions.nu +++ b/custom-completions/winget/winget-completions.nu @@ -36,6 +36,10 @@ def "nu-complete winget flagify" [name: string, value: any, --short(-s)] { } } +def "nu-complete winget trimLoadingSymbol" [] { + str replace -r r#'^[^\w]*'# "" +} + def "nu-complete winget uninstall package id" [] { ^winget export -s winget -o __winget-temp__.json | ignore let results = (open __winget-temp__.json | get Sources.Packages | first | get PackageIdentifier) @@ -259,8 +263,7 @@ export def "winget search" [ ) let output = ^winget ...$params - # remove loading symbols at start of output - | str replace -r r#'^[^\w]*'# "" + | nu-complete winget trimLoadingSymbol if $raw or $help { $output } else { @@ -319,8 +322,7 @@ export def "winget list" [ ) let output = ^winget ...$params - # remove loading symbols at start of output - | str replace -r r#'^[^\w]*'# "" + | nu-complete winget trimLoadingSymbol if $help or $raw { $output } else { @@ -338,6 +340,31 @@ export def "winget list" [ } export alias "winget ls" = winget list +def "winget upgrades" [] { + let output = ^winget upgrade | nu-complete winget trimLoadingSymbol | lines + + let head = $output | first + let rest = $output | skip 2 + + let colnames = [ Name Id Version Available Source ] + # We must be unicode aware in determining and using index; winget uses `…` elippses to hide overflow + let cols = $colnames | each {|col| $head | {name: $col i: ($head | str index-of $col --grapheme-clusters) } } + + let dirty = $rest | each {|line| + let chars = $line | split chars + { + name: ( $chars | slice ($cols.0.i)..($cols.1.i - 1) | str join | str trim ) + id: ( $chars | slice ($cols.1.i)..($cols.2.i - 1) | str join | str trim ) + version: ( $chars | slice ($cols.2.i)..($cols.3.i - 1) | str join | str trim ) + available: ( $chars | slice ($cols.3.i)..($cols.4.i - 1) | str join | str trim ) + source: ( $chars | slice ($cols.4.i).. | str join | str trim ) + } + } + # Reject footer lines, in a best effort approach, because there is no clear separator or definitely identifiable form change. + # We expect `x upgrades available.` to follow the table. Then maybe `x package(s) have version numbers that cannot be determined. Use --include-unknown to see all results.` + return ($dirty | take until {|x| $x.source == '' }) +} + # Upgrades the given package export extern "winget upgrade" [ query?: string, From b40b6ee2a2f3afbdb70acd0ae631e00b6597b066 Mon Sep 17 00:00:00 2001 From: Jan Klass Date: Sun, 12 Oct 2025 17:08:15 +0200 Subject: [PATCH 2/2] Do nothing for no Upgrades untested, unverified --- custom-completions/winget/winget-completions.nu | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/custom-completions/winget/winget-completions.nu b/custom-completions/winget/winget-completions.nu index 5287a7cf..4e0f14e1 100644 --- a/custom-completions/winget/winget-completions.nu +++ b/custom-completions/winget/winget-completions.nu @@ -341,10 +341,14 @@ export def "winget list" [ export alias "winget ls" = winget list def "winget upgrades" [] { - let output = ^winget upgrade | nu-complete winget trimLoadingSymbol | lines + let output = ^winget upgrade | nu-complete winget trimLoadingSymbol - let head = $output | first - let rest = $output | skip 2 + # Do nothing when no upgrades available + if ( ($output | str starts-with 'No') or ($output | str starts-with '0') ) { return } + + let lines = $output | lines + let head = $lines | first + let rest = $lines | skip 2 let colnames = [ Name Id Version Available Source ] # We must be unicode aware in determining and using index; winget uses `…` elippses to hide overflow