Sarcasm / irony-mode

A C/C++ minor mode for Emacs powered by libclang
GNU General Public License v3.0
909 stars 99 forks source link

How to check and install irony server from .emacs #366

Open nickbroon opened 7 years ago

nickbroon commented 7 years ago

I naively tried this:

(use-package irony
  :ensure t
  :config
  (unless (irony--locate-server-executable)
    (irony-install-server))
  )

but that fails with:

Error (use-package): irony :config: Wrong number of arguments: (lambda (command) "Install or reinstall the Irony server.

The installation requires CMake and the libclang developpement package." (interactive (list (let ((command (format (concat "%s %s %s && %s --build . " "--use-stderr --config Release --target install") (shell-quote-argument irony-cmake-executable) (shell-quote-argument (concat "-DCMAKE_INSTALL_PREFIX=" (expand-file-name irony-server-install-prefix))) (shell-quote-argument irony-server-source-dir) (shell-quote-argument irony-cmake-executable)))) (irony--install-server-read-command command)))) (let ((build-dir (or irony-server-build-dir (concat (file-name-as-directory temporary-file-directory) (file-name-as-directory (format "build-irony-server-%s" (irony-version))))))) (make-directory build-dir t) (let ((default-directory build-dir)) (irony-server-kill) (save-current-buffer (set-buffer (compilation-start command nil (function (lambda (maj-mode) "*irony-server build*")))) (set (make-local-variable (quote compilation-finish-functions)) (quote (irony--server-install-finish-function))))))), 0

Is there a more appropriate way to check to see if the server is installed, and if not install it?

Sarcasm commented 7 years ago

The signature of the function is (irony-install-server COMMAND): You can try to specify the default command line manually.

nickbroon commented 7 years ago

Is the default command line defined in the code anywhere, so that it can be used?

Sarcasm commented 7 years ago

You can look at the code with M-x find-function RET irony-install-server RET.

This is not a simple string, it depends on some other variables but it should work for a given .emacs.

nickbroon commented 7 years ago

I see that command is built here: https://github.com/Sarcasm/irony-mode/blob/ebc373b0b4f2b3f491c91d3a7f6dc9ce565a2960/irony.el#L490 But that is private to the irony-install-server function. Could it be hoisted out of the function, so that can the be reused?

nickbroon commented 7 years ago

Just as an FYI, this started as part of this discussion: https://www.reddit.com/r/emacs/comments/5p475u/is_there_a_way_to_invoke_a_command_only_when_the/dewrg9c/

Sarcasm commented 7 years ago

Ok, thanks for the link. I guess it's slightly similar to a recent on Emacs-devel regarding the installation of modules: https://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00653.html

Anyway, for irony-mode I think the best is to simply make the command argument optional (unless this does not play well with interactive but I doubt it).

You could try to change the signature locally to something like this:

(defun irony-install-server (&optional command)
   ;; detect command == nil, and use the default command in this situation
   ...

You can try that locally and propose a pull request, I would gladly accept it. :+1:

I should really give use-package a try.

alexmurray commented 7 years ago

How about just:

(use-package irony
  :ensure t
  :config
  (unless (irony--locate-server-executable)
    (call-interactively #'irony-install-server)))
alexmurray commented 7 years ago

And if you want it to be fully automatic (so no need to hit enter to accept the default compile command for irony) - from http://emacs.stackexchange.com/a/10416/144:

;; -*- lexical-binding: t -*-
(defmacro with-minibuffer-input (form &rest inputs)
  (declare (indent 1))
  `(minibuffer-with-setup-hook
       (lambda ()
         (minibuffer-input-provider ',inputs))
     ,form))

(defun minibuffer-input-provider (inputs)
  (let ((hook (make-symbol "hook")))
    (fset hook (lambda ()
                 (remove-hook 'post-command-hook hook)
                 (when inputs
                   (when (= 0 (minibuffer-depth))
                     (error "Too many inputs"))
                   (when (cdr inputs)
                     (add-hook 'post-command-hook hook))
                   (insert (pop inputs))
                   (exit-minibuffer))))
    (add-hook 'post-command-hook hook)))

(use-package irony
  :ensure t
  :config
  (unless (irony--locate-server-executable)
    (with-minibuffer-input
    (call-interactively #'irony-install-server) "")))
nickbroon commented 7 years ago

@alexmurray Thanks for that. While that seems to work, it definitely strikes me as hack around irony-install-server. I think @Sarcasm suggestion of making the argument passed to irony-install-server optional is the correct approach, as it simplifies using this function programmatically. Hopefully I'll get some time to look at implementing the suggestion, though my elisp skills are weak.

kongds commented 7 years ago

@alexmurray I found the macro with-minibuffer-input can't work because some wrong in minibuffer-input-provider.

Maybe can change it like this.

(defun minibuffer-input-provider (inputs)
  (fset 'hook (lambda (hook_inputs)
                (eval `(remove-hook 'post-command-hook
                                    (lambda () (hook ',hook_inputs))) )
                (when hook_inputs
                  (when (= 0 (minibuffer-depth))
                    (error "Too many inputs"))
                  (if (cdr hook_inputs)
                      (eval `(add-hook 'post-command-hook
                                       (lambda () (hook ',(cdr hook_inputs)))))
                    (unintern "hook" nil))
                  (insert (pop hook_inputs))
                  (exit-minibuffer))))
  (eval `(add-hook 'post-command-hook
                   (lambda () (hook ',inputs)))))