Subject: bug#34787: New defcustom to govern TAB completion

From: Alex Branham <[email-masked]>
Date: Sat, 25 May 2019 08:05:59 -0500

Hi -

Just following up on this since I didn't hear back. If people are
interested, great. If not, I can close this bug report.

As a quick reminder/TLDR - the proposal is to add a defcustom that
governs whether TAB offers completion when hit the first time. The
thinking is that if point is in the middle of a word, you may not want
completion.

Thanks,
Alex

On Fri 08 Mar 2019 at 12:21, Alex Branham <[email-masked]> wrote:

> Hello -
>
> In ESS, we have 'ess-first-tab-never-complete' which governs whether TAB
> offers completion. The gist of it is that if you're in a buffer (with |
> representing point):
>
> (def|var foo)
>
> and you hit TAB, you might not want completion offered. I've put
> together a patch that incorporates this into Emacs by adding a new
> defcustom `tab-first-completion'. The values are currently based on
> ESS's implementation, though we can certainly change that if we want.
> Perhaps it should be a function that receives one argument (position)
> and if it returns non-nil, then we complete?
>
> Is this something people would be interested in adding?
>
> Thanks,
> Alex
>
>
> From eb4c1a9c8bcd2f5018150b0502e8ed945c181e74 Mon Sep 17 00:00:00 2001
> From: Alex Branham <[email-masked]>
> Date: Fri, 8 Mar 2019 12:09:04 -0600
> Subject: [PATCH] New defcustom tab-first-completion
>
> * lisp/indent.el (tab-always-indent): Mention 'tab-first-completion'.
> (tab-first-completion): New defcustom.
> (indent-for-tab-command): Use 'tab-first-completion'.
>
> Bug#<TBD>
> ---
>  lisp/indent.el | 40 +++++++++++++++++++++++++++++++++++++---
>  1 file changed, 37 insertions(+), 3 deletions(-)
>
> diff --git a/lisp/indent.el b/lisp/indent.el
> index 34757a43d7..08087effa6 100644
> --- a/lisp/indent.el
> +++ b/lisp/indent.el
> @@ -51,6 +51,7 @@ If nil, hitting TAB indents the current line if point is at the left margin
>  or in the line's indentation, otherwise it inserts a \"real\" TAB character.
>  If `complete', TAB first tries to indent the current line, and if the line
>  was already indented, then try to complete the thing at point.
> +See also `tab-first-completion'.
>
>  Some programming language modes have their own variable to control this,
>  e.g., `c-tab-always-indent', and do not respect this variable."
> @@ -60,6 +61,27 @@ e.g., `c-tab-always-indent', and do not respect this variable."
>  	  (const :tag "Indent if inside indentation, else TAB" nil)
>  	  (const :tag "Indent, or if already indented complete" complete)))
>
> +(defcustom tab-first-completion nil
> +  "Governs the behavior of TAB completion on the first press of the key.
> +When nil, complete.  When `eol', only complete if point is at the
> +end of a line.  When `word', complete unless the next character
> +has word syntax (according to `syntax-after').  When
> +`word-or-paren', complete unless the next character is part of a
> +word or a parenthesis.  When `word-or-paren-or-punct', complete
> +unless the next character is part of a word, parenthesis, or
> +punctuation.  Typing TAB a second time always results in
> +completion.
> +
> +This variable has no effect unless `tab-always-indent' is `complete'."
> +  :group 'indent
> +  :type '(choice
> +          (const :tag "Always complete" nil)
> +          (const :tag "Unless at the end of a line" 'eol)
> +          (const :tag "Unless looking at a word" 'word)
> +          (const :tag "Unless at a word or parenthesis" 'word-or-paren)
> +          (const :tag "Unless at a word, parenthesis, or punctuation." 'word-or-paren-or-punct))
> +  :version "27.1")
> +
>
>  (defun indent-according-to-mode ()
>    "Indent line in proper way for current major mode.
> @@ -111,7 +133,7 @@ or performs symbol completion, depending on `tab-always-indent'.
>  The function called to actually indent the line or insert a tab
>  is given by the variable `indent-line-function'.
>
> -If a prefix argument is given, after this function indents the
> +If a prefix argument is given (ARG), after this function indents the
>  current line or inserts a tab, it also rigidly indents the entire
>  balanced expression which starts at the beginning of the current
>  line, to reflect the current line's indentation.
> @@ -139,7 +161,8 @@ prefix argument is ignored."
>     (t
>      (let ((old-tick (buffer-chars-modified-tick))
>            (old-point (point))
> -	  (old-indent (current-indentation)))
> +	  (old-indent (current-indentation))
> +          (syn (syntax-after (point))))
>
>        ;; Indent the line.
>        (or (not (eq (indent--funcall-widened indent-line-function) 'noindent))
> @@ -152,7 +175,18 @@ prefix argument is ignored."
>         ;; If the text was already indented right, try completion.
>         ((and (eq tab-always-indent 'complete)
>               (eq old-point (point))
> -             (eq old-tick (buffer-chars-modified-tick)))
> +             (eq old-tick (buffer-chars-modified-tick))
> +             (or (null tab-first-completion)
> +                 (eq last-command this-command)
> +                 (and (equal tab-first-completion 'eol)
> +                      (eolp))
> +                 (and (member tab-first-completion '(word word-or-paren word-or-paren-or-punct))
> +                      (not (member 2 syn)))
> +                 (and (member tab-first-completion '(word-or-paren word-or-paren-or-punct))
> +                      (not (or (member 4 syn)
> +                               (member 5 syn))))
> +                 (and (equal tab-first-completion 'word-or-paren-or-punct)
> +                      (not (member 1 syn)))))
>          (completion-at-point))
>
>         ;; If a prefix argument was given, rigidly indent the following