Subject: bug#15362: 24.1; pcomplete unnecessarily restricts tab-completion entry points

From: Vivek Dasmohapatra <[email-masked]>
Date: Fri, 13 Sep 2013 03:15:58 +0100

In an erc buffer, I tried to tab-complete a nick: The initial tab completion
showed many possibilities - I typed an extra character, which should have
limited it to a small enough number for tab-completion-cycling to kick in...
but instead TAB just unconditionally completed to the first of the remaining
candidates, and never offered me the chance to pick any of the others.

It turns out that pcomplete assumes that the interactive entry point
(activating command) can only ever be one of a handful of commands,
and specifically checks for those when deciding whether to enter the
code path that starts cycling through the remaining completions.

So modes that use pcomplete but define their own commands for tab completion
lose the ability to use pcomplete cycling unless they have
a way of adding themselves to the list of entry points that pcomplete
knows about.

In GNU Emacs 24.1.1 (x86_64-pc-linux-gnu, GTK+ Version 2.24.10)
 of 2012-08-23 on noise, modified by Debian
Windowing system distributor `The X.Org Foundation', version 11.0.11204000
Configured using:
 `configure '--build' 'x86_64-linux-gnu' '--build' 'x86_64-linux-gnu'
 '--prefix=/usr' '--sharedstatedir=/var/lib' '--libexecdir=/usr/lib'
 '--localstatedir=/var/lib' '--infodir=/usr/share/info'
 '--mandir=/usr/share/man' '--with-pop=yes'
 '--enable-locallisppath=/etc/emacs24:/etc/emacs:/usr/local/share/emacs/24.1/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/24.1/site-lisp:/usr/share/emacs/site-lisp'
 '--with-crt-dir=/usr/lib/x86_64-linux-gnu' '--with-x=yes'
 '--with-x-toolkit=gtk' '--with-toolkit-scroll-bars'
 'build_alias=x86_64-linux-gnu' 'CFLAGS=-g -O2 -fstack-protector
 --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wall'
 'CPPFLAGS=-D_FORTIFY_SOURCE=2''

Important settings:
  value of $LC_ALL: en_GB.UTF-8
  value of $LC_COLLATE: nil
  value of $LC_CTYPE: nil
  value of $LC_MESSAGES: nil
  value of $LC_MONETARY: nil
  value of $LC_NUMERIC: nil
  value of $LC_TIME: nil
  value of $LANG: en_GB.UTF-8
  value of $XMODIFIERS: nil
  locale-coding-system: utf-8-unix
  default enable-multibyte-characters: t

Major mode: Diff

Minor modes in effect:
  diff-auto-refine-mode: t
  shell-dirtrack-mode: t
  erc-truncate-mode: t
  erc-track-mode: t
  erc-track-minor-mode: t
  erc-services-mode: t
  erc-ring-mode: t
  erc-networks-mode: t
  erc-netsplit-mode: t
  erc-match-mode: t
  erc-log-mode: t
  erc-pcomplete-mode: t
  erc-button-mode: t
  erc-fill-mode: t
  erc-stamp-mode: t
  erc-autojoin-mode: t
  erc-move-to-prompt-mode: t
  auto-image-file-mode: t
  iswitchb-mode: t
  tooltip-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t

Recent input:
<left> m a k e s SPC t h i n s g SPC <M-backspace> 
<M-backspace> <M-backspace> <M-backspace> <M-backspace> 
<down> <backspace> <up> <down> <backspace> <return> 
<up> <return> A t t a c h e d SPC p a t c h SPC h a 
s SPC b e e n SPC i n SPC u s e SPC h e r e SPC <M-backspace> 
<M-backspace> <M-backspace> <M-backspace> <M-backspace> 
f i x e s SPC t h e SPC p r o b l e m SPC f o r SPC 
m e SPC ( 0 <left> i n SPC e r c SPC <right> <backspace> 
) <left> <left> , SPC a n y w a y , SPC o t h e r SPC 
p c o m p l e t e <return> c a l l e r s <right> w 
o u l d SPC r e w q <backspace> <backspace> q u i r 
e SPC a SPC s i m i l a r SPC s m a l SPC f i x <left> 
<left> <left> <left> <left> <left> <left> <left> <left> 
<left> <left> <left> <left> <left> <left> <left> <left> 
<left> <left> <left> <left> <left> <left> <left> <left> 
<left> <left> <left> <left> <left> <left> <left> <left> 
<left> <left> <left> <left> <left> <left> <left> <left> 
<left> <left> <left> <left> <left> <left> <left> <left> 
<left> <left> <left> <left> <left> <left> <left> <left> 
<backspace> : <down> <down> C-c C-c <up> <return> <tab> 
s m <tab> <return> <select-window> <select-window> 
<help-echo> <select-window> l o c <tab> <backspace> 
M-/ <M-backspace> <M-backspace> <M-backspace> <select-window> 
<down-mouse-1> <mouse-movement> <mouse-movement> <drag-mouse-1> 
<select-window> <help-echo> <help-echo> <select-window> 
l o c a l h o s t <return> <select-window> <help-echo> 
<help-echo> <help-echo> <help-echo> <help-echo> <select-window> 
<help-echo> <down-mouse-1> <mouse-movement> <mouse-1> 
M-x r e p o r t <tab> <return>

Recent messages:
Sending...
Sending via mail...
Scanning for dabbrevs...done
Saving file /home/vivek/.emacs...
Wrote /home/vivek/.emacs [2 times]
Saving file /home/vivek/.emacs...
Wrote /home/vivek/.emacs [2 times]
Sending email 
Sending email done
Sending...done

Load-path shadows:
/usr/share/emacs/24.1/site-lisp/debian-startup hides /usr/share/emacs/site-lisp/debian-startup
/usr/share/emacs24/site-lisp/dictionaries-common/flyspell hides /usr/share/emacs/24.1/lisp/textmodes/flyspell
/usr/share/emacs24/site-lisp/dictionaries-common/ispell hides /usr/share/emacs/24.1/lisp/textmodes/ispell
/home/vivek/.emacs.d/lib/erc-identd hides /usr/share/emacs/24.1/lisp/erc/erc-identd
/home/vivek/.emacs.d/lib/erc-list hides /usr/share/emacs/24.1/lisp/erc/erc-list
/home/vivek/.emacs.d/lib/erc-sound hides /usr/share/emacs/24.1/lisp/erc/erc-sound
/home/vivek/.emacs.d/lib/erc-ring hides /usr/share/emacs/24.1/lisp/erc/erc-ring
/home/vivek/.emacs.d/lib/erc-spelling hides /usr/share/emacs/24.1/lisp/erc/erc-spelling
/home/vivek/.emacs.d/lib/erc-speedbar hides /usr/share/emacs/24.1/lisp/erc/erc-speedbar
/home/vivek/.emacs.d/lib/erc-ibuffer hides /usr/share/emacs/24.1/lisp/erc/erc-ibuffer
/home/vivek/.emacs.d/lib/erc-capab hides /usr/share/emacs/24.1/lisp/erc/erc-capab
/home/vivek/.emacs.d/lib/erc-pcomplete hides /usr/share/emacs/24.1/lisp/erc/erc-pcomplete
/home/vivek/.emacs.d/lib/erc-networks hides /usr/share/emacs/24.1/lisp/erc/erc-networks
/home/vivek/.emacs.d/lib/erc-ezbounce hides /usr/share/emacs/24.1/lisp/erc/erc-ezbounce
/home/vivek/.emacs.d/lib/erc-page hides /usr/share/emacs/24.1/lisp/erc/erc-page
/home/vivek/.emacs.d/lib/erc-menu hides /usr/share/emacs/24.1/lisp/erc/erc-menu
/home/vivek/.emacs.d/lib/erc-replace hides /usr/share/emacs/24.1/lisp/erc/erc-replace
/home/vivek/.emacs.d/lib/erc-imenu hides /usr/share/emacs/24.1/lisp/erc/erc-imenu
/home/vivek/.emacs.d/lib/erc-dcc hides /usr/share/emacs/24.1/lisp/erc/erc-dcc
/home/vivek/.emacs.d/lib/erc-backend hides /usr/share/emacs/24.1/lisp/erc/erc-backend
/home/vivek/.emacs.d/lib/erc-stamp hides /usr/share/emacs/24.1/lisp/erc/erc-stamp
/home/vivek/.emacs.d/lib/erc-services hides /usr/share/emacs/24.1/lisp/erc/erc-services
/home/vivek/.emacs.d/lib/erc-compat hides /usr/share/emacs/24.1/lisp/erc/erc-compat
/home/vivek/.emacs.d/lib/erc-autoaway hides /usr/share/emacs/24.1/lisp/erc/erc-autoaway
/home/vivek/.emacs.d/lib/erc-goodies hides /usr/share/emacs/24.1/lisp/erc/erc-goodies
/home/vivek/.emacs.d/lib/erc-notify hides /usr/share/emacs/24.1/lisp/erc/erc-notify
/home/vivek/.emacs.d/lib/erc-truncate hides /usr/share/emacs/24.1/lisp/erc/erc-truncate
/home/vivek/.emacs.d/lib/erc hides /usr/share/emacs/24.1/lisp/erc/erc
/home/vivek/.emacs.d/lib/erc-button hides /usr/share/emacs/24.1/lisp/erc/erc-button
/home/vivek/.emacs.d/lib/erc-log hides /usr/share/emacs/24.1/lisp/erc/erc-log
/home/vivek/.emacs.d/lib/erc-xdcc hides /usr/share/emacs/24.1/lisp/erc/erc-xdcc
/home/vivek/.emacs.d/lib/erc-match hides /usr/share/emacs/24.1/lisp/erc/erc-match
/home/vivek/.emacs.d/lib/erc-lang hides /usr/share/emacs/24.1/lisp/erc/erc-lang
/home/vivek/.emacs.d/lib/erc-netsplit hides /usr/share/emacs/24.1/lisp/erc/erc-netsplit
/home/vivek/.emacs.d/lib/erc-fill hides /usr/share/emacs/24.1/lisp/erc/erc-fill
/home/vivek/.emacs.d/lib/erc-track hides /usr/share/emacs/24.1/lisp/erc/erc-track
/home/vivek/.emacs.d/lib/erc-join hides /usr/share/emacs/24.1/lisp/erc/erc-join

Features:
(mailalias smtpmail qp shadow sort mail-extr emacsbug message rfc822 mml
mml-sec mm-decode mm-bodies mm-encode mailabbrev gmm-utils mailheader
sendmail diff-mode conf-mode tramp-cache tramp-sh tramp tramp-compat
tramp-loaddefs mule-util info shell debug cus-edit js json imenu
newcomment jka-compr tabulated-list cc-mode cc-fonts cc-guess cc-menus
cc-cmds cc-styles cc-align cc-engine cc-vars cc-defs vc-git texinfo
parse-time url-cache gtimelog hi-lock url-http url-gw mail-parse rfc2231
rfc2047 rfc2045 ietf-drums image-mode dired-aux dired edict
edict-japanese edict-english edict-morphology edict-edit dui-registry
dui multi-isearch etags apropos two-column iso-transl find-func dabbrev
gnutls network-stream starttls tls erc-truncate erc-track erc-services
erc-ring erc-networks erc-netsplit erc-match erc-log erc-pcomplete
pcomplete comint erc-button erc-fill erc-stamp erc-join erc-goodies erc
erc-backend erc-compat format-spec pp help-mode easymenu view regexp-opt
uniquify gnus gnus-ems nnheader mail-utils wid-edit image-file cus-start
cus-load cl ls-lisp iswitchb thingatpt browse-url wiki warnings advice
help-fns advice-preload url-auth url url-proxy url-privacy url-expand
url-methods url-history url-cookie url-util url-parse auth-source eieio
byte-opt bytecomp byte-compile cconv macroexp assoc gnus-util
password-cache url-vars mm-util mail-prsvr mailcap ediff-merg ediff-diff
ediff-wind ediff-help ediff-util ediff-mult ediff-init ediff diff wiki-+
derived ansi-color lui tracking edmacro kmacro flyspell ispell ring
incomplete debian-el debian-el-loaddefs magit-install emacs-goodies-el
emacs-goodies-custom emacs-goodies-loaddefs easy-mmode dpkg-dev-el
dpkg-dev-el-loaddefs devhelp time-date tooltip ediff-hook vc-hooks
lisp-float-type mwheel x-win x-dnd tool-bar dnd fontset image fringe
lisp-mode register page menu-bar rfn-eshadow timer select scroll-bar
mouse jit-lock font-lock syntax facemenu font-core frame cham georgian
utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean
japanese hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese case-table epa-hook jka-cmpr-hook help simple abbrev
minibuffer loaddefs button faces cus-face files text-properties overlay
sha1 md5 base64 format env code-pages mule custom widget
hashtable-print-readable backquote make-network-process dbusbind
dynamic-setting system-font-setting font-render-setting move-toolbar gtk
x-toolkit x multi-tty emacs)

diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 01efd83..3917638 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -3964,7 +3964,9 @@ This places `point' just after the prompt, or at the beginning of the line."
     (kill-line)))
 
 (defun erc-complete-word-at-point ()
-  (run-hook-with-args-until-success 'erc-complete-functions))
+  (let ((pcomplete-extra-entry-points
+	 (cons 'completion-at-point pcomplete-extra-entry-points)))
+  (run-hook-with-args-until-success 'erc-complete-functions)))
 
 (define-obsolete-function-alias 'erc-complete-word 'completion-at-point "24.1")
 
diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index 957505f..30a743b 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -471,6 +471,15 @@ Same as `pcomplete' but using the standard completion UI."
                           (plist-get :predicate (nthcdr 3 data)))))
 
 ;;; Pcomplete's native UI.
+(defvar pcomplete-extra-entry-points nil
+  "When an non-native command is driving `pcomplete', that command's
+symbol must be placed in this variable while `pcomplete' is invoked
+in order to allow completion cycling to occur.\n
+For example:\n
+  When completing ab → aba abc abd able via `completion-at-point',\n
+which [may] delegate to `pcomplete' via `completion-at-point-functions',
+the functions in that list should bind this variable to include the
+symbol 'completion-at-point.")
 
 ;;;###autoload
 (defun pcomplete (&optional interactively)
@@ -481,9 +490,10 @@ completion functions list (it should occur fairly early in the list)."
   (if (and interactively
 	   pcomplete-cycle-completions
 	   pcomplete-current-completions
-	   (memq last-command '(pcomplete
-				pcomplete-expand-and-complete
-				pcomplete-reverse)))
+           (or (memq last-command '(pcomplete
+                                    pcomplete-expand-and-complete
+                                    pcomplete-reverse))
+               (memq last-command pcomplete-extra-entry-points)))
       (progn
 	(delete-char (- pcomplete-last-completion-length))
 	(if (eq this-command 'pcomplete-reverse)