Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[PATCH] Completion: Improve _bind_addresses, _php



The PHP function hasn't been updated in many years, so i tore it up a little:

* Removed checks dealing with PHP 4 (long dead now)

* Added several missing options, particularly ones related to reflection and the
  built-in Web server

* Added helper functions to complete some of the above

* Fixed missing */+/= on several option specs

* Removed the functionality that tried to use ld.so.conf to get extension paths.
  This could work, if it was updated to account for the fact that ld.so.conf
  itself often contains only an include statement, but, as far as i've seen,
  nobody puts their PHP extension path in ld.so.conf* anyway, so

* Drastically improved option-exclusivity accuracy. (Making this as accurate as
  possible is functionally pleasing to me, but let me know if i'm abusing groups
  and/or readability too much by doing so...)

It's common when using `php -S` to specify either 0.0.0.0 or localhost as the
listen address, which was something that _bind_addresses couldn't accommodate.
Someone on IRC suggested that i add that functionality directly to the function,
and i'd thought about doing that in the past anyway, so that suited me

I am a bit anxious about giving utility functions short options because compadd
has already used up so many of them. I picked ones that don't conflict, so it's
not a big deal i guess, but i'm curious whether there are any conventions or
expectations when it comes to that kind of thing

dana


diff --git a/Completion/Unix/Type/_bind_addresses b/Completion/Unix/Type/_bind_addresses
index 6042eaf1e..d12727800 100644
--- a/Completion/Unix/Type/_bind_addresses
+++ b/Completion/Unix/Type/_bind_addresses
@@ -1,9 +1,21 @@
 #autoload
 
-# @todo In the future it might be useful to have this function take a glob or
-# similar to filter out loop-back addresses, only return IPv4/6, etc.
+# Complete locally bound IP addresses
+#
+# Options:
+#   -0  Return also `0.0.0.0` and `::`
+#   -4  Return only IPv4 addresses
+#   -6  Return only IPv6 addresses
+#   -b  Return IPv6 addresses in brackets (for use with port numbers)
+#   -h  Return also `localhost`
+#   -L  Exclude loop-back addresses (`127.0.0.0/8` and `::1`)
+#   -K  Exclude link-local addresses (`169.254.0.0/16` and `fe80::/10`)
 
+local MATCH MBEGIN MEND
 local -a expl tmp cmd=( ifconfig -a )
+local -A opts
+
+zparseopts -A opts -D -E -- 0 4 6 b h L K
 
 # A lot of Linux systems have ifconfig, but this is probably safer (and it's
 # parsed the same way)
@@ -14,4 +26,25 @@ tmp=( ${(@M)tmp##(|[[:space:]]##)inet(|6)(|:)[[:space:]]*} )
 tmp=( ${(@)tmp#*inet(|6)(|:)[[:space:]]##} )
 tmp=( ${(@)tmp%%[^0-9A-Fa-f:.]*} )
 
+# The order of operations here is significant
+(( $+opts[-0] )) && tmp+=( 0.0.0.0 :: )
+
+if (( $+opts[-6] )); then
+  tmp=( ${(@M)tmp:#*:*} )
+elif (( $+opts[-4] )); then
+  tmp=( ${(@)tmp:#*:*} )
+fi
+
+(( $+opts[-L] )) && {
+  tmp=( ${(@)tmp:#127.*} )
+  tmp=( ${(@)tmp:#[0:]##:1} )
+}
+(( $+opts[-K] )) && {
+  tmp=( ${(@)tmp:#169.254.*} )
+  tmp=( ${(@)tmp:#(#i)fe[89ab]?:*} )
+}
+
+(( $+opts[-b] )) && tmp=( ${(@)tmp/(#m)*:*/\[$MATCH\]} )
+(( $+opts[-h] )) && tmp+=( localhost )
+
 _wanted bind-addresses expl 'bind address' compadd -a "$@" - tmp

diff --git a/Completion/Unix/Command/_php b/Completion/Unix/Command/_php
index d03f3395e..2302216c1 100644
--- a/Completion/Unix/Command/_php
+++ b/Completion/Unix/Command/_php
@@ -1,71 +1,177 @@
-#compdef php
+#compdef php -P php[0-9.-]
 
-# PHP 5.0.4  (cli)
-# PHP 4.3.11 (cli)
+# Notes:
+# - We make no distinction between internal and user functions
+# - We don't complete CGI options, which are rarely used interactively
+# - Exclusivity on some of the miscellaneous options isn't very accurate
+# - @todo Arguments to an -f script aren't completed accurately -- we need to
+#   massage words/CURRENT so that the -f arg becomes words[1], but if we just
+#   leave it at that the output will break if the script has any options of its
+#   own. We would want to complete script options only following `--`, as in
+#   `php -f /bin/foo -- -<TAB>`
 
-local curcontext="$curcontext" line state expl
-typeset -A opt_args
+# Complete PHP class names
+(( $+functions[_php_classes] )) ||
+_php_classes() {
+  local cmd
+  local -a tmp
 
-local -a args
-local exclusions php_suffix
+  cmd='foreach ( get_declared_classes() as $c ) { echo "$c\n"; }'
+  tmp=( ${(f)"$( _call_program classes $words[1] -r ${(q)cmd} )"} )
 
-zstyle -s ":completion:${curcontext}:" suffixes php_suffix '|' || php_suffix='php|phar'
-local php_files=":PHP file:_files -g '*.($php_suffix)(-.)'"
+  _wanted -x classes expl 'PHP class' compadd -a "$@" - tmp
+}
+
+# Complete PHP extensions/module names; use --zend for Zend extensions only
+(( $+functions[_php_extensions] )) ||
+_php_extensions() {
+  local idx
+  local -a expl zend tmp
+
+  zparseopts -a zend -D -E -- -zend
+
+  # `php -m` lists all extensions under two sections called '[PHP Modules]' and
+  # '[Zend Modules]'. An extension can (but won't necessarily) exist under both
+  # of these at the same time
+  tmp=( ${(f)"$( _call_program extensions $words[1] -m )"} )
+  idx=${tmp[(i)\[Zend Modules\]]}
+
+  # Get only Zend extensions (for --rz)
+  if (( $#zend )); then
+    tmp=( ${(@)tmp[(idx+1),-1]} )
+  # Get PHP extensions (for everything else)
+  else
+    tmp=( ${(@)tmp[2,(idx-1)]} )
+  fi
+
+  _wanted -x extensions expl 'PHP extension' compadd -a "$@" - tmp
+}
+
+# Complete PHP function names
+(( $+functions[_php_functions] )) ||
+_php_functions() {
+  local cmd
+  local -a expl tmp
+
+  cmd='
+    foreach ( get_defined_functions() as $a ) {
+      foreach ( $a as $f ) {
+        echo "$f\n";
+      }
+    }
+  '
+  tmp=( ${(f)"$( _call_program functions $words[1] -r ${(q)cmd} )"} )
+
+  _wanted -x functions expl 'PHP function' compadd -a "$@" - tmp
+}
+
+_php() {
+  local curcontext=$curcontext php_suffix php_files ret=1
+  local -a context expl line state state_descr args
+  local -A opt_args
+
+  zstyle -s ":completion:${curcontext}:" suffixes php_suffix '|' ||
+  php_suffix='php|php5|phar'
+
+  php_files=":PHP file:_files -g '*.($php_suffix)(#q-.)'"
 
-if _pick_variant php5=PHP\ 5 php4 --version; then
-  exclusions="-B --process-begin -R --process-code -F --process-file -E --process-end"
   args=(
-    '(-B --process-begin -f --file -r --run 1)'{-B,--process-begin}'[run specified PHP code before processing input lines]:PHP code:'
-    '(-R --process-code -F --process-file -f --file -r --run 1)'{-R,--process-code}'[run specified PHP code for every input line]:PHP code:'
-    '(-F --process-file -R --process-code -f --file -r --run 1)'{-F,--process-file}'[parse and execute specified file for every input line]'$php_files
-    '(-E --process-end -f --file -r --run 1)'{-E,--process-end}'[run specified PHP code after processing all input lines]:PHP code:'
-    '(-H --hide-args)'{-H,--hide-args}'[hide any passed arguments from external tools]'
+    + mc # Misc. options
+    '(-a --interactive)'{-a,--interactive}'[run interactively]'
+    '*'{-d+,--define=}'[define INI directive]: :->directive'
+    '(-e --profile-info)'{-e,--profile-info}'[generate extended information for debugger/profiler]'
+    '(-H --hide-args)'{-H,--hide-args}'[hide script name and arguments from external tools]'
+    '(fi im pb pf rf rn sc sv *)--ini[display configured INI paths]'
+    # Note: PHP does not automatically prepend extension_dir to extension file
+    # names (the way it does when parsing the INI file) at the command line
+    '*'{-z+,--zend-extension=}'[load specified Zend extension]:Zend extension:_files -g "*.so(|.*)(#q-.)"'
+
+    + '(fi)' # File arguments
+    "(im pb pf rf sv)"{-f+,--file=}'[parse and/or execute specified file]'$php_files
+    '(-)1'$php_files
+
+    + '(hv)' # Help/version options; kept separate by convention
+    '(- 1 *)'{-h,--help}'[display help information]'
+    '(- 1 *)'{-v,--version}'[display version information]'
+    '!(- 1 *)'{-\?,-\\\?,--usage}
+
+    + '(im)' # Info/module options (exclusive with everything but -c/-n)
+    '(fi mc pb pf rf rn sc sv *)'{-i,--info}'[display configuration information (phpinfo())]'
+    '(fi mc pb pf rf rn sc sv *)'{-m,--modules}'[display installed extensions]'
+
+    + '(in)' # php.ini set/disable options (unrelated to --ini!)
+    {-c+,--php-ini=}'[specify php.ini or containing directory]:INI file or directory:_files -g "*.ini(-.)"'
+    {-n,--no-php-ini}'[ignore php.ini]'
+
+    + '(pb)' # Input-processing begin/end options
+    '(-B --process-begin fi im rf rn sc sv)'{-B+,--process-begin=}'[run specified PHP code before processing input lines]:PHP code:'
+    '(-E --process-end fi im rf rn sc sv)'{-E+,--process-end=}'[run specified PHP code after processing input lines]:PHP code:'
+
+    + '(pf)' # Input-processing options
+    '(fi im rf rn sc sv)'{-R+,--process-code=}'[run specified PHP code for every input line]:PHP code:'
+    '(fi im rf rn sc sv)'{-F+,--process-file=}'[parse and execute specified file for every input line]'$php_files
+
+    + '(rf)' # Reflection options
+    '(fi im rn pb pf sc sv *)'{--rc=,--rclass=}'[display information about specified class]: :_php_classes'
+    '(fi im rn pb pf sc sv *)'{--re=,--rextension=}'[display information about specified extension]: :_php_extensions'
+    '(fi im rn pb pf sc sv *)'{--rf=,--rfunction=}'[display information about specified function]: :_php_functions'
+    '(fi im rn pb pf sc sv *)'{--ri=,--rextinfo=}'[display configuration information about specified extension]: :_php_extensions'
+    '(fi im rn pb pf sc sv *)'{--rz=,--rzendextension=}'[display information about specified Zend extension]: :_php_extensions --zend'
+
+    + '(rn)' # Run-script options
+    "(fi im pb pf rf sc sv)"{-r+,--run=}'[run specified PHP code]:PHP code:'
+
+    + '(sc)' # Source-checking/formatting options
+    '(im pb pf rf rn sv *)'{-l,--syntax-check}'[check syntax only (lint)]'
+    '(im pb pf rf rn sv *)'{-s,--syntax-highlight}'[display HTML syntax-highlighted source]'
+    '!(im pb pf rf rn sv *)--syntax-highlighting'
+    '(im pb pf rf rn sv *)'{-w,--strip}'[display source stripped of comments and whitespace]'
+
+    + sv # Built-in Web server options
+    '(-S --server fi im pb pf rf rn sc *)'{-S+,--server=}'[start Web server on specified address/port]: :->server'
+    '(-t --docroot fi im pb pf rf rn sc *)'{-t+,--docroot=}'[specify Web-server document root]:document root:_directories'
+
+    + ar # Script-argument operands
+    '(-)*:: :->argument'
   )
-fi
-
-args+=(
-  '(-a --interactive)'{-a,--interactive}'[run interactively]'
-  '(-c --php-ini -n --no-php-ini)'{-c,--php-ini}'[look for php.ini file in the specified directory]:INI file or directory:_files -g "*.ini(-.)"'
-  '(-c --php-ini -n --no-php-ini)'{-n,--no-php-ini}'[no php.ini file will be used]'
-  '(-d --define)'{-d,--define}'[define INI entry]:configuration directive:->directive'
-  '(-e --profile-info)'{-e,--profile-info}'[generate extended information for debugger/profiler]'
-  "(-f --file -r --run $exclusions 1)"{-f,--file}'[parse specified file]'$php_files
-  '(- 1 *)'{-h,--help}'[display help information]'
-  '(- 1 *)'{-i,--info}'[PHP information]'
-  '(-   *)'{-l,--syntax-check}'[syntax check only (lint)]'
-  '(- 1 *)'{-m,--modules}'[show compiled in modules]'
-  "(-r --run -f --file $exclusions -l --syntax-check -s --syntax-highlight -w --strip 1)"{-r,--run}'[run the specified PHP code without using script tags <?..?>]:PHP code:'
-  '(- 1 *)'{-s,--syntax-highlight}'[display colour syntax highlighted source]'
-  '(- 1 *)'{-v,--version}'[display version information]'
-  '(-   *)'{-w,--strip}'[display source with stripped comments and whitespace]'
-  '(-z --zend-extension)'{-z,--zend-extension}'[load specified Zend extension]:extension file:->extension'
-  '(-)1'$php_files
-  '(-)*::script argument: _normal'
-)
-
-_arguments -C -s -S "$args[@]" && return 0
-
-case $state in
-  directive)
-    local -a directives suf
-    local code='foreach (ini_get_all() as $k => $v) { echo "$k\n"; }'
-    directives=( $(_call_program directives $words[1] -r ${(q)code} 2>/dev/null) )
-    if compset -P 1 '*='; then
-      _default && return 0
-    else
-      compset -S '=*' || suf=( -qS '=' )
-      _wanted directives expl 'configuration directive' compadd "$suf[@]" -a directives && return 0
-    fi
-  ;;
-  extension)
-    local -a paths
-    if [[ -r /etc/ld.so.conf ]]; then
-      paths=( ${(f)"$(</etc/ld.so.conf)"} )
-    else
-      paths=(.)
-    fi
-    _wanted extensions expl 'zend extension' _files -W paths -g "*.so(|.*)(-.)" && return 0
-  ;;
-esac
-
-return 1
+
+  _arguments -C -s -S : $args && ret=0
+
+  case $state in
+    argument)
+      if [[ -n $opt_args[(i)(pb|pf|rn)-*] ]]; then
+        _description files expl 'script argument'
+        _files "${(@)expl}" && ret=0
+      else
+        _normal && ret=0
+      fi
+      ;;
+    directive)
+      local -a directives suf
+      local code='foreach (ini_get_all() as $k => $v) { echo "$k\n"; }'
+      directives=( ${(f)"$(
+        _call_program directives $words[1] -r ${(q)code}
+      )"} )
+      if compset -P 1 '*='; then
+        _default && ret=0
+      else
+        compset -S '=*' || suf=( -qS '=' )
+        _wanted directives expl 'INI directive' \
+          compadd "$suf[@]" -a directives && ret=0
+      fi
+      ;;
+    server)
+      if compset -P '*:'; then
+        _wanted -2V port-numbers expl 'port number' \
+          compadd 80 81 443 591 8000 8001 8008 8080 8443 && ret=0
+        ret=0
+      else
+        _wanted hosts expl 'local address' _bind_addresses -0bh -qS: && ret=0
+      fi
+      ;;
+  esac
+
+  return ret
+}
+
+_php "$@"



Messages sorted by: Reverse Date, Date, Thread, Author