unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [PATCH 2/2] emacs: Add shell completions for "guix" command.
@ 2015-06-06 20:44 Alex Kost
  2015-06-07 15:36 ` Ludovic Courtès
  0 siblings, 1 reply; 12+ messages in thread
From: Alex Kost @ 2015-06-06 20:44 UTC (permalink / raw)
  To: guix-devel

[-- Attachment #1: Type: text/plain, Size: 699 bytes --]

Oof!  There is a plenty of guix commands, actions, options, …
So after this patch, it would be more convenient to use ‘guix’ in
"M-x shell".

It should complete almost everything except suboptions of subcommands, I
mean those ‘import’ modules (e.g., it will not complete
‘--no-test-dependencies’ option for ‘guix import hackage’ command).

Completing may lag a bit from time to time.  It happens because shell
commands (like ‘guix ... --help’) are called when necessary to find
options, packages, etc.  But the found entries are "memoized" so next
time the same entries will be completed much faster.

Many thanks to Ludovic for the great idea of this package!


[-- Attachment #2: 0002-emacs-Add-shell-completions-for-guix-command.patch --]
[-- Type: text/x-patch, Size: 18410 bytes --]

From 9ed30b97519afad84c58f1a7166f11a5e22ecda1 Mon Sep 17 00:00:00 2001
From: Alex Kost <alezost@gmail.com>
Date: Sat, 6 Jun 2015 22:19:51 +0300
Subject: [PATCH 2/2] emacs: Add shell completions for "guix" command.

* emacs/guix-pcomplete.el: New file.
* emacs.am (ELFILES): Add it.
* doc/emacs.texi (Emacs Completions): New node.
---
 doc/emacs.texi          |  32 ++++
 emacs.am                |   1 +
 emacs/guix-pcomplete.el | 394 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 427 insertions(+)
 create mode 100644 emacs/guix-pcomplete.el

diff --git a/doc/emacs.texi b/doc/emacs.texi
index 6c1b255..00cd5c1 100644
--- a/doc/emacs.texi
+++ b/doc/emacs.texi
@@ -19,6 +19,7 @@ guix package}).  Specifically, ``guix.el'' makes it easy to:
 * Usage: Emacs Usage.			Using the interface.
 * Configuration: Emacs Configuration.	Configuring the interface.
 * Prettify Mode: Emacs Prettify.	Abbreviating @file{/gnu/store/@dots{}} file names.
+* Completions: Emacs Completions.       Completing @command{guix} shell command.
 @end menu
 
 @node Emacs Initial Setup
@@ -489,3 +490,34 @@ mode hooks (@pxref{Hooks,,, emacs, The GNU Emacs Manual}), for example:
 (add-hook 'shell-mode-hook 'guix-prettify-mode)
 (add-hook 'dired-mode-hook 'guix-prettify-mode)
 @end example
+
+
+@node Emacs Completions
+@subsection Shell Completions
+
+Another feature that becomes available after configuring Emacs interface
+(@pxref{Emacs Initial Setup}) is completing of @command{guix}
+subcommands, options, packages and other things in @code{shell}
+(@pxref{Interactive Shell,,, emacs, The GNU Emacs Manual}) and
+@code{eshell} (@pxref{Top,,, eshell, Eshell: The Emacs Shell}).
+
+It works the same way as other completions do.  Just press @key{TAB}
+when your intuition tells you.
+
+And here are some examples, where pressing @key{TAB} may complete
+something:
+
+@itemize @w{}
+
+@item @code{guix pa}@key{TAB}
+@item @code{guix package -}@key{TAB}
+@item @code{guix package --}@key{TAB}
+@item @code{guix package -i gei}@key{TAB}
+@item @code{guix build -L/tm}@key{TAB}
+@item @code{guix build --sy}@key{TAB}
+@item @code{guix build --system=i}@key{TAB}
+@item @code{guix system rec}@key{TAB}
+@item @code{guix lint --checkers=sy}@key{TAB}
+@item @code{guix lint --checkers=synopsis,des}@key{TAB}
+
+@end itemize
diff --git a/emacs.am b/emacs.am
index a43168e..372b33e 100644
--- a/emacs.am
+++ b/emacs.am
@@ -26,6 +26,7 @@ ELFILES =					\
   emacs/guix-info.el				\
   emacs/guix-list.el				\
   emacs/guix-messages.el			\
+  emacs/guix-pcomplete.el			\
   emacs/guix-prettify.el			\
   emacs/guix-utils.el				\
   emacs/guix.el
diff --git a/emacs/guix-pcomplete.el b/emacs/guix-pcomplete.el
new file mode 100644
index 0000000..5249bde
--- /dev/null
+++ b/emacs/guix-pcomplete.el
@@ -0,0 +1,394 @@
+;;; guix-pcomplete.el --- Functions for completing guix commands  -*- lexical-binding: t -*-
+
+;; Copyright © 2015 Alex Kost <alezost@gmail.com>
+
+;; This file is part of GNU Guix.
+
+;; GNU Guix is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Guix is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file provides completions for "guix" command that may be used in
+;; `shell', `eshell' and wherever `pcomplete' works.
+
+;;; Code:
+
+(require 'pcomplete)
+(require 'pcmpl-unix)
+(require 'cl-lib)
+(require 'guix-utils)
+
+\f
+;;; Regexps for parsing various "guix ..." outputs
+
+(defvar guix-pcomplete-parse-package-regexp
+  (rx bol (group (one-or-more (not blank))))
+  "Regexp used to find names of the packages.")
+
+(defvar guix-pcomplete-parse-command-regexp
+  (rx bol
+      (or "   "
+          "  - '")                      ; for "guix system" actions
+      (group wordchar (one-or-more (or wordchar "-"))))
+  "Regexp used to find guix commands.
+'Command' means any option not prefixed with '-'.  For example,
+guix subcommand, system action, importer, etc.")
+
+(defvar guix-pcomplete-parse-long-option-regexp
+  (rx (or "  " ", ")
+      (group "--" (one-or-more (or wordchar "-"))
+             (zero-or-one "=")))
+  "Regexp used to find long options.")
+
+(defvar guix-pcomplete-parse-short-option-regexp
+  (rx bol (one-or-more blank)
+      "-" (group (not (any "- "))))
+  "Regexp used to find short options.")
+
+(defvar guix-pcomplete-parse-linter-regexp
+  (rx bol "- " (group (one-or-more (or wordchar "-"))))
+  "Regexp used to find 'lint' checkers.")
+
+(defvar guix-pcomplete-parse-regexp-group 1
+  "Parenthesized expression of regexps used to find commands and
+options.")
+
+\f
+;;; Non-receivable completions
+
+(defvar guix-pcomplete-systems
+  '("x86_64-linux" "i686-linux" "armhf-linux" "mips64el-linux")
+  "List of supported systems.")
+
+(defvar guix-pcomplete-hash-formats
+  '("nix-base32" "base32" "base16" "hex" "hexadecimal")
+  "List of supported hash formats.")
+
+(defvar guix-pcomplete-refresh-subsets
+  '("core" "non-core")
+  "List of supported 'refresh' subsets.")
+
+(defvar guix-pcomplete-key-policies
+  '("interactive" "always" "never")
+  "List of supported key download policies.")
+
+\f
+;;; Interacting with guix
+
+(defcustom guix-pcomplete-guix-program (executable-find "guix")
+  "Name of the 'guix' program.
+It is used to find guix commands, options, packages, etc."
+  :type 'file
+  :group 'pcomplete
+  :group 'guix)
+
+(defun guix-pcomplete-run-guix (&rest args)
+  "Run `guix-pcomplete-guix-program' with ARGS.
+Insert the output to the current buffer."
+  (apply #'call-process
+         guix-pcomplete-guix-program nil t nil args))
+
+(defun guix-pcomplete-run-guix-and-search (regexp &optional group
+                                                  &rest args)
+  "Run `guix-pcomplete-guix-program' with ARGS and search for matches.
+Return a list of strings matching REGEXP.
+GROUP specifies a parenthesized expression used in REGEXP."
+  (with-temp-buffer
+    (apply #'guix-pcomplete-run-guix args)
+    (goto-char (point-min))
+    (let (result)
+      (while (re-search-forward regexp nil t)
+        (push (match-string-no-properties group) result))
+      (nreverse result))))
+
+(defmacro guix-pcomplete-define-options-finder (name docstring regexp
+                                                     &optional filter)
+  "Define function NAME to receive guix options and commands.
+
+The defined function takes an optional COMMAND argument.  This
+function will run 'guix COMMAND --help' (or 'guix --help' if
+COMMAND is nil) using `guix-pcomplete-run-guix-and-search' and
+return its result.
+
+If FILTER is specified, it should be a function.  The result is
+passed to this FILTER as argument and the result value of this
+function call is returned."
+  (declare (doc-string 2) (indent 1))
+  `(guix-memoized-defun ,name (&optional command)
+     ,docstring
+     (let* ((args '("--help"))
+            (args (if command (cons command args) args))
+            (res (apply #'guix-pcomplete-run-guix-and-search
+                        ,regexp guix-pcomplete-parse-regexp-group args)))
+       ,(if filter
+            `(funcall ,filter res)
+          'res))))
+
+(guix-pcomplete-define-options-finder guix-pcomplete-commands
+  "If COMMAND is nil, return a list of available guix commands.
+If COMMAND is non-nil (it should be a string), return available
+subcommands, actions, etc. for this guix COMMAND."
+  guix-pcomplete-parse-command-regexp)
+
+(guix-pcomplete-define-options-finder guix-pcomplete-long-options
+  "Return a list of available long options for guix COMMAND."
+  guix-pcomplete-parse-long-option-regexp)
+
+(guix-pcomplete-define-options-finder guix-pcomplete-short-options
+  "Return a string with available short options for guix COMMAND."
+  guix-pcomplete-parse-short-option-regexp
+  (lambda (list)
+    (mapconcat #'identity list "")))
+
+(guix-memoized-defun guix-pcomplete-all-packages ()
+  "Return a list of all available Guix packages."
+  (guix-pcomplete-run-guix-and-search
+   guix-pcomplete-parse-package-regexp
+   guix-pcomplete-parse-regexp-group
+   "package" "--list-available"))
+
+(guix-memoized-defun guix-pcomplete-installed-packages (&optional profile)
+  "Return a list of Guix packages installed in PROFILE."
+  (let* ((args (and profile
+                    (list (concat "--profile=" profile))))
+         (args (append '("package" "--list-installed") args)))
+    (apply #'guix-pcomplete-run-guix-and-search
+           guix-pcomplete-parse-package-regexp
+           guix-pcomplete-parse-regexp-group
+           args)))
+
+(guix-memoized-defun guix-pcomplete-lint-checkers ()
+  "Return a list of all available lint checkers."
+  (guix-pcomplete-run-guix-and-search
+   guix-pcomplete-parse-linter-regexp
+   guix-pcomplete-parse-regexp-group
+   "lint" "--list-checkers"))
+
+\f
+;;; Completing
+
+(defvar guix-pcomplete-option-regexp (rx string-start "-")
+  "Regexp to match an option.")
+
+(defvar guix-pcomplete-long-option-regexp (rx string-start "--")
+  "Regexp to match a long option.")
+
+(defvar guix-pcomplete-long-option-with-arg-regexp
+  (rx string-start
+      (group "--" (one-or-more any)) "="
+      (group (zero-or-more any)))
+  "Regexp to match a long option with its argument.
+The first parenthesized group defines the option and the second
+group - the argument.")
+
+(defvar guix-pcomplete-short-option-with-arg-regexp
+  (rx string-start
+      (group "-" (not (any "-")))
+      (group (zero-or-more any)))
+  "Regexp to match a short option with its argument.
+The first parenthesized group defines the option and the second
+group - the argument.")
+
+(defun guix-pcomplete-match-option ()
+  "Return non-nil, if the current argument is an option."
+  (pcomplete-match guix-pcomplete-option-regexp 0))
+
+(defun guix-pcomplete-match-long-option ()
+  "Return non-nil, if the current argument is a long option."
+  (pcomplete-match guix-pcomplete-long-option-regexp 0))
+
+(defun guix-pcomplete-match-long-option-with-arg ()
+  "Return non-nil, if the current argument is a long option with value."
+  (pcomplete-match guix-pcomplete-long-option-with-arg-regexp 0))
+
+(defun guix-pcomplete-match-short-option-with-arg ()
+  "Return non-nil, if the current argument is a short option with value."
+  (pcomplete-match guix-pcomplete-short-option-with-arg-regexp 0))
+
+(defun guix-pcomplete-long-option-arg (option args)
+  "Return a long OPTION's argument from a list of arguments ARGS."
+  (let* ((re (concat "\\`" option "=\\(.*\\)"))
+         (args (cl-member-if (lambda (arg)
+                               (string-match re arg))
+                             args))
+         (cur (car args)))
+    (when cur
+      (match-string-no-properties 1 cur))))
+
+(defun guix-pcomplete-short-option-arg (option args)
+  "Return a short OPTION's argument from a list of arguments ARGS."
+  (let* ((re (concat "\\`" option "\\(.*\\)"))
+         (args (cl-member-if (lambda (arg)
+                               (string-match re arg))
+                             args))
+         (cur (car args)))
+    (when cur
+      (let ((arg (match-string-no-properties 1 cur)))
+        (if (string= "" arg)
+            (cadr args)                 ; take the next arg
+          arg)))))
+
+(defun guix-pcomplete-complete-comma-args (entries)
+  "Complete comma separated arguments using ENTRIES."
+  (let ((index pcomplete-index))
+    (while (= index pcomplete-index)
+      (let* ((args (if (or (guix-pcomplete-match-long-option-with-arg)
+                           (guix-pcomplete-match-short-option-with-arg))
+                       (pcomplete-match-string 2 0)
+                     (pcomplete-arg 0)))
+             (input (if (string-match ".*,\\(.*\\)" args)
+                        (match-string-no-properties 1 args)
+                      args)))
+        (pcomplete-here* entries input)))))
+
+(defun guix-pcomplete-complete-command-arg (command)
+  "Complete argument for guix COMMAND."
+  (cond
+   ((member command
+            '("archive" "build" "environment" "lint" "refresh"))
+    (while t
+      (pcomplete-here (guix-pcomplete-all-packages))))
+   (t (pcomplete-here* (pcomplete-entries)))))
+
+(defun guix-pcomplete-complete-option-arg (command option &optional input)
+  "Complete argument for COMMAND's OPTION.
+INPUT is the current partially completed string."
+  (cl-flet ((option? (short long)
+              (or (string= option short)
+                  (string= option long)))
+            (command? (&rest commands)
+              (member command commands))
+            (complete (entries)
+              (pcomplete-here entries input nil t))
+            (complete* (entries)
+              (pcomplete-here* entries input t)))
+    (cond
+     ((option? "-L" "--load-path")
+      (complete* (pcomplete-dirs)))
+     ((string= "--key-download" option)
+      (complete* guix-pcomplete-key-policies))
+
+     ((command? "package")
+      (cond
+       ;; For '--install[=]' and '--remove[=]', try to complete a package
+       ;; name (INPUT) after the "=" sign, and then the rest packages
+       ;; separated with spaces.
+       ((option? "-i" "--install")
+        (complete (guix-pcomplete-all-packages))
+        (while (not (guix-pcomplete-match-option))
+          (pcomplete-here (guix-pcomplete-all-packages))))
+       ((option? "-r" "--remove")
+        (let* ((profile (or (guix-pcomplete-short-option-arg
+                             "-p" pcomplete-args)
+                            (guix-pcomplete-long-option-arg
+                             "--profile" pcomplete-args)))
+               (profile (and profile (expand-file-name profile))))
+          (complete (guix-pcomplete-installed-packages profile))
+          (while (not (guix-pcomplete-match-option))
+            (pcomplete-here (guix-pcomplete-installed-packages profile)))))
+       ((string= "--show" option)
+        (complete (guix-pcomplete-all-packages)))
+       ((option? "-p" "--profile")
+        (complete* (pcomplete-dirs)))
+       ((option? "-m" "--manifest")
+        (complete* (pcomplete-entries)))))
+
+     ((and (command? "archive" "build")
+           (option? "-s" "--system"))
+      (complete* guix-pcomplete-systems))
+
+     ((and (command? "build")
+           (option? "-r" "--root"))
+      (complete* (pcomplete-entries)))
+
+     ((and (command? "environment")
+           (option? "-l" "--load"))
+      (complete* (pcomplete-entries)))
+
+     ((and (command? "hash" "download")
+           (option? "-f" "--format"))
+      (complete* guix-pcomplete-hash-formats))
+
+     ((and (command? "lint")
+           (option? "-c" "--checkers"))
+      (guix-pcomplete-complete-comma-args
+       (guix-pcomplete-lint-checkers)))
+
+     ((and (command? "publish")
+           (option? "-u" "--user"))
+      (complete* (pcmpl-unix-user-names)))
+
+     ((and (command? "refresh")
+           (option? "-s" "--select"))
+      (complete* guix-pcomplete-refresh-subsets)))))
+
+(defun guix-pcomplete-complete-options (command)
+  "Complete options (with their arguments) for guix COMMAND."
+  (while (guix-pcomplete-match-option)
+    (let ((index pcomplete-index))
+      (if (guix-pcomplete-match-long-option)
+
+          ;; Long options.
+          (if (guix-pcomplete-match-long-option-with-arg)
+              (let ((option (pcomplete-match-string 1 0))
+                    (arg    (pcomplete-match-string 2 0)))
+                (guix-pcomplete-complete-option-arg
+                 command option arg))
+
+            (pcomplete-here* (guix-pcomplete-long-options command))
+            ;; We support '--opt arg' style (along with '--opt=arg'),
+            ;; because 'guix package --install/--remove' may be used this
+            ;; way.  So try to complete an argument after the option has
+            ;; been completed.
+            (unless (guix-pcomplete-match-option)
+              (guix-pcomplete-complete-option-arg
+               command (pcomplete-arg 0 -1))))
+
+        ;; Short options.
+        (let ((arg (pcomplete-arg 0)))
+          (if (> (length arg) 2)
+              ;; Support specifying an argument after a short option without
+              ;; spaces (for example, '-L/tmp/foo').
+              (guix-pcomplete-complete-option-arg
+               command
+               (substring-no-properties arg 0 2)
+               (substring-no-properties arg 2))
+            (pcomplete-opt (guix-pcomplete-short-options command))
+            (guix-pcomplete-complete-option-arg
+             command (pcomplete-arg 0 -1)))))
+
+      ;; If there were no completions, move to the next argument and get
+      ;; out if the last argument is achieved.
+      (when (= index pcomplete-index)
+        (if (= pcomplete-index pcomplete-last)
+            (throw 'pcompleted nil)
+          (pcomplete-next-arg))))))
+
+;;;###autoload
+(defun pcomplete/guix ()
+  "Completion for `guix'."
+  (let ((commands (guix-pcomplete-commands)))
+    (pcomplete-here* (cons "--help" commands))
+    (let ((command (pcomplete-arg 'first 1)))
+      (when (member command commands)
+        (guix-pcomplete-complete-options command)
+        (let ((subcommands (guix-pcomplete-commands command)))
+          (when subcommands
+            (pcomplete-here* subcommands)))
+        (guix-pcomplete-complete-options command)
+        (guix-pcomplete-complete-command-arg command)))))
+
+(provide 'guix-pcomplete)
+
+;;; guix-pcomplete.el ends here
-- 
2.2.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/2] emacs: Add shell completions for "guix" command.
  2015-06-06 20:44 [PATCH 2/2] emacs: Add shell completions for "guix" command Alex Kost
@ 2015-06-07 15:36 ` Ludovic Courtès
  2015-06-07 19:38   ` Ludovic Courtès
  2015-06-08  8:52   ` Alex Kost
  0 siblings, 2 replies; 12+ messages in thread
From: Ludovic Courtès @ 2015-06-07 15:36 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> Oof!  There is a plenty of guix commands, actions, options, …
> So after this patch, it would be more convenient to use ‘guix’ in
> "M-x shell".

Woohoo!  Wonderful!  :-)

> It should complete almost everything except suboptions of subcommands, I
> mean those ‘import’ modules (e.g., it will not complete
> ‘--no-test-dependencies’ option for ‘guix import hackage’ command).

I think that’s fine.  We wouldn’t want to maintain relatively custom
completion code for all these things anyway.

However it’s important to fall back to file name completions for things
that are not handled, so that at least it is not worse than without the
pcomplete code.

> Completing may lag a bit from time to time.  It happens because shell
> commands (like ‘guix ... --help’) are called when necessary to find
> options, packages, etc.  But the found entries are "memoized" so next
> time the same entries will be completed much faster.

I wonder if ‘guix-pcomplete-run-guix’ couldn’t run the command in a REPL
instead of spawning a process.  That is, when completing ‘guix build’,
it would evaluate something like:

  (with-output-to-string
    (lambda ()
      (catch 'quit
        (lambda ()
          ((@ (guix scripts build) guix-build) "--help"))
        (const #t))))

How does that sound?

> Many thanks to Ludovic for the great idea of this package!

Well I’m always impressed by your productivity!

> From 9ed30b97519afad84c58f1a7166f11a5e22ecda1 Mon Sep 17 00:00:00 2001
> From: Alex Kost <alezost@gmail.com>
> Date: Sat, 6 Jun 2015 22:19:51 +0300
> Subject: [PATCH 2/2] emacs: Add shell completions for "guix" command.
>
> * emacs/guix-pcomplete.el: New file.
> * emacs.am (ELFILES): Add it.
> * doc/emacs.texi (Emacs Completions): New node.

LGTM!  OK to push.

> +     ((option? "-L" "--load-path")
> +      (complete* (pcomplete-dirs)))
> +     ((string= "--key-download" option)
> +      (complete* guix-pcomplete-key-policies))
> +
> +     ((command? "package")
> +      (cond
> +       ;; For '--install[=]' and '--remove[=]', try to complete a package
> +       ;; name (INPUT) after the "=" sign, and then the rest packages
> +       ;; separated with spaces.
> +       ((option? "-i" "--install")
> +        (complete (guix-pcomplete-all-packages))
> +        (while (not (guix-pcomplete-match-option))
> +          (pcomplete-here (guix-pcomplete-all-packages))))
> +       ((option? "-r" "--remove")
> +        (let* ((profile (or (guix-pcomplete-short-option-arg
> +                             "-p" pcomplete-args)
> +                            (guix-pcomplete-long-option-arg
> +                             "--profile" pcomplete-args)))
> +               (profile (and profile (expand-file-name profile))))
> +          (complete (guix-pcomplete-installed-packages profile))
> +          (while (not (guix-pcomplete-match-option))
> +            (pcomplete-here (guix-pcomplete-installed-packages profile)))))
> +       ((string= "--show" option)
> +        (complete (guix-pcomplete-all-packages)))
> +       ((option? "-p" "--profile")
> +        (complete* (pcomplete-dirs)))
> +       ((option? "-m" "--manifest")
> +        (complete* (pcomplete-entries)))))
> +
> +     ((and (command? "archive" "build")
> +           (option? "-s" "--system"))
> +      (complete* guix-pcomplete-systems))
> +
> +     ((and (command? "build")
> +           (option? "-r" "--root"))
> +      (complete* (pcomplete-entries)))
> +
> +     ((and (command? "environment")
> +           (option? "-l" "--load"))
> +      (complete* (pcomplete-entries)))
> +
> +     ((and (command? "hash" "download")
> +           (option? "-f" "--format"))
> +      (complete* guix-pcomplete-hash-formats))
> +
> +     ((and (command? "lint")
> +           (option? "-c" "--checkers"))
> +      (guix-pcomplete-complete-comma-args
> +       (guix-pcomplete-lint-checkers)))
> +
> +     ((and (command? "publish")
> +           (option? "-u" "--user"))
> +      (complete* (pcmpl-unix-user-names)))
> +
> +     ((and (command? "refresh")
> +           (option? "-s" "--select"))
> +      (complete* guix-pcomplete-refresh-subsets)))))

I like that this is very accurate.  I was first slightly concerned about
the risk of this becoming out of sync, but I think we’d quickly notice
and it seems easy to update anyway.

Thank you!

Ludo’.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/2] emacs: Add shell completions for "guix" command.
  2015-06-07 15:36 ` Ludovic Courtès
@ 2015-06-07 19:38   ` Ludovic Courtès
  2015-06-08  8:52   ` Alex Kost
  1 sibling, 0 replies; 12+ messages in thread
From: Ludovic Courtès @ 2015-06-07 19:38 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

ludo@gnu.org (Ludovic Courtès) skribis:

> I wonder if ‘guix-pcomplete-run-guix’ couldn’t run the command in a REPL
> instead of spawning a process.  That is, when completing ‘guix build’,
> it would evaluate something like:
>
>   (with-output-to-string
>     (lambda ()
>       (catch 'quit
>         (lambda ()
>           ((@ (guix scripts build) guix-build) "--help"))
>         (const #t))))

Another possibility would be to inspect the ‘%options’ variable of the
command, but that wouldn’t give access to the help message.

Ludo’.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/2] emacs: Add shell completions for "guix" command.
  2015-06-07 15:36 ` Ludovic Courtès
  2015-06-07 19:38   ` Ludovic Courtès
@ 2015-06-08  8:52   ` Alex Kost
  2015-06-08 19:19     ` Ludovic Courtès
  1 sibling, 1 reply; 12+ messages in thread
From: Alex Kost @ 2015-06-08  8:52 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

Ludovic Courtès (2015-06-07 18:36 +0300) wrote:

> Alex Kost <alezost@gmail.com> skribis:
>
>> It should complete almost everything except suboptions of subcommands, I
>> mean those ‘import’ modules (e.g., it will not complete
>> ‘--no-test-dependencies’ option for ‘guix import hackage’ command).
>
> I think that’s fine.  We wouldn’t want to maintain relatively custom
> completion code for all these things anyway.
>
> However it’s important to fall back to file name completions for things
> that are not handled, so that at least it is not worse than without the
> pcomplete code.

Yes, it is the current behaviour (I mean the file names completion).

>> Completing may lag a bit from time to time.  It happens because shell
>> commands (like ‘guix ... --help’) are called when necessary to find
>> options, packages, etc.  But the found entries are "memoized" so next
>> time the same entries will be completed much faster.
>
> I wonder if ‘guix-pcomplete-run-guix’ couldn’t run the command in a REPL
> instead of spawning a process.  That is, when completing ‘guix build’,
> it would evaluate something like:
>
>   (with-output-to-string
>     (lambda ()
>       (catch 'quit
>         (lambda ()
>           ((@ (guix scripts build) guix-build) "--help"))
>         (const #t))))
>
> How does that sound?

It was the initial deliberate decision to separate completions code from
interacting with "guix repl", to make completing independent.  I imagine
that a user wouldn't like to see that some "Guix REPL has been started"
just because he presses TAB after ‘guix pa’ in his shell buffer.  Well,
at least I wouldn't use such strange completions.

I think lags made by spawning the guix processes are bearable and not
repeatable (I mean the next time ‘guix pa’<TAB> is completed
immediately), so I would not like to change the code to use a REPL if
you don't mind :-)

(Perhaps in the future there will be added some option (e.g., a boolean
`guix-pcomplete-use-repl' variable) to choose what a user prefers)

>> Many thanks to Ludovic for the great idea of this package!
>
> Well I’m always impressed by your productivity!

Thanks, but it is far from yours!!

>> From 9ed30b97519afad84c58f1a7166f11a5e22ecda1 Mon Sep 17 00:00:00 2001
>> From: Alex Kost <alezost@gmail.com>
>> Date: Sat, 6 Jun 2015 22:19:51 +0300
>> Subject: [PATCH 2/2] emacs: Add shell completions for "guix" command.
>>
>> * emacs/guix-pcomplete.el: New file.
>> * emacs.am (ELFILES): Add it.
>> * doc/emacs.texi (Emacs Completions): New node.
>
> LGTM!  OK to push.
[...]
>> +     ((and (command? "publish")
>> +           (option? "-u" "--user"))
>> +      (complete* (pcmpl-unix-user-names)))
>> +
>> +     ((and (command? "refresh")
>> +           (option? "-s" "--select"))
>> +      (complete* guix-pcomplete-refresh-subsets)))))
>
> I like that this is very accurate.  I was first slightly concerned about
> the risk of this becoming out of sync, but I think we’d quickly notice
> and it seems easy to update anyway.

Yes, I think we'll notice, thanks.

I have pushed this commit, so if someone notices that something is not
completed properly, please report.

-- 
Alex

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/2] emacs: Add shell completions for "guix" command.
  2015-06-08  8:52   ` Alex Kost
@ 2015-06-08 19:19     ` Ludovic Courtès
  2015-06-10  7:31       ` Complete './pre-inst-env guix ...' Alex Kost
  0 siblings, 1 reply; 12+ messages in thread
From: Ludovic Courtès @ 2015-06-08 19:19 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> Ludovic Courtès (2015-06-07 18:36 +0300) wrote:

[...]

>> However it’s important to fall back to file name completions for things
>> that are not handled, so that at least it is not worse than without the
>> pcomplete code.
>
> Yes, it is the current behaviour (I mean the file names completion).

Perfect.

>>> Completing may lag a bit from time to time.  It happens because shell
>>> commands (like ‘guix ... --help’) are called when necessary to find
>>> options, packages, etc.  But the found entries are "memoized" so next
>>> time the same entries will be completed much faster.
>>
>> I wonder if ‘guix-pcomplete-run-guix’ couldn’t run the command in a REPL
>> instead of spawning a process.  That is, when completing ‘guix build’,
>> it would evaluate something like:
>>
>>   (with-output-to-string
>>     (lambda ()
>>       (catch 'quit
>>         (lambda ()
>>           ((@ (guix scripts build) guix-build) "--help"))
>>         (const #t))))
>>
>> How does that sound?
>
> It was the initial deliberate decision to separate completions code from
> interacting with "guix repl", to make completing independent.  I imagine
> that a user wouldn't like to see that some "Guix REPL has been started"
> just because he presses TAB after ‘guix pa’ in his shell buffer.  Well,
> at least I wouldn't use such strange completions.
>
> I think lags made by spawning the guix processes are bearable and not
> repeatable (I mean the next time ‘guix pa’<TAB> is completed
> immediately), so I would not like to change the code to use a REPL if
> you don't mind :-)
>
> (Perhaps in the future there will be added some option (e.g., a boolean
> `guix-pcomplete-use-repl' variable) to choose what a user prefers)

Right, it makes sense to have that module independent of the rest.
I just tried and it feels faster than the Bash completion anyway.  :-)

Thank you!

Ludo’.

PS: I wonder if something could also be done for “./pre-inst-env guix”...

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Complete './pre-inst-env guix ...'
  2015-06-08 19:19     ` Ludovic Courtès
@ 2015-06-10  7:31       ` Alex Kost
  2015-06-10 19:44         ` Ludovic Courtès
  0 siblings, 1 reply; 12+ messages in thread
From: Alex Kost @ 2015-06-10  7:31 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

Ludovic Courtès (2015-06-08 22:19 +0300) wrote:

> PS: I wonder if something could also be done for “./pre-inst-env guix”...

IIUC there is no simple way to configure pcomplete for that.  Here is
an invasive hack I came up with:

--8<---------------cut here---------------start------------->8---
(defvar al/pcomplete-skipped-commands
  '("sudo" "pre-inst-env")
  "List of special commands for `al/pcomplete-reduce-args-maybe'.")

(defun al/pcomplete-reduce-args-maybe (&rest _)
  "Change some global variables to complete a special command properly.
If a command from `al/pcomplete-skipped-commands' is being
completed, skip it and perform completion as if the next argument
was the current command."
  (when pcomplete-args
    (let ((cmd (file-name-nondirectory (car pcomplete-args))))
      (when (member cmd al/pcomplete-skipped-commands)
        (setq pcomplete-args (cdr pcomplete-args)
              pcomplete-last (1- pcomplete-last))))))

(advice-add 'pcomplete-parse-arguments
  :after #'al/pcomplete-reduce-args-maybe)
--8<---------------cut here---------------end--------------->8---

-- 
Alex

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Complete './pre-inst-env guix ...'
  2015-06-10  7:31       ` Complete './pre-inst-env guix ...' Alex Kost
@ 2015-06-10 19:44         ` Ludovic Courtès
  2015-06-11 14:41           ` Alex Kost
  0 siblings, 1 reply; 12+ messages in thread
From: Ludovic Courtès @ 2015-06-10 19:44 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> Ludovic Courtès (2015-06-08 22:19 +0300) wrote:
>
>> PS: I wonder if something could also be done for “./pre-inst-env guix”...
>
> IIUC there is no simple way to configure pcomplete for that.  Here is
> an invasive hack I came up with:

Nice, thanks!

> (defvar al/pcomplete-skipped-commands
>   '("sudo" "pre-inst-env")

Actually ‘sudo’ completion works well by default; e.g., “sudo ha<TAB>”
completes to “sudo halt”.  Could it be that there’s another way to
achieve that?

Ludo’.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Complete './pre-inst-env guix ...'
  2015-06-10 19:44         ` Ludovic Courtès
@ 2015-06-11 14:41           ` Alex Kost
  2015-06-14 22:07             ` Ludovic Courtès
  0 siblings, 1 reply; 12+ messages in thread
From: Alex Kost @ 2015-06-11 14:41 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

Ludovic Courtès (2015-06-10 22:44 +0300) wrote:

[...]
> Actually ‘sudo’ completion works well by default; e.g., “sudo ha<TAB>”
> completes to “sudo halt”.  Could it be that there’s another way to
> achieve that?

‘sudo’ completions don't work by default (did you check "emacs -Q"?)
I think you are using some additional package for that.

-- 
Alex

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Complete './pre-inst-env guix ...'
  2015-06-11 14:41           ` Alex Kost
@ 2015-06-14 22:07             ` Ludovic Courtès
  2015-06-15 12:43               ` Alex Kost
  0 siblings, 1 reply; 12+ messages in thread
From: Ludovic Courtès @ 2015-06-14 22:07 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> Ludovic Courtès (2015-06-10 22:44 +0300) wrote:
>
> [...]
>> Actually ‘sudo’ completion works well by default; e.g., “sudo ha<TAB>”
>> completes to “sudo halt”.  Could it be that there’s another way to
>> achieve that?
>
> ‘sudo’ completions don't work by default (did you check "emacs -Q"?)
> I think you are using some additional package for that.

Indeed, this must be provided by the ‘pcmpl-args’ package.

Ludo’.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Complete './pre-inst-env guix ...'
  2015-06-14 22:07             ` Ludovic Courtès
@ 2015-06-15 12:43               ` Alex Kost
  2015-06-15 20:16                 ` Ludovic Courtès
  0 siblings, 1 reply; 12+ messages in thread
From: Alex Kost @ 2015-06-15 12:43 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

[-- Attachment #1: Type: text/plain, Size: 917 bytes --]

Ludovic Courtès (2015-06-15 01:07 +0300) wrote:

> Alex Kost <alezost@gmail.com> skribis:
>
>> Ludovic Courtès (2015-06-10 22:44 +0300) wrote:
>>
>> [...]
>>> Actually ‘sudo’ completion works well by default; e.g., “sudo ha<TAB>”
>>> completes to “sudo halt”.  Could it be that there’s another way to
>>> achieve that?
>>
>> ‘sudo’ completions don't work by default (did you check "emacs -Q"?)
>> I think you are using some additional package for that.
>
> Indeed, this must be provided by the ‘pcmpl-args’ package.

Thanks for the pointer, that's a great package!  Its completions don't
use a common 'pcomplete-here' function (and don't rely on
'pcomplete-args' and other global variables).  Instead the own
"pcmpl-args" code base (and it is big!) is used.

So (analogously to 'pcomplete/sudo') the following function may be used
to complete "pre-inst-env" properly:


[-- Attachment #2: pcomplete-pre-inst-env.el --]
[-- Type: application/emacs-lisp, Size: 188 bytes --]

[-- Attachment #3: Type: text/plain, Size: 10 bytes --]


-- 
Alex

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Complete './pre-inst-env guix ...'
  2015-06-15 12:43               ` Alex Kost
@ 2015-06-15 20:16                 ` Ludovic Courtès
  2015-06-16 17:23                   ` Alex Kost
  0 siblings, 1 reply; 12+ messages in thread
From: Ludovic Courtès @ 2015-06-15 20:16 UTC (permalink / raw)
  To: Alex Kost; +Cc: guix-devel

Alex Kost <alezost@gmail.com> skribis:

> So (analogously to 'pcomplete/sudo') the following function may be used
> to complete "pre-inst-env" properly:
>
> (defun pcomplete/pre-inst-env ()
>   (pcmpl-args-pcomplete
>    (pcmpl-args-make-argspecs
>     `((argument 0 (("COMMAND" nil))
>                 :subparser pcmpl-args-command-subparser)))))

Indeed, that works like a charm, thanks!

Now, the fact that ‘pre-inst-env’ is not mentioned in this function is
puzzling.  It doesn’t take the function name into account, does it?

Ludo’.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Complete './pre-inst-env guix ...'
  2015-06-15 20:16                 ` Ludovic Courtès
@ 2015-06-16 17:23                   ` Alex Kost
  0 siblings, 0 replies; 12+ messages in thread
From: Alex Kost @ 2015-06-16 17:23 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guix-devel

Ludovic Courtès (2015-06-15 23:16 +0300) wrote:

> Alex Kost <alezost@gmail.com> skribis:
>
>> So (analogously to 'pcomplete/sudo') the following function may be used
>> to complete "pre-inst-env" properly:
>>
>> (defun pcomplete/pre-inst-env ()
>>   (pcmpl-args-pcomplete
>>    (pcmpl-args-make-argspecs
>>     `((argument 0 (("COMMAND" nil))
>>                 :subparser pcmpl-args-command-subparser)))))
>
> Indeed, that works like a charm, thanks!
>
> Now, the fact that ‘pre-inst-env’ is not mentioned in this function is
> puzzling.  It doesn’t take the function name into account, does it?

Yes, that's how 'pcomplete' works.  To make a completion for some "foo"
command, you define 'pcomplete/foo' function and that's all.

-- 
Alex

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2015-06-16 17:23 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-06-06 20:44 [PATCH 2/2] emacs: Add shell completions for "guix" command Alex Kost
2015-06-07 15:36 ` Ludovic Courtès
2015-06-07 19:38   ` Ludovic Courtès
2015-06-08  8:52   ` Alex Kost
2015-06-08 19:19     ` Ludovic Courtès
2015-06-10  7:31       ` Complete './pre-inst-env guix ...' Alex Kost
2015-06-10 19:44         ` Ludovic Courtès
2015-06-11 14:41           ` Alex Kost
2015-06-14 22:07             ` Ludovic Courtès
2015-06-15 12:43               ` Alex Kost
2015-06-15 20:16                 ` Ludovic Courtès
2015-06-16 17:23                   ` Alex Kost

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).