From 35ac91e58f99a6ffacb7873ec9abd7a13d42fd6b Mon Sep 17 00:00:00 2001 From: TheSw1m Date: Tue, 8 Sep 2026 20:14:52 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20Zsh=20Tab=20=E8=A1=A5?= =?UTF-8?q?=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/PR-test.yml | 11 +- Makefile | 20 +- pkg/deb/deb-installation-test.sh | 15 +- pkg/deb/debian/rules | 4 +- test/zsh-completion-integration.zsh | 117 ++++++++++ test/zsh-completion-sync.pl | 100 +++++++++ test/zsh-completion.zsh | 219 +++++++++++++++++++ tool/completion/_chsrc | 321 ++++++++++++++++++++++++++++ 8 files changed, 802 insertions(+), 5 deletions(-) create mode 100644 test/zsh-completion-integration.zsh create mode 100644 test/zsh-completion-sync.pl create mode 100644 test/zsh-completion.zsh create mode 100644 tool/completion/_chsrc diff --git a/.github/workflows/PR-test.yml b/.github/workflows/PR-test.yml index eed02fa8..032dba62 100644 --- a/.github/workflows/PR-test.yml +++ b/.github/workflows/PR-test.yml @@ -25,6 +25,10 @@ on: paths: - "src/**" - "lib/**" + - "Makefile" + - "tool/completion/**" + - "test/zsh-completion*" + - "pkg/deb/**" jobs: test-on-ubuntu: @@ -36,13 +40,18 @@ jobs: - name: 检出代码 uses: actions/checkout@v7 + - name: 安装Zsh补全测试依赖 + run: | + sudo apt-get update + sudo apt-get install -y zsh + - name: 测试构建情况 run: | make - name: 测试test情况 run: | - make test + make test REQUIRE_ZSH=1 test-on-windows: name: 在Windows上测试 diff --git a/Makefile b/Makefile index eaaa5d42..a91771ac 100755 --- a/Makefile +++ b/Makefile @@ -215,7 +215,7 @@ debug: build-in-debug-mode -test: test-make-var test-xy test-fw +test: test-make-var test-xy test-fw test-zsh-completion test-make-var: @echo "On-Linux: $(On-Linux)" @@ -245,6 +245,19 @@ test-fw: @$(CC) test/fw.c $(CFLAGS) -o fw @./fw +test-zsh-completion: + @perl ./test/zsh-completion-sync.pl + @if command -v zsh >/dev/null 2>&1; then \ + zsh -n ./tool/completion/_chsrc ./test/zsh-completion.zsh ./test/zsh-completion-integration.zsh && \ + zsh -f ./test/zsh-completion.zsh && \ + zsh -f ./test/zsh-completion-integration.zsh; \ + elif [ "$(REQUIRE_ZSH)" = "1" ]; then \ + echo "zsh is required for Zsh completion tests" >&2; \ + exit 1; \ + else \ + echo "zsh not found; skipped Zsh completion runtime tests"; \ + fi + check: test # AUR package 安装时将执行此 target @@ -276,10 +289,13 @@ build-deb: clean-deb: @$(MAKE) -C pkg/deb deb-clean +ZSH_COMPLETION_DIR ?= /usr/share/zsh/site-functions + install: $(ReleaseMode-Target-Name) install -D -m 755 $(ReleaseMode-Target-Name) $(DESTDIR)/usr/bin/chsrc install -D -m 644 doc/chsrc.1 $(DESTDIR)/usr/share/man/man1/chsrc.1 install -D -m 644 tool/completion/bash_completion.sh $(DESTDIR)/usr/share/bash-completion/completions/chsrc + install -D -m 644 tool/completion/_chsrc $(DESTDIR)$(ZSH_COMPLETION_DIR)/_chsrc @@ -291,4 +307,4 @@ install: $(ReleaseMode-Target-Name) .PHONY: all b build bd br bcir d t check c \ build-in-dev-mode build-in-debug-mode build-in-release-mode build-in-ci-release-mode \ - debug test test-make-var test-xy test-fw fastcheck test-cli clean install build-deb clean-deb rawstr4c + debug test test-make-var test-xy test-fw test-zsh-completion fastcheck test-cli clean install build-deb clean-deb rawstr4c diff --git a/pkg/deb/deb-installation-test.sh b/pkg/deb/deb-installation-test.sh index 214612b5..869e58ab 100644 --- a/pkg/deb/deb-installation-test.sh +++ b/pkg/deb/deb-installation-test.sh @@ -36,7 +36,20 @@ else echo "✓ chsrc man page exists" fi -# Test 3: Test basic functionality +# Test 3: Check if shell completion files exist +if [ ! -f "/usr/share/bash-completion/completions/chsrc" ]; then + echo "ERROR: Bash completion not found at /usr/share/bash-completion/completions/chsrc" + exit 1 +fi + +if [ ! -f "/usr/share/zsh/vendor-completions/_chsrc" ]; then + echo "ERROR: Zsh completion not found at /usr/share/zsh/vendor-completions/_chsrc" + exit 1 +fi + +echo "✓ Bash and Zsh completion files exist" + +# Test 4: Test basic functionality echo "Testing basic chsrc functionality..." if /usr/bin/chsrc help >/dev/null 2>&1; then echo "✓ command 'chsrc help' works" diff --git a/pkg/deb/debian/rules b/pkg/deb/debian/rules index 98ce5cd6..d9e2e7da 100755 --- a/pkg/deb/debian/rules +++ b/pkg/deb/debian/rules @@ -46,4 +46,6 @@ override_dh_auto_build: @$(MAKE) -C $(Chsrc-Root-Dir) build-in-release-mode override_dh_auto_install: - @$(MAKE) -C $(Chsrc-Root-Dir) install DESTDIR=$(CURDIR)/debian/chsrc + @$(MAKE) -C $(Chsrc-Root-Dir) install \ + DESTDIR=$(CURDIR)/debian/chsrc \ + ZSH_COMPLETION_DIR=/usr/share/zsh/vendor-completions diff --git a/test/zsh-completion-integration.zsh b/test/zsh-completion-integration.zsh new file mode 100644 index 00000000..4d96d1a5 --- /dev/null +++ b/test/zsh-completion-integration.zsh @@ -0,0 +1,117 @@ +#!/usr/bin/env zsh +# ----------------------------------------------------------------------------- +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Exercise real ZLE completion matching and insertion in an isolated child Zsh. +# The child uses a temporary ZDOTDIR and compinit -D, so neither user nor system +# completion configuration is read or written. +# ----------------------------------------------------------------------------- + +emulate -L zsh +setopt errexit nounset pipefail extendedglob + +zmodload zsh/zpty +zmodload zsh/zselect + +typeset test_dir=${0:A:h} +typeset completion_dir=${test_dir:h}/tool/completion +typeset temp_parent=${TMPDIR:-/tmp} +typeset temp_zdotdir +temp_zdotdir=$(mktemp -d "${temp_parent}/chsrc-zsh-completion.XXXXXXXX") +typeset child_started=0 + +cleanup() { + if (( child_started )); then + zpty -d chsrc_completion_shell 2>/dev/null || true + fi + if [[ -n ${temp_zdotdir:-} && -d ${temp_zdotdir} && + ${temp_zdotdir:t} == chsrc-zsh-completion.* ]]; then + command rm -r -- "${temp_zdotdir}" + fi +} +trap cleanup EXIT HUP INT TERM + +{ + print -r -- "fpath=(${(q)completion_dir} \${fpath})" + print -r -- 'autoload -Uz compinit' + print -r -- 'compinit -D' + print -r -- "PS1='CHSRC_TEST> '" + print -r -- "RPROMPT=''" + print -r -- "bindkey '^I' expand-or-complete" + print -r -- 'setopt autolist beep' +} > "${temp_zdotdir}/.zshrc" + +# Keep the captured ZLE transcript independent of the terminal running the +# tests. Rich terminals add ANSI attributes around the automatically inserted +# suffix, which breaks the plain-text assertions below even when completion is +# correct. +zpty -b chsrc_completion_shell env TERM=dumb ZDOTDIR="${temp_zdotdir}" zsh -d +child_started=1 + +wait_for_output() { + local needle=$1 chunk='' + integer attempts=300 + REPLY='' + while (( attempts-- > 0 )); do + while zpty -rt chsrc_completion_shell chunk; do + REPLY+="${chunk}" + chunk='' + done + [[ ${REPLY} == *"${needle}"* ]] && return 0 + zselect -t 1 || true + done + print -u2 -r -- "Timed out waiting for ${(qqq)needle}; output was ${(qqq)REPLY}" + return 1 +} + +clear_input() { + zpty -wn chsrc_completion_shell $'\C-u' +} + +collect_pending_output() { + local chunk='' + zselect -t 5 || true + while zpty -rt chsrc_completion_shell chunk; do + REPLY+="${chunk}" + chunk='' + done +} + +complete_and_expect() { + local input=$1 expected=$2 + zpty -wn chsrc_completion_shell "${input}"$'\t' + wait_for_output "${expected}" + clear_input +} + +complete_and_expect 'chsrc set omar' 'chsrc set omarchy ' +complete_and_expect 'chsrc s -scope=p' 'chsrc s -scope=project ' +complete_and_expect 'chsrc set -scope p' 'chsrc set -scope project ' +complete_and_expect 'chsrc -scope=p' 'chsrc -scope=project ' +complete_and_expect 'chsrc set ruby fi' 'chsrc set ruby first ' +complete_and_expect 'chsrc -en -no-' 'chsrc -en -no-color ' + +# A completed mirror occupies the final positional slot. Pressing Tab in the +# next empty slot must not list first/upstream again. +zpty -wn chsrc_completion_shell $'chsrc set ruby first \t' +wait_for_output $'\a' +collect_pending_output +if [[ ${REPLY} == *upstream* ]]; then + print -u2 -r -- "Completion offered a second mirror: ${(qqq)REPLY}" + exit 1 +fi +clear_input + +# A non-empty token in the same invalid slot must not uniquely expand to +# upstream either. This catches the case even when candidate listing styles +# suppress the menu for an empty prefix. +zpty -wn chsrc_completion_shell $'chsrc set ruby first u\t' +wait_for_output $'\a' +collect_pending_output +if [[ ${REPLY} == *upstream* ]]; then + print -u2 -r -- "Completion inserted a second mirror: ${(qqq)REPLY}" + exit 1 +fi +clear_input + +print 'Real Zsh completion insertion tests passed.' diff --git a/test/zsh-completion-sync.pl b/test/zsh-completion-sync.pl new file mode 100644 index 00000000..3ae9acd4 --- /dev/null +++ b/test/zsh-completion-sync.pl @@ -0,0 +1,100 @@ +#!/usr/bin/env perl +# ----------------------------------------------------------------------------- +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Check that the native Zsh completion contains every alias of every dish +# registered in src/recipe/menu.c. Internal sources-only dishes are not menu +# entries and therefore must not be offered to users. +# ----------------------------------------------------------------------------- + +use strict; +use warnings; +use FindBin qw($Bin); +use File::Find qw(find); + +my $root = "$Bin/.."; +my $menu_path = "$root/src/recipe/menu.c"; +my $completion_path = "$root/tool/completion/_chsrc"; + +open my $menu_fh, '<', $menu_path or die "Cannot read $menu_path: $!\n"; +my @registered; +my $category; +while (my $line = <$menu_fh>) { + if ($line =~ /^#define add\(t\).*&((?:pl|os|wr))_##t##_dish/) { + $category = $1; + next; + } + if ($line =~ /^#undef add/) { + undef $category; + next; + } + if (defined $category && $line =~ /\badd\s*\(\s*([A-Za-z0-9_]+)\s*\)/) { + push @registered, "${category}_$1"; + } +} +close $menu_fh; + +my %aliases_for; +find( + sub { + return unless -f $_ && /\.c\z/; + open my $fh, '<', $File::Find::name + or die "Cannot read $File::Find::name: $!\n"; + local $/; + my $source = <$fh>; + close $fh; + while ($source =~ /def_(?:combo_)?dish\s*\(\s*([A-Za-z0-9_]+)\s*,\s*"([^"]+)"\s*\)/g) { + $aliases_for{$1} = [split m{/}, $2]; + } + }, + "$root/src/recipe" +); + +my @errors; +my %expected; +my %menu_category = (pl => 'lang', os => 'os', wr => 'ware'); +for my $dish (@registered) { + if (!exists $aliases_for{$dish}) { + push @errors, "registered dish $dish has no def_dish/def_combo_dish declaration"; + next; + } + my ($prefix) = $dish =~ /^(pl|os|wr)_/; + for my $alias (@{$aliases_for{$dish}}) { + $expected{$alias} = $menu_category{$prefix}; + } +} + +open my $completion_fh, '<', $completion_path + or die "Cannot read $completion_path: $!\n"; +my %completed; +my $completion_category; +while (my $line = <$completion_fh>) { + if ($line =~ /^dishes_(lang|os|ware)=\(/) { + $completion_category = $1; + next; + } + if (defined $completion_category && $line =~ /^\)/) { + undef $completion_category; + next; + } + if (defined $completion_category && $line =~ /^\s*'([^':]+):/) { + $completed{$1} = $completion_category; + } +} +close $completion_fh; + +for my $alias (sort keys %expected) { + if (!exists $completed{$alias}) { + push @errors, "completion is missing registered dish alias '$alias'"; + } elsif ($completed{$alias} ne $expected{$alias}) { + push @errors, "completion puts '$alias' in $completed{$alias}, expected $expected{$alias}"; + } +} + +for my $alias (sort keys %completed) { + push @errors, "completion contains unregistered dish alias '$alias'" + unless exists $expected{$alias}; +} + +die join("\n", @errors) . "\n" if @errors; +print "Zsh completion dish aliases match the registered menus.\n"; diff --git a/test/zsh-completion.zsh b/test/zsh-completion.zsh new file mode 100644 index 00000000..3b8662a2 --- /dev/null +++ b/test/zsh-completion.zsh @@ -0,0 +1,219 @@ +#!/usr/bin/env zsh +# ----------------------------------------------------------------------------- +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Lightweight tests for the completion state machine. Completion widgets are +# mocked so the test does not touch the user's Zsh setup. +# ----------------------------------------------------------------------------- + +emulate -L zsh +setopt errexit nounset pipefail extendedglob + +typeset test_dir=${0:A:h} +fpath=("${test_dir}/../tool/completion" ${fpath}) +autoload -Uz _chsrc + +typeset -ga captured + +_describe() { + local array_name=${argv[-1]} + local -a specs + specs=("${(@P)array_name}") + local spec candidate + for spec in ${specs}; do + candidate=${spec%%:*} + if [[ -z ${PREFIX} || ${candidate[1,${#PREFIX}]} == ${PREFIX} ]]; then + captured+=("${candidate}") + fi + done +} + +compadd() { + local after_separator=0 arg + for arg in ${argv}; do + if (( after_separator )); then + if [[ -z ${PREFIX} || ${arg[1,${#PREFIX}]} == ${PREFIX} ]]; then + captured+=("${arg}") + fi + elif [[ ${arg} == -- ]]; then + after_separator=1 + fi + done +} + +compset() { + if [[ $1 == -P ]]; then + PREFIX=${PREFIX#$2} + fi +} + +_message() { return 0 } + +capture() { + captured=() + words=("${(@)argv}") + CURRENT=${#words} + PREFIX=${words[CURRENT]} + _chsrc +} + +assert_has() { + local expected=$1 + if (( ! ${captured[(Ie)${expected}]} )); then + print -u2 -- "Expected completion '$expected' in: ${captured[*]}" + return 1 + fi +} + +assert_lacks() { + local unexpected=$1 + if (( ${captured[(Ie)${unexpected}]} )); then + print -u2 -- "Unexpected completion '$unexpected' in: ${captured[*]}" + return 1 + fi +} + +capture chsrc '' +assert_has version +assert_has measure +assert_has reset +assert_has -scope= +assert_lacks mea +assert_lacks rest +assert_lacks -local +assert_lacks -scope +assert_lacks -english +assert_lacks -no-colour +assert_lacks -h +assert_lacks -help +assert_lacks --help +assert_lacks -v +assert_lacks -version +assert_lacks --version + +capture chsrc -scope=p +assert_has project +assert_lacks default + +typeset -A dispatch_expect=( + help -dry + h -dry + -h -dry + -help -dry + --help -dry + issue -dry + issues -dry + isue -dry + i -dry + version -dry + ver -dry + -v -dry + -version -dry + --version -dry + list mirror + ls mirror + l mirror + measure ruby + mea ruby + m ruby + cesu ruby + ce ruby + c ruby + get ruby + g ruby + set ruby + s ruby + reset ruby + rest ruby + r ruby +) + +typeset command expected +for command expected in "${(@kv)dispatch_expect}"; do + capture chsrc "${command}" '' + assert_has "${expected}" +done + +capture chsrc set '' +assert_has omarchy +assert_has uv-pypi-index +assert_has uv-python-build +assert_has -scope= +assert_lacks pypi +assert_lacks nodejs-binary + +capture chsrc s -dry '' +assert_has ruby + +capture chsrc set -local '' +assert_has ruby + +capture chsrc set -english '' +assert_has ruby + +capture chsrc set -no-colour '' +assert_has ruby + +capture chsrc set -scope project '' +assert_has ruby + +capture chsrc set -scope '' +assert_has default +assert_has project +assert_has user +assert_has system +assert_lacks ruby + +capture chsrc set -scope= +assert_has default +assert_has project +assert_has user +assert_has system + +capture chsrc set -scope=p +assert_has project +assert_lacks default +assert_lacks system + +capture chsrc set -scope=x +assert_lacks default +assert_lacks project + +capture chsrc set -lo +assert_lacks -local + +capture chsrc -en -he +assert_lacks -help +assert_lacks help + +capture chsrc list '' +assert_has mirror +assert_has dish +assert_has lang +assert_has ware +assert_lacks mirrors +assert_lacks dishes +assert_lacks language +assert_lacks software +assert_has ruby + +capture chsrc set ruby '' +assert_has first +assert_has upstream +assert_lacks -dry + +capture chsrc set ruby f +assert_has first +assert_lacks upstream + +capture chsrc set ruby first '' +assert_lacks first +assert_lacks upstream + +capture chsrc set ruby first u +assert_lacks upstream + +capture chsrc set ruby -scope= +assert_lacks default + +print 'Zsh completion state-machine tests passed.' diff --git a/tool/completion/_chsrc b/tool/completion/_chsrc new file mode 100644 index 00000000..46e4595c --- /dev/null +++ b/tool/completion/_chsrc @@ -0,0 +1,321 @@ +#compdef chsrc +# ----------------------------------------------------------------------------- +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Native Zsh completion for chsrc. Keep the dish aliases in sync with the +# registered menus in src/recipe/menu.c; test/zsh-completion-sync.pl checks it. +# ----------------------------------------------------------------------------- + +emulate -L zsh +setopt extendedglob + +local -a commands options scope_options list_targets +local -a dishes_lang dishes_os dishes_ware mirror_choices + +# Keep menus concise by offering one canonical spelling for commands, options, +# and list targets. Accepted aliases remain in the parser states below, so an +# alias typed explicitly still receives context-aware completion. +commands=( + 'help:show help (aliases: h, -h, -help, --help)' + 'issue:show related issue links (aliases: issues, isue, i)' + 'list:list mirrors or supported dishes (aliases: ls, l)' + 'measure:measure all sources for a dish (aliases: mea, m, cesu, ce, c)' + 'get:show the current source for a dish (alias: g)' + 'set:change the source for a dish (alias: s)' + 'reset:reset a dish to its upstream source (aliases: rest, r)' + 'version:show version information (aliases: ver, -v, -version, --version)' + '-dry:simulate changes before a help or version flag' + '-ipv6:use IPv6 before a help or version flag' + '-en:use English before a help or version flag (alias: -english)' + '-no-color:disable colored output before a help or version flag (alias: -no-colour)' +) + +options=( + '-dry:simulate changes without running them' + '-ipv6:use IPv6 for speed measurements' + '-en:use English output (alias: -english)' + '-no-color:disable colored output (alias: -no-colour)' + '-h:show help (aliases: -help, --help)' + '-v:show version information' +) + +scope_options=( + 'default:use the scope selected by the recipe' + 'project:change the current project only' + 'user:change the current user only' + 'system:change the whole system' +) + +list_targets=( + 'mirror:list all mirror sites (alias: mirrors)' + 'dish:list all supported dishes (aliases: dishes, dishs)' + 'os:list operating-system dishes' + 'lang:list programming-language dishes (aliases: pl, language)' + 'ware:list software dishes (alias: software)' +) + +dishes_lang=( + 'gem:Ruby and RubyGems' + 'ruby:Ruby and RubyGems' + 'rb:Ruby and RubyGems' + 'rubygem:Ruby and RubyGems' + 'rubygems:Ruby and RubyGems' + 'bundler:Ruby and RubyGems' + 'py:Python package tools' + 'python:Python package tools' + 'pip:Python pip' + 'poetry:Python Poetry' + 'pdm:Python PDM' + 'rye:Python Rye' + 'uv:Python uv tools' + 'uv-pypi-index:uv package index' + 'uv-python-build:uv Python downloads' + 'js:JavaScript package tools' + 'javascript:JavaScript package tools' + 'node:JavaScript package tools' + 'nodejs:JavaScript package tools' + 'bun:Bun' + 'npm:npm' + 'yarn:Yarn' + 'pnpm:pnpm' + 'nvm:nvm' + 'perl:Perl and CPAN' + 'cpan:Perl and CPAN' + 'php:PHP and Composer' + 'composer:PHP and Composer' + 'lua:Lua and LuaRocks' + 'luarocks:Lua and LuaRocks' + 'go:Go modules' + 'golang:Go modules' + 'goproxy:Go modules' + 'rust:Rust crates' + 'cargo:Rust crates' + 'crate:Rust crates' + 'crates:Rust crates' + 'rustup:Rust toolchains' + 'java:Java build tools' + 'maven:Java build tools' + 'mvn:Java build tools' + 'maven-daemon:Java build tools' + 'mvnd:Java build tools' + 'gradle:Java build tools' + 'clojure:Clojure tools' + 'clojars:Clojure tools' + 'cloj:Clojure tools' + 'lein:Clojure tools' + 'dart:Dart packages' + 'pub:Dart packages' + 'flutter:Flutter packages' + 'nuget:NuGet' + 'net:NuGet' + '.net:NuGet' + 'dotnet:NuGet' + 'haskell:Haskell packages' + 'cabal:Haskell packages' + 'stack:Haskell packages' + 'hackage:Haskell packages' + 'ocaml:OCaml packages' + 'opam:OCaml packages' + 'r:R and CRAN' + 'cran:R and CRAN' + 'julia:Julia packages' +) + +dishes_os=( + 'ubuntu:Ubuntu' + 'zorinos:Zorin OS' + 'linuxmint:Linux Mint' + 'debian:Debian' + 'fedora:Fedora Linux' + 'opensuse:openSUSE' + 'opensuse-leap:openSUSE Leap' + 'opensuse-tumbleweed:openSUSE Tumbleweed' + 'kali:Kali Linux' + 'msys2:MSYS2' + 'msys:MSYS2' + 'arch:Arch Linux' + 'archlinux:Arch Linux' + 'archlinuxcn:Arch Linux CN repository' + 'archcn:Arch Linux CN repository' + 'manjaro:Manjaro Linux' + 'omarchy:Omarchy OPR repository' + 'gentoo:Gentoo Linux' + 'rocky:Rocky Linux' + 'rockylinux:Rocky Linux' + 'alma:AlmaLinux' + 'almalinux:AlmaLinux' + 'alpine:Alpine Linux' + 'voidlinux:Void Linux' + 'solus:Solus' + 'trisquel:Trisquel' + 'linuxlite:Linux Lite' + 'ros:Robot Operating System' + 'ros2:Robot Operating System' + 'raspi:Raspberry Pi OS' + 'raspberrypi:Raspberry Pi OS' + 'armbian:Armbian' + 'openwrt:OpenWrt' + 'opkg:OpenWrt' + 'LEDE:OpenWrt' + 'termux:Termux' + 'openkylin:openKylin' + 'openeuler:openEuler' + 'anolis:Anolis OS' + 'openanolis:Anolis OS' + 'deepin:deepin' + 'freebsd:FreeBSD' + 'netbsd:NetBSD' + 'openbsd:OpenBSD' +) + +dishes_ware=( + 'winget:WinGet' + 'brew:Homebrew' + 'homebrew:Homebrew' + 'cocoa:CocoaPods' + 'cocoapods:CocoaPods' + 'cocoapod:CocoaPods' + 'docker:Docker Hub' + 'dockerhub:Docker Hub' + 'flatpak:Flatpak and Flathub' + 'flathub:Flatpak and Flathub' + 'nix:Nix channels' + 'guix:GNU Guix' + 'emacs:Emacs packages' + 'elpa:Emacs packages' + 'latex:TeX packages' + 'ctan:TeX packages' + 'tex:TeX packages' + 'texlive:TeX packages' + 'miktex:TeX packages' + 'tlmgr:TeX packages' + 'mpm:TeX packages' + 'conda:Anaconda' + 'anaconda:Anaconda' +) + +mirror_choices=( + 'first:use the first source selected by the maintainers' + 'upstream:use the upstream source' +) + +if (( CURRENT == 2 )); then + if [[ ${words[CURRENT]} == -scope=* ]]; then + compset -P '-scope=' + _describe -t scopes 'scope' scope_options + return + fi + + local command_ret=1 + _describe -t commands 'chsrc command' commands && command_ret=0 + compadd -S '' -X 'option with an attached value' -- '-scope=' && command_ret=0 + return command_ret +fi + +local command=${words[2]} +local current_word=${words[CURRENT]} + +# Locate the first positional argument before the cursor. Options belong +# between the command and dish; offering them after a dish would shift the +# dish position in chsrc's current parser. +local dish='' +local mirror='' +local expect_scope=0 +local ret=1 +local token +integer i +[[ ${command} == -scope ]] && expect_scope=1 +for (( i = 3; i < CURRENT; i++ )); do + token=${words[i]} + if (( expect_scope )); then + expect_scope=0 + continue + fi + case ${token} in + -scope) + expect_scope=1 + ;; + -*) + ;; + *) + if [[ -z ${dish} ]]; then + dish=${token} + elif [[ -z ${mirror} ]]; then + mirror=${token} + fi + ;; + esac +done + +# Both spellings are accepted by the CLI: "-scope=value" and +# "-scope value". Complete only scope values while one is expected, and do +# not suggest an option after the dish positional argument. +if [[ -z ${dish} && ${current_word} == -scope=* ]]; then + compset -P '-scope=' + _describe -t scopes 'scope' scope_options + return +fi + +if [[ -z ${dish} ]] && (( expect_scope )); then + _describe -t scopes 'scope' scope_options + return +fi + +case ${command} in + -dry|-scope|-scope=*|-ipv6|-local|-en|-english|-no-color|-no-colour) + if [[ -z ${dish} ]]; then + _describe -t options 'option' options && ret=0 + compadd -S '' -X 'option with an attached value' -- '-scope=' && ret=0 + return ret + else + _message 'only a help or version flag may follow leading options' + fi + ;; + + help|h|-h|-help|--help|issue|issues|isue|i|version|ver|-v|-version|--version) + _describe -t options 'option' options + ;; + + list|ls|l) + if [[ -z ${dish} ]]; then + _describe -t list-targets 'list target' list_targets && ret=0 + _describe -t dishes-lang 'programming-language dish' dishes_lang && ret=0 + _describe -t dishes-os 'operating-system dish' dishes_os && ret=0 + _describe -t dishes-ware 'software dish' dishes_ware && ret=0 + _describe -t options 'option' options && ret=0 + compadd -S '' -X 'option with an attached value' -- '-scope=' && ret=0 + return ret + else + _message 'no more arguments' + fi + ;; + + measure|mea|m|cesu|ce|c|get|g|reset|rest|r) + if [[ -z ${dish} ]]; then + _describe -t dishes-lang 'programming-language dish' dishes_lang && ret=0 + _describe -t dishes-os 'operating-system dish' dishes_os && ret=0 + _describe -t dishes-ware 'software dish' dishes_ware && ret=0 + _describe -t options 'option' options && ret=0 + compadd -S '' -X 'option with an attached value' -- '-scope=' && ret=0 + return ret + else + _message 'no more arguments' + fi + ;; + + set|s) + if [[ -z ${dish} ]]; then + _describe -t dishes-lang 'programming-language dish' dishes_lang && ret=0 + _describe -t dishes-os 'operating-system dish' dishes_os && ret=0 + _describe -t dishes-ware 'software dish' dishes_ware && ret=0 + _describe -t options 'option' options && ret=0 + compadd -S '' -X 'option with an attached value' -- '-scope=' && ret=0 + return ret + elif [[ -z ${mirror} ]]; then + _describe -t mirrors 'mirror selection' mirror_choices || + _message 'mirror code or source URL' + else + _message 'no more arguments' + fi + ;; +esac From 77ec269019f15b76aadc9cab7c1384fa50317d07 Mon Sep 17 00:00:00 2001 From: TheSw1m Date: Tue, 8 Sep 2026 20:15:51 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=8E=E5=B9=B2?= =?UTF-8?q?=E5=87=80=E6=BA=90=E7=A0=81=E6=A0=91=E6=89=A7=E8=A1=8C=20`make?= =?UTF-8?q?=20install`=20=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index a91771ac..ab8776f5 100755 --- a/Makefile +++ b/Makefile @@ -202,6 +202,12 @@ build-in-release-mode: @$(call Build-Command-For-Release, $(ReleaseMode-Target-Name)) @$(call Finished-Echo-Info, RELEASE) +# `install` depends on the release binary by filename. Build it on demand +# when installing from a clean source tree; packaging flows that already ran +# build-in-release-mode can reuse the existing artifact. +$(ReleaseMode-Target-Name): + @$(MAKE) build-in-release-mode + build-in-ci-release-mode: @$(call Starting-Echo-Info, CI-RELEASE, $(CIReleaseMode-Target-Name)) @$(call Build-Command-For-Release, $(CIReleaseMode-Target-Name)) From 6b2ff7ad42a4c034be14cc1f6590d600fd819f40 Mon Sep 17 00:00:00 2001 From: TheSw1m Date: Wed, 9 Sep 2026 14:29:38 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=8C=89=E7=85=A7=E5=AE=A1=E6=9F=A5?= =?UTF-8?q?=E6=84=8F=E8=A7=81=E5=AE=8C=E5=96=84=20Zsh=20=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/PR-test.yml | 5 ++- Makefile | 18 ++++---- ...letion.zsh => simulate-zsh-completion.zsh} | 25 +++++++++-- ...pl => verify-sync-state-of-zsh-completion} | 18 +++++--- test/zsh-completion-integration.zsh | 34 +++++++++------ tool/completion/README.md | 43 +++++++++++++++++++ tool/completion/_chsrc | 30 +++++++------ 7 files changed, 126 insertions(+), 47 deletions(-) rename test/{zsh-completion.zsh => simulate-zsh-completion.zsh} (79%) rename test/{zsh-completion-sync.pl => verify-sync-state-of-zsh-completion} (84%) create mode 100644 tool/completion/README.md diff --git a/.github/workflows/PR-test.yml b/.github/workflows/PR-test.yml index 032dba62..68c082b8 100644 --- a/.github/workflows/PR-test.yml +++ b/.github/workflows/PR-test.yml @@ -2,9 +2,10 @@ # Workflow File : PR-test.yml # File Authors : 曾奥然 # Contributors : Mikachu2333 +# | @swim233 # | # Created On : <2025-06-19> -# Last Modified : <2025-08-17> +# Last Modified : <2026-09-09> # # Test PR # --------------------------------------------------------------- @@ -27,7 +28,7 @@ on: - "lib/**" - "Makefile" - "tool/completion/**" - - "test/zsh-completion*" + - "test/*zsh-completion*" - "pkg/deb/**" jobs: diff --git a/Makefile b/Makefile index ab8776f5..351d04a5 100755 --- a/Makefile +++ b/Makefile @@ -9,9 +9,10 @@ # | @G_I_Y # | @NewbieXvwu # | @Mikachu2333 +# | @swim233 # | # Created On : <2023-08-28> -# Last Modified : <2026-08-14> +# Last Modified : <2026-09-09> # # 请阅读 ./doc/01-开发与构建.md 来使用 # -------------------------------------------------------------- @@ -202,11 +203,8 @@ build-in-release-mode: @$(call Build-Command-For-Release, $(ReleaseMode-Target-Name)) @$(call Finished-Echo-Info, RELEASE) -# `install` depends on the release binary by filename. Build it on demand -# when installing from a clean source tree; packaging flows that already ran -# build-in-release-mode can reuse the existing artifact. -$(ReleaseMode-Target-Name): - @$(MAKE) build-in-release-mode +# 确保从干净源码树执行 install 时已构建 RELEASE mode 二进制 +$(ReleaseMode-Target-Name): build-in-release-mode build-in-ci-release-mode: @$(call Starting-Echo-Info, CI-RELEASE, $(CIReleaseMode-Target-Name)) @@ -252,10 +250,10 @@ test-fw: @./fw test-zsh-completion: - @perl ./test/zsh-completion-sync.pl + @perl ./test/verify-sync-state-of-zsh-completion @if command -v zsh >/dev/null 2>&1; then \ - zsh -n ./tool/completion/_chsrc ./test/zsh-completion.zsh ./test/zsh-completion-integration.zsh && \ - zsh -f ./test/zsh-completion.zsh && \ + zsh -n ./tool/completion/_chsrc ./test/simulate-zsh-completion.zsh ./test/zsh-completion-integration.zsh && \ + zsh -f ./test/simulate-zsh-completion.zsh && \ zsh -f ./test/zsh-completion-integration.zsh; \ elif [ "$(REQUIRE_ZSH)" = "1" ]; then \ echo "zsh is required for Zsh completion tests" >&2; \ @@ -295,6 +293,8 @@ build-deb: clean-deb: @$(MAKE) -C pkg/deb deb-clean +# 源码安装使用 Zsh 通用的 site-functions 目录;Debian 打包时会将其 +# 覆盖为 /usr/share/zsh/vendor-completions,以符合 Debian 的目录约定 ZSH_COMPLETION_DIR ?= /usr/share/zsh/site-functions install: $(ReleaseMode-Target-Name) diff --git a/test/zsh-completion.zsh b/test/simulate-zsh-completion.zsh similarity index 79% rename from test/zsh-completion.zsh rename to test/simulate-zsh-completion.zsh index 3b8662a2..2bd75615 100644 --- a/test/zsh-completion.zsh +++ b/test/simulate-zsh-completion.zsh @@ -1,10 +1,19 @@ #!/usr/bin/env zsh -# ----------------------------------------------------------------------------- +# --------------------------------------------------------------- # SPDX-License-Identifier: GPL-3.0-or-later +# --------------------------------------------------------------- +# Test File : simulate-zsh-completion.zsh +# Test Authors : @swim233 +# Contributors : Nil Null +# | +# Created On : <2026-09-08> +# Last Modified : <2026-09-09> # -# Lightweight tests for the completion state machine. Completion widgets are -# mocked so the test does not touch the user's Zsh setup. -# ----------------------------------------------------------------------------- +# 使用同名函数覆盖 Zsh 补全组件,模拟并测试 _chsrc 的状态机。 +# 测试不会读取或修改用户的 Zsh 配置。 +# +# $ zsh -f test/simulate-zsh-completion.zsh +# --------------------------------------------------------------- emulate -L zsh setopt errexit nounset pipefail extendedglob @@ -15,6 +24,7 @@ autoload -Uz _chsrc typeset -ga captured +# 模拟 _describe,将符合 PREFIX 的候选项记录到 captured _describe() { local array_name=${argv[-1]} local -a specs @@ -28,6 +38,7 @@ _describe() { done } +# 模拟 compadd,仅记录 -- 分隔符之后且符合 PREFIX 的候选项 compadd() { local after_separator=0 arg for arg in ${argv}; do @@ -41,14 +52,17 @@ compadd() { done } +# 模拟 compset -P,从 PREFIX 中移除已经输入的固定前缀 compset() { if [[ $1 == -P ]]; then PREFIX=${PREFIX#$2} fi } +# 测试无需展示补全提示信息 _message() { return 0 } +# 构造 Zsh 补全上下文并执行 _chsrc capture() { captured=() words=("${(@)argv}") @@ -59,6 +73,8 @@ capture() { assert_has() { local expected=$1 + # (I) 返回最后一个匹配元素的下标,未匹配时返回 0; + # (e) 使用字符串精确匹配,避免将候选项当作模式解析。 if (( ! ${captured[(Ie)${expected}]} )); then print -u2 -- "Expected completion '$expected' in: ${captured[*]}" return 1 @@ -67,6 +83,7 @@ assert_has() { assert_lacks() { local unexpected=$1 + # 与 assert_has 相同,使用 (Ie) 取得精确匹配元素的下标。 if (( ${captured[(Ie)${unexpected}]} )); then print -u2 -- "Unexpected completion '$unexpected' in: ${captured[*]}" return 1 diff --git a/test/zsh-completion-sync.pl b/test/verify-sync-state-of-zsh-completion similarity index 84% rename from test/zsh-completion-sync.pl rename to test/verify-sync-state-of-zsh-completion index 3ae9acd4..dd0f3063 100644 --- a/test/zsh-completion-sync.pl +++ b/test/verify-sync-state-of-zsh-completion @@ -1,11 +1,19 @@ #!/usr/bin/env perl -# ----------------------------------------------------------------------------- +# --------------------------------------------------------------- # SPDX-License-Identifier: GPL-3.0-or-later +# --------------------------------------------------------------- +# Test File : verify-sync-state-of-zsh-completion +# Test Authors : @swim233 +# Contributors : Nil Null +# | +# Created On : <2026-09-08> +# Last Modified : <2026-09-09> # -# Check that the native Zsh completion contains every alias of every dish -# registered in src/recipe/menu.c. Internal sources-only dishes are not menu -# entries and therefore must not be offered to users. -# ----------------------------------------------------------------------------- +# 校验原生 Zsh 补全中的菜品别名与 src/recipe/menu.c 注册菜单保持一致。 +# 仅供内部使用的 sources-only dish 不属于注册菜单,不应出现在补全中。 +# +# $ perl test/verify-sync-state-of-zsh-completion +# --------------------------------------------------------------- use strict; use warnings; diff --git a/test/zsh-completion-integration.zsh b/test/zsh-completion-integration.zsh index 4d96d1a5..d94b807d 100644 --- a/test/zsh-completion-integration.zsh +++ b/test/zsh-completion-integration.zsh @@ -1,11 +1,19 @@ #!/usr/bin/env zsh -# ----------------------------------------------------------------------------- +# --------------------------------------------------------------- # SPDX-License-Identifier: GPL-3.0-or-later +# --------------------------------------------------------------- +# Test File : zsh-completion-integration.zsh +# Test Authors : @swim233 +# Contributors : Nil Null +# | +# Created On : <2026-09-08> +# Last Modified : <2026-09-09> # -# Exercise real ZLE completion matching and insertion in an isolated child Zsh. -# The child uses a temporary ZDOTDIR and compinit -D, so neither user nor system -# completion configuration is read or written. -# ----------------------------------------------------------------------------- +# 在隔离的子 Zsh 中测试真实的 ZLE 补全匹配与插入行为。 +# 子进程使用临时 ZDOTDIR 和 compinit -D,不读取或修改用户及系统配置。 +# +# $ zsh -f test/zsh-completion-integration.zsh +# --------------------------------------------------------------- emulate -L zsh setopt errexit nounset pipefail extendedglob @@ -41,10 +49,9 @@ trap cleanup EXIT HUP INT TERM print -r -- 'setopt autolist beep' } > "${temp_zdotdir}/.zshrc" -# Keep the captured ZLE transcript independent of the terminal running the -# tests. Rich terminals add ANSI attributes around the automatically inserted -# suffix, which breaks the plain-text assertions below even when completion is -# correct. +# 使用 TERM=dumb,避免运行测试的终端影响捕获到的 ZLE 输出。 +# 功能较丰富的终端会给自动插入的后缀增加 ANSI 属性,即使补全行为正确, +# 也会导致下面的纯文本断言失败。 zpty -b chsrc_completion_shell env TERM=dumb ZDOTDIR="${temp_zdotdir}" zsh -d child_started=1 @@ -91,8 +98,8 @@ complete_and_expect 'chsrc -scope=p' 'chsrc -scope=project ' complete_and_expect 'chsrc set ruby fi' 'chsrc set ruby first ' complete_and_expect 'chsrc -en -no-' 'chsrc -en -no-color ' -# A completed mirror occupies the final positional slot. Pressing Tab in the -# next empty slot must not list first/upstream again. +# 镜像占据最后一个位置参数;在后续空参数处按 Tab 不应再次列出 +# first/upstream。 zpty -wn chsrc_completion_shell $'chsrc set ruby first \t' wait_for_output $'\a' collect_pending_output @@ -102,9 +109,8 @@ if [[ ${REPLY} == *upstream* ]]; then fi clear_input -# A non-empty token in the same invalid slot must not uniquely expand to -# upstream either. This catches the case even when candidate listing styles -# suppress the menu for an empty prefix. +# 在同一个无效位置输入非空内容,也不应被唯一扩展为 upstream。 +# 即使候选项样式隐藏了空前缀菜单,该断言仍可捕获重复镜像补全。 zpty -wn chsrc_completion_shell $'chsrc set ruby first u\t' wait_for_output $'\a' collect_pending_output diff --git a/tool/completion/README.md b/tool/completion/README.md new file mode 100644 index 00000000..e2755819 --- /dev/null +++ b/tool/completion/README.md @@ -0,0 +1,43 @@ + + +# Shell 补全说明 + +本目录包含 chsrc 的 Shell 补全实现: + +- `bash_completion.sh`:Bash 补全 +- `_chsrc`:原生 Zsh 补全 + +Zsh 会通过 `#compdef chsrc` 将 `_chsrc` 注册为 `chsrc` 的补全函数。补全 +函数根据 Zsh 提供的 `words`、`CURRENT` 和 `PREFIX` 判断光标位置及当前待 +补全内容。 + +## `_describe` + +`_describe` 是 Zsh 补全系统提供的辅助函数,用于批量添加带说明的候选项。 +本项目使用 `候选项:说明` 格式的数组,使补全菜单可以同时展示命令、选项 +及其用途;`-t` 参数用于为候选项指定标签,方便 Zsh 按类别应用样式。 + +## `compadd` + +`compadd` 是直接向当前补全上下文添加候选项的内建命令。本项目用它添加 +`-scope=`:`-S ''` 禁止在候选项后自动附加空格,使用户可以继续输入作用域; +`-X` 则为这一组候选项提供说明。 + +## `compset` + +`compset` 用于调整 Zsh 的补全上下文。本项目使用 `compset -P '-scope='` +把已输入的 `-scope=` 从 `PREFIX` 移到 `IPREFIX`。这样后续匹配只针对 +`default`、`project`、`user`、`system`,插入结果时仍会保留 `-scope=` +前缀。 diff --git a/tool/completion/_chsrc b/tool/completion/_chsrc index 46e4595c..9b2204a8 100644 --- a/tool/completion/_chsrc +++ b/tool/completion/_chsrc @@ -1,10 +1,17 @@ #compdef chsrc -# ----------------------------------------------------------------------------- +# --------------------------------------------------------------- # SPDX-License-Identifier: GPL-3.0-or-later +# --------------------------------------------------------------- +# Shell File : _chsrc +# File Authors : @swim233 +# Contributors : Nil Null +# | +# Created On : <2026-09-08> +# Last Modified : <2026-09-09> # -# Native Zsh completion for chsrc. Keep the dish aliases in sync with the -# registered menus in src/recipe/menu.c; test/zsh-completion-sync.pl checks it. -# ----------------------------------------------------------------------------- +# chsrc 原生 Zsh 补全。菜品别名应与 src/recipe/menu.c 中注册的菜单保持一致, +# test/verify-sync-state-of-zsh-completion 会校验两者的同步状态。 +# --------------------------------------------------------------- emulate -L zsh setopt extendedglob @@ -12,9 +19,8 @@ setopt extendedglob local -a commands options scope_options list_targets local -a dishes_lang dishes_os dishes_ware mirror_choices -# Keep menus concise by offering one canonical spelling for commands, options, -# and list targets. Accepted aliases remain in the parser states below, so an -# alias typed explicitly still receives context-aware completion. +# 命令、选项和 list 目标仅展示一种规范写法,避免补全菜单重复。 +# 解析状态仍接受所有别名,因此用户手动输入别名后仍可获得上下文补全。 commands=( 'help:show help (aliases: h, -h, -help, --help)' 'issue:show related issue links (aliases: issues, isue, i)' @@ -215,9 +221,8 @@ fi local command=${words[2]} local current_word=${words[CURRENT]} -# Locate the first positional argument before the cursor. Options belong -# between the command and dish; offering them after a dish would shift the -# dish position in chsrc's current parser. +# 查找光标前的第一个位置参数。选项应位于命令与菜品之间;如果在菜品后 +# 继续提供选项,会改变 chsrc 当前解析器所识别的菜品位置。 local dish='' local mirror='' local expect_scope=0 @@ -247,9 +252,8 @@ for (( i = 3; i < CURRENT; i++ )); do esac done -# Both spellings are accepted by the CLI: "-scope=value" and -# "-scope value". Complete only scope values while one is expected, and do -# not suggest an option after the dish positional argument. +# CLI 同时接受 "-scope=value" 和 "-scope value"。等待作用域时只补全 +# 作用域值,并且不在菜品位置参数后继续建议选项。 if [[ -z ${dish} && ${current_word} == -scope=* ]]; then compset -P '-scope=' _describe -t scopes 'scope' scope_options From f4802c0eb3f24a022d23f77f0aa6f58b77d6e559 Mon Sep 17 00:00:00 2001 From: TheSw1m Date: Wed, 9 Sep 2026 15:00:17 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B4=A1=E7=8C=AE?= =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chsrc-main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/chsrc-main.c b/src/chsrc-main.c index ec9679ed..c61716ab 100644 --- a/src/chsrc-main.c +++ b/src/chsrc-main.c @@ -82,6 +82,7 @@ chsrc_register_chefs_and_sauciers () chef_register_contributor ("@techoc", "Rui Yang", "techoc@foxmail.com", NULL); chef_register_contributor ("@BingChunMoLi", "BingChunMoLi", "bingchunmoli@bingchunmoli.com", NULL); chef_register_contributor ("@wcbing", "wcbing", "i@wcbing.top", NULL); + chef_register_contributor ("@swim233", "TheSw1m", "swim853279614@163.com", NULL); // 该注释下一行的用户 ID 为 Gitee ID chef_register_contributor ("@hezonglun", "HeZongLun", "hezonglun123456@outlook.com", NULL); chef_register_contributor ("@Young-Lord", "LY", "ly-niko@qq.com", NULL); From 2b2f9633bd7b2e91020e61a2f1554a0da9b9f23b Mon Sep 17 00:00:00 2001 From: TheSw1m Date: Wed, 9 Sep 2026 15:27:02 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=B4=A1=E7=8C=AE?= =?UTF-8?q?=E8=80=85=E7=BD=B2=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/deb/debian/rules | 2 +- test/simulate-zsh-completion.zsh | 2 +- test/verify-sync-state-of-zsh-completion | 2 +- test/zsh-completion-integration.zsh | 2 +- tool/completion/README.md | 2 +- tool/completion/_chsrc | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/deb/debian/rules b/pkg/deb/debian/rules index d9e2e7da..e0aeac4d 100755 --- a/pkg/deb/debian/rules +++ b/pkg/deb/debian/rules @@ -5,7 +5,7 @@ # Build File : rules # File Authors : sanchuanhehe # | 曾奥然 -# Contributors : Nil Null +# Contributors : TheSw1m # | # Created On : <2025-06-14> # Last Modified : <2025-06-20> diff --git a/test/simulate-zsh-completion.zsh b/test/simulate-zsh-completion.zsh index 2bd75615..aaebdb7b 100644 --- a/test/simulate-zsh-completion.zsh +++ b/test/simulate-zsh-completion.zsh @@ -4,7 +4,7 @@ # --------------------------------------------------------------- # Test File : simulate-zsh-completion.zsh # Test Authors : @swim233 -# Contributors : Nil Null +# Contributors : TheSw1m # | # Created On : <2026-09-08> # Last Modified : <2026-09-09> diff --git a/test/verify-sync-state-of-zsh-completion b/test/verify-sync-state-of-zsh-completion index dd0f3063..859cbe7d 100644 --- a/test/verify-sync-state-of-zsh-completion +++ b/test/verify-sync-state-of-zsh-completion @@ -4,7 +4,7 @@ # --------------------------------------------------------------- # Test File : verify-sync-state-of-zsh-completion # Test Authors : @swim233 -# Contributors : Nil Null +# Contributors : TheSw1m # | # Created On : <2026-09-08> # Last Modified : <2026-09-09> diff --git a/test/zsh-completion-integration.zsh b/test/zsh-completion-integration.zsh index d94b807d..ecc5f3f3 100644 --- a/test/zsh-completion-integration.zsh +++ b/test/zsh-completion-integration.zsh @@ -4,7 +4,7 @@ # --------------------------------------------------------------- # Test File : zsh-completion-integration.zsh # Test Authors : @swim233 -# Contributors : Nil Null +# Contributors : TheSw1m # | # Created On : <2026-09-08> # Last Modified : <2026-09-09> diff --git a/tool/completion/README.md b/tool/completion/README.md index e2755819..bc51baff 100644 --- a/tool/completion/README.md +++ b/tool/completion/README.md @@ -4,7 +4,7 @@ ! Doc Type : Markdown ! Doc Name : (Shell Completion Introduction).md ! Doc Authors : @swim233 - ! Contributors : Nil Null + ! Contributors : TheSw1m ! | ! Created On : <2026-09-09> ! Last Modified : <2026-09-09> diff --git a/tool/completion/_chsrc b/tool/completion/_chsrc index 9b2204a8..40047a53 100644 --- a/tool/completion/_chsrc +++ b/tool/completion/_chsrc @@ -4,7 +4,7 @@ # --------------------------------------------------------------- # Shell File : _chsrc # File Authors : @swim233 -# Contributors : Nil Null +# Contributors : TheSw1m # | # Created On : <2026-09-08> # Last Modified : <2026-09-09> From e08139911f8f47d7f23661e2ec15469e5f7c4cb3 Mon Sep 17 00:00:00 2001 From: TheSw1m Date: Wed, 9 Sep 2026 15:30:46 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E6=92=A4=E9=94=80=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E7=BD=B2=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/deb/debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/deb/debian/rules b/pkg/deb/debian/rules index e0aeac4d..d9e2e7da 100755 --- a/pkg/deb/debian/rules +++ b/pkg/deb/debian/rules @@ -5,7 +5,7 @@ # Build File : rules # File Authors : sanchuanhehe # | 曾奥然 -# Contributors : TheSw1m +# Contributors : Nil Null # | # Created On : <2025-06-14> # Last Modified : <2025-06-20>