From 7568bbd9f57e0d7d90dd4a15abffc7e8a269f88f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc=20Cornell=C3=A0?= <hello@mcornella.com>
Date: Tue, 28 Dec 2021 20:12:33 +0100
Subject: [PATCH] feat(ng): get `ng` completion from parsed help output
 (#10294)

Co-authored-by: Yannick Galatol <yannick.galatol@gmail.com>
---
 plugins/ng/_ng           | 56 +++++++++++++++++++++++++++++
 plugins/ng/ng.plugin.zsh | 78 ----------------------------------------
 2 files changed, 56 insertions(+), 78 deletions(-)
 create mode 100644 plugins/ng/_ng
 delete mode 100644 plugins/ng/ng.plugin.zsh

diff --git a/plugins/ng/_ng b/plugins/ng/_ng
new file mode 100644
index 000000000..9c96cf7e3
--- /dev/null
+++ b/plugins/ng/_ng
@@ -0,0 +1,56 @@
+#compdef ng
+
+setopt localoptions extendedglob
+
+if (( CURRENT == 2 )); then
+  local -a cmds alias
+  # Sample output (ng help):
+  # Available Commands:
+  #   add Adds support for an external library to your project.
+  for line in ${(@f)"$(ng help 2>/dev/null | sed -n '/^  /p')"}; do
+    if [[ "$line" =~ '^  ([^ ]+) \(([^)]+)\) (.*)$' ]]; then
+      alias=(${match[1]} ${(s:, :)match[2]})
+      cmds+=(${^alias}:"${match[3]//:/}")
+    elif [[ "$line" =~ '^  ([^ ]+) (.*)$' ]]; then
+      cmds+=("${match[1]}:${match[2]//:/}")
+    fi
+  done
+  _describe commands cmds && return 0
+elif (( CURRENT == 3 )); then
+  local -a flags args
+  local section description
+  # Sample output (ng build --help):
+  # --configuration (-c)
+  #   One or more named builder configurations as a comma-separated list as specified in the "configurations" section of angular.json.
+  #   The builder uses the named configurations to run the given target.
+  #   For more information, see https://angular.io/guide/workspace-config#alternate-build-configurations.
+  # Prefix --flags with literal \0, and split on that to get each flag section
+  for section in ${(s:\0:)"$(ng ${words[2]} --help 2>/dev/null | sed -e '1,/^options/ d;s/^  --/\\0--/')"}; do
+    # Split by newline and discard extra description lines (lines > 2)
+    for args description in ${${(@f)section}[1,2]}; do
+      args=(${(s: :)${${args%% #}## #}//[(),]/})
+      description=${${description%% #}## #}
+      flags+=(${^args}":$description")
+    done
+  done
+  _describe flags flags
+
+  case "$words[2]" in
+  b|build|l|lint|t|test)
+    # Sample output (ng config projects):
+    # {
+    #   "angular-project-1": {
+    #     ...
+    #   },
+    #   "angular-project-2": {
+    #     ...
+    #   }
+    # }
+    # In abscence of a proper JSON parser, just grab the lines with
+    # a 2-space indentation and only the stuff inside quotes
+    local -a projects
+    projects=(${(@f)"$(ng config projects 2>/dev/null | sed -n 's/^  "\([^"]\+\)".*$/\1/p')"})
+    _describe projects projects
+    ;;
+  esac
+fi
diff --git a/plugins/ng/ng.plugin.zsh b/plugins/ng/ng.plugin.zsh
deleted file mode 100644
index 44102e2a6..000000000
--- a/plugins/ng/ng.plugin.zsh
+++ /dev/null
@@ -1,78 +0,0 @@
-ng_opts='addon asset-sizes b build completion d destroy doc e2e g generate get github-pages:deploy gh-pages:deploy h help i init install lint make-this-awesome new s serve server set t test update v version -h --help'
-
-_ng_completion () {
-  local words cword opts
-  read -Ac words
-  read -cn cword
-  let cword-=1
-
-  case $words[cword] in
-    addon )
-      opts='-b --blueprint -d -dir --directory --dry-run -sb --skip-bower -sg --skip-git -sn --skip-npm -v --verbose'
-      ;;
-
-    asset-sizes )
-      opts='-o --output-path'
-      ;;
-
-    b | build )
-      opts='--environment --output-path --suppress-sizes --target --watch --watcher -dev -e -prod'
-      ;;
-
-    d | destroy )
-      opts='--dry-run --verbose --pod --classic --dummy --in-repo --in-repo-addon -d -v -p -c -dum -id -ir'
-      ;;
-
-    g | generate )
-      opts='class component directive enum module pipe route service --generate -d --dry-run --verbose -v --pod -p --classic -c --dummy -dum -id --in-repo --in-repo-addon -ir'
-      ;;
-
-    gh-pages:deploy | github-pages:deploy )
-      opts='--environment --gh-token --gh-username --skip-build --user-page --message'
-      ;;
-
-    h | help | -h | --help)
-      opts='--json --verbose -v'
-      ;;
-
-    init )
-      opts='--blueprint --dry-run --link-cli --mobile --name --prefix --skip-bower --skip-npm --source-dir --style --verbose -b -d -lc -n -p -sb -sd -sn -v'
-      ;;
-
-    new )
-      opts='--blueprint --directory --dry-run --link-cli --mobile --prefix --skip-bower --skip-git --skip-npm --source-dir --style --verbose -b -d -dir -lc -p -sb -sd -sg -sn -v'
-      ;;
-
-    s | serve | server )
-      opts='--environment --host --insecure-proxy --inspr --live-reload --live-reload-base-url --live-reload-host --live-reload-live-css --live-reload-port --output-path --port --proxy --ssl --ssl-cert --ssl-key --target --watcher -H -dev -e -lr -lrbu -lrh -lrp -op -out -p -pr -prod -pxy -t -w'
-      ;;
-
-    set )
-      opts='--global -g'
-      ;;
-
-    t | test )
-      opts='--browsers --colors --config-file --environment --filter --host --launch --log-level --module --path --port --query --reporter --server --silent --test-page --test-port --watch -H -c -cf -e -f -m -r -s -tp -w'
-      ;;
-      
-    update )
-      opts='--all --dryRun --force --from --migrate-only --next --registry --to -d'
-      ;;
-
-    v | version )
-      opts='--verbose'
-      ;;
-
-    ng )
-      opts=$ng_opts
-      ;;
-
-    * )
-      opts=''
-      ;;
-  esac
-
-  reply=(${=opts})
-}
-
-compctl -K _ng_completion ng