From 3f4a0d5370ae6c34afe180df96add3b8522f4af1 Mon Sep 17 00:00:00 2001 From: mattkae Date: Wed, 11 May 2022 09:23:58 -0400 Subject: initial commit --- elpa/irony-20220110.849/irony.el | 917 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 917 insertions(+) create mode 100644 elpa/irony-20220110.849/irony.el (limited to 'elpa/irony-20220110.849/irony.el') diff --git a/elpa/irony-20220110.849/irony.el b/elpa/irony-20220110.849/irony.el new file mode 100644 index 0000000..f70eca3 --- /dev/null +++ b/elpa/irony-20220110.849/irony.el @@ -0,0 +1,917 @@ +;;; irony.el --- C/C++ minor mode powered by libclang + +;; Copyright (C) 2011-2016 Guillaume Papin + +;; Author: Guillaume Papin +;; Version: 1.5.0 +;; URL: https://github.com/Sarcasm/irony-mode +;; Compatibility: GNU Emacs 24.x +;; Keywords: c, convenience, tools +;; Package-Requires: ((cl-lib "0.5") (json "1.2")) + +;; This program 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. + +;; This program 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 . + +;;; Commentary: +;; +;; This file provides `irony-mode', a minor mode for C, C++ and Objective-C. +;; +;; Usage: +;; (add-hook 'c++-mode-hook 'irony-mode) +;; (add-hook 'c-mode-hook 'irony-mode) +;; (add-hook 'objc-mode-hook 'irony-mode) +;; +;; ;; Windows performance tweaks +;; ;; +;; (when (boundp 'w32-pipe-read-delay) +;; (setq w32-pipe-read-delay 0)) +;; ;; Set the buffer size to 64K on Windows (from the original 4K) +;; (when (boundp 'w32-pipe-buffer-size) +;; (setq irony-server-w32-pipe-buffer-size (* 64 1024))) +;; +;; See also: +;; - https://github.com/Sarcasm/company-irony +;; - https://github.com/Sarcasm/flycheck-irony +;; - https://github.com/Sarcasm/ac-irony + +;;; Code: + +(require 'irony-iotask) + +(autoload 'irony-completion--enter "irony-completion") +(autoload 'irony-completion--exit "irony-completion") + +(require 'cl-lib) + +(autoload 'find-library-name "find-func") +(autoload 'lm-version "lisp-mnt") + + +;; +;; Compatibility +;; + +(eval-and-compile + + ;; As seen in flycheck/magit + ;; + ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3). + (unless (fboundp 'setq-local) + (defmacro setq-local (var val) + "Set variable VAR to value VAL in current buffer." + (list 'set (list 'make-local-variable (list 'quote var)) val))) + + ;; Added in Emacs 24.3 (mirrors/emacs@b335efc3). + (unless (fboundp 'defvar-local) + (defmacro defvar-local (var val &optional docstring) + "Define VAR as a buffer-local variable with default value VAL. +Like `defvar' but additionally marks the variable as being +automatically buffer-local wherever it is set." + (declare (debug defvar) (doc-string 3)) + (list 'progn (list 'defvar var val docstring) + (list 'make-variable-buffer-local (list 'quote var))))) + + ) ;; eval-and-compile + + +;; +;; Customizable variables +;; + +(defgroup irony nil + "C/C++ minor mode powered by libclang." + :group 'c) + +(defcustom irony-lighter " Irony" + "Text to display in the mode line when irony mode is on." + :type 'string + :group 'irony) + +(defcustom irony-extra-cmake-args nil + "Extra arguments to CMake when compiling the server." + :type '(repeat string) + :group 'irony) + +(defcustom irony-user-dir (locate-user-emacs-file "irony/") + "Directory containing the Irony generated files. + +The slash is expected at the end." + :type 'directory + :risky t + :group 'irony) + +(defcustom irony-supported-major-modes '(c++-mode + c-mode + objc-mode) + "List of modes known to be compatible with Irony." + :type '(repeat symbol) + :group 'irony) + +;;;###autoload +(defcustom irony-additional-clang-options nil + "Additional command line options to pass down to libclang. + +Please, do NOT use this variable to add header search paths, only +additional warnings or compiler options. + +These compiler options will be prepended to the command line, in +order to not override the value coming from a compilation +database." + :type '(repeat string) + :options '("-Wdocumentation") + :group 'irony) + +(defcustom irony-lang-compile-option-alist + '((c++-mode . "c++") + (c-mode . "c") + (objc-mode . "objective-c")) + "Alist to decide the language option to used based on the `major-mode'." + :type '(alist :key-type symbol :value-type string) + :group 'irony) + +(defcustom irony-cmake-executable "cmake" + "Name or path of the CMake executable." + :type 'string + :group 'irony) + +(defcustom irony-server-source-dir nil + "Points to the irony-server source directory. + +This should point to the directory that contains the top-most +CMakeLists.txt used to build the server. + +By default it will find the directory based on the irony.el directory." + :type 'directory + :group 'irony + :package-version '(irony . "1.2.0")) + +(defcustom irony-server-build-dir nil + "Build directory for irony-server. + +If set to nil the default is to create a build directory in +`temporary-file-directory'/build-irony-server-`(irony-version)'." + :type 'directory + :group 'irony) + +(defcustom irony-server-install-prefix irony-user-dir + "Installation prefix used to install irony-server. + +The irony-server executable is expected to be in +`irony-server-install-prefix'/bin/." + :type 'directory + :group 'irony) + +(defcustom irony-server-w32-pipe-buffer-size nil + "Windows-only setting, +the buffer size to use for the irony-server process pipe on Windows. + +Larger values can improve performances on large buffers. + +If non-nil, `w32-pipe-buffer-size' will be let-bound to this value +during the creation of the irony-server process.") + + +;; +;; Public/API variables +;; +;; Non-customizable variables provided by Irony that can be useful to other +;; packages. +;; +;; Note that they shouldn't be modified directly by external packages, just +;; read. +;; + +;; TODO: make this variable public when the CDB API stabilizes. +(defvar-local irony--compile-options nil + "Compile options for the current file. + +The compile options used by the compiler to build the current +buffer file.") + +;; TODO: make this variable public when the CDB API stabilizes. +(defvar-local irony--working-directory nil + "The working directory to pass to libclang, if any.") + + +;; +;; Internal variables +;; +;; The prefix `irony--' is used when something can completely change (or +;; disappear) from one release to the other. +;; +;; -- https://lists.gnu.org/archive/html/emacs-devel/2013-06/msg01129.html + +(defconst irony--eot "\n;;EOT\n" + "String sent by the server to signal the end of a response.") + + +;; +;; Error conditions +;; + +;; `define-error' breaks backward compatibility with Emacs < 24.4 +(defun irony--define-error (name message &optional parent) + "Define NAME as a new error signal. +MESSAGE is a string that will be output to the echo area if such an error +is signaled without being caught by a `condition-case'. +PARENT is either a signal or a list of signals from which it inherits. +Defaults to `error'." + (unless parent (setq parent 'error)) + (let ((conditions + (if (consp parent) + (apply #'nconc + (mapcar (lambda (parent) + (cons parent + (or (get parent 'error-conditions) + (error "Unknown signal `%s'" parent)))) + parent)) + (cons parent (get parent 'error-conditions))))) + (put name 'error-conditions + (delete-dups (copy-sequence (cons name conditions)))) + (when message (put name 'error-message message)))) + +(irony--define-error 'irony-error "Irony-Mode error") +(irony--define-error 'irony-parse-error "Irony-Mode parsing error" 'irony-error) +(irony--define-error 'irony-server-error "Irony-Mode server error" 'irony-error) + + +;; +;; Utility functions & macros +;; + +;; TODO: remove and use `if-let' when supported version jumps to Emacs 25.1 +(defmacro irony--aif (test if-expr &rest else-body) + (declare (indent 2)) + `(let ((it ,test)) + (if it + ,if-expr + (progn ,@else-body)))) + +;; TODO: remove and use `when-let' when supported version jumps to Emacs 25.1 +(defmacro irony--awhen (test &rest body) + (declare (indent 1)) + `(let ((it ,test)) + (when it + (progn ,@body)))) + +(defun irony--assoc-all (key list) + (delq nil (mapcar (lambda (c) + (when (equal (car c) key) + c)) + list))) + +(defmacro irony--without-narrowing (&rest body) + "Remove the effect of narrowing for the current buffer. + +Note: If `save-excursion' is needed for BODY, it should be used +before calling this macro." + (declare (indent 0) (debug t)) + `(save-restriction + (widen) + (progn ,@body))) + +(defun irony--buffer-size-in-bytes () + "Return the buffer size, in bytes." + (1- (position-bytes (point-max)))) + +(defun irony--read-char-choice (prompt chars) + "Wrapper around `read-char-choice', available since Emacs 24." + (setq prompt (concat prompt " [" chars "]: ")) + (if (fboundp 'read-char-choice) + (read-char-choice prompt chars) + (setq prompt (propertize prompt 'face 'minibuffer-prompt)) + (let ((cursor-in-echo-area t) + k) + (while (not (member k chars)) + (setq k (read-char-exclusive prompt))) + k))) + +(defun irony--shorten-path (path) + "Make PATH as short as possible. + +The given path can be considered understandable by human but not +necessary a valid path string to use in code. Its only purpose is +to be displayed to the user." + (let ((relative (file-relative-name path)) + (abbreviated (abbreviate-file-name path))) + (if (< (string-width relative) (string-width abbreviated)) + relative + abbreviated))) + +(defun irony--split-command-line-1 (quoted-str) + "Remove the escaped quotes and backlash from a QUOTED-STR. + +Return a list of the final characters in the reverse order. + +Only to be consumed by `irony--split-command-line'." + (let ((len (length quoted-str)) + (i 0) + ch next-ch + result) + (while (< i len) + (setq ch (aref quoted-str i)) + (when (eq ch ?\\) + (let ((next-ch (and (< (1+ i) len) + (aref quoted-str (1+ i))))) + (when (member next-ch '(?\\ ?\")) + (setq ch next-ch) + (cl-incf i)))) + (push ch result) + (cl-incf i)) + result)) + +;; TODO: rewrite the function correctly to handle things like the following: +;; +;; "/usr/bin/clang++ -Irelative -DSOMEDEF=\"With spaces, quotes and \\-es.\" " +(defun irony--split-command-line (cmd-line) + "Split CMD-LINE into a list of arguments. + +Takes care of double quotes as well as backslash. + +Sadly I had to write this because `split-string-and-unquote' +breaks with escaped quotes in compile_commands.json, such as in: + + /usr/bin/c++ -DLLVM_VERSION_INFO=\\\\\\\"3.2svn\\\\\\\" " + ;; everytime I write a function like this one, it makes me feel bad + (let* ((len (length cmd-line)) + (spaces (string-to-list " \f\t\n\r\v")) + (first-not-spaces-re (concat "[^" spaces "]")) + (i 0) + ch + args cur-arg) + (while (< i len) + (setq ch (aref cmd-line i)) + (cond + ((member ch spaces) ;spaces + (when cur-arg + (setq args (cons (apply 'string (nreverse cur-arg)) args) + cur-arg nil)) + ;; move to the next char + (setq i (or (string-match-p first-not-spaces-re cmd-line i) + len))) + ((eq ch ?\") ;quoted string + (let ((endq (string-match-p "[^\\]\"" cmd-line i))) + (unless endq + (signal 'irony-parse-error (list "ill formed command line" cmd-line))) + (let ((quoted-str (substring cmd-line (1+ i) (1+ endq)))) + (setq cur-arg (append (irony--split-command-line-1 quoted-str) + cur-arg) + i (+ endq 2))))) + (t ;a valid char + ;; if it's an escape of: a backslash, a quote or a space push + ;; only the following char. + (when (eq ch ?\\) + (let ((next-ch (and (< (1+ i) len) + (aref cmd-line (1+ i))))) + (when (or (member next-ch '(?\\ ?\")) + (member next-ch spaces)) + (setq ch next-ch) + (cl-incf i)))) + (push ch cur-arg) + (cl-incf i)))) + (when cur-arg + (setq args (cons (apply 'string (nreverse cur-arg)) args))) + (nreverse args))) + +(defun irony--get-buffer-path-for-server (&optional buffer) + "Get the path of the current buffer to send to irony-server. + +If no such file exists on the filesystem the special file '-' is + returned instead." + (let ((file (buffer-file-name buffer))) + (if (and file (file-exists-p file)) + file + "-"))) + + +;; +;; Mode +;; + +(defvar irony-mode-map (make-sparse-keymap) + "Keymap used in `irony-mode' buffers.") + +;;;###autoload +(define-minor-mode irony-mode + "Minor mode for C, C++ and Objective-C, powered by libclang." + nil + irony-lighter + irony-mode-map + :group 'irony + (if irony-mode + (irony--mode-enter) + (irony--mode-exit))) + +(defun irony--mode-enter () + ;; warn the user about modes such as php-mode who inherits c-mode + (when (not (memq major-mode irony-supported-major-modes)) + (display-warning 'irony "Major mode is unknown to Irony,\ + see `irony-supported-major-modes'.")) + ;; warn the user about Windows-specific issues + (when (eq system-type 'windows-nt) + (cond + ((version< emacs-version "24.4") + (display-warning 'irony "Emacs >= 24.4 expected on Windows.")) + ((and (boundp 'w32-pipe-read-delay) (> w32-pipe-read-delay 0)) + (display-warning 'irony "Performance will be bad because a\ + pipe delay is set for this platform (see variable\ + `w32-pipe-read-delay').")))) + (irony-completion--enter)) + +(defun irony--mode-exit () + (irony-completion--exit)) + +;;;###autoload +(defun irony-version (&optional show-version) + "Return the version number of the file irony.el. + +If called interactively display the version in the echo area." + (interactive (list t)) + ;; Shamelessly stolen from `company-mode'. + (with-temp-buffer + (insert-file-contents (find-library-name "irony")) + (let ((v (lm-version))) + (when show-version + (message "irony version: %s" v)) + v))) + + +;; +;; Compile options handling +;; + +(defun irony--lang-compile-option () + (irony--awhen (cdr-safe (assq major-mode irony-lang-compile-option-alist)) + (list "-x" it))) + +(defun irony--extract-working-directory-option (flags) + "Return working directory specified on the command line, if +any." + (catch 'found + (while flags + (let ((flag (car flags))) + (cond + ((string= "-working-directory" flag) + (throw 'found (cadr flags))) + ((string-prefix-p "-working-directory=" flag) + (throw 'found (substring flag (length "-working-directory=")))) + (t + (setq flags (cdr flags)))))))) + +(defun irony--adjust-compile-options () + "The compile options to send to libclang." + ;; TODO: if current buffer has no associated file (will be sent as '-') but is + ;; in an existing directory, we will want to add -I (directory-file-name + ;; buffer-file-name) to find the relative headers + (append + (irony--lang-compile-option) + (irony--awhen irony--working-directory + (unless (irony--extract-working-directory-option irony--compile-options) + (list "-working-directory" it))) + irony-additional-clang-options + irony--compile-options)) + +(defun irony--extract-user-search-paths (compile-options work-dir) + "Retrieve the user search paths present in COMPILE-OPTIONS. + +Relative paths are expanded to be relative to WORK-DIR. + +The returned paths are returned as +directory (`file-name-as-directory'). + +Note: WORK-DIR is not used when the compile option +'-working-directory=' is detected in COMPILE-OPTIONS." + (setq work-dir (or (irony--extract-working-directory-option compile-options) + work-dir)) + (let (include-dirs opt) + (while (setq opt (car compile-options)) + (cond + ((string= "-I" opt) + (add-to-list 'include-dirs (nth 1 compile-options) t) + (setq compile-options (cddr compile-options))) + ((string-prefix-p "-I" opt) + (add-to-list 'include-dirs (substring opt 2) t) + (setq compile-options (cdr compile-options))) + (t + (setq compile-options (cdr compile-options))))) + (delete-dups (mapcar #'(lambda (path) + (file-name-as-directory + (expand-file-name path work-dir))) + include-dirs)))) + + +;; +;; Irony-Server setup +;; + +(defvar irony--server-install-command-history nil) +(defun irony--install-server-read-command (command) + (read-shell-command + "Install command: " command + (if (equal (car irony--server-install-command-history) command) + '(irony--server-install-command-history . 1) + 'irony--server-install-command-history))) + +(defun irony-install-server (command) + "Install or reinstall the Irony server. + +The installation requires CMake and the libclang development package." + (interactive + (list (let ((command + (format + (concat "%s %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))) + (mapconcat 'shell-quote-argument irony-extra-cmake-args " ") + (shell-quote-argument + (or irony-server-source-dir + (expand-file-name "server" + (file-name-directory + (find-library-name "irony"))))) + (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)) + ;; we need to kill the process to be able to install a new one, + ;; at least on Windows + (irony-server-kill) + (with-current-buffer (compilation-start command nil + #'(lambda (maj-mode) + "*irony-server build*")) + (setq-local compilation-finish-functions + '(irony--server-install-finish-function)))))) + +(defun irony--server-install-finish-function (buffer msg) + (if (string= "finished\n" msg) + (message "irony-server installed successfully!") + (message "Failed to build irony-server, you are on your own buddy!"))) + +(defun irony--find-server-executable () + "Return the path to the irony-server executable. + +Throw an `irony-server-error' if a proper executable cannot be +found." + (let* ((exec-path (cons (expand-file-name "bin" irony-server-install-prefix) + exec-path)) + (exe (executable-find "irony-server"))) + (condition-case err + (let ((version (car (process-lines exe "--version")))) + (if (and (string-match "^irony-server version " version) + (version= (irony-version) + (substring version + (length "irony-server version ")))) + ;; irony-server is working and up-to-date! + exe + (signal 'irony-server-error + (list + (format "irony-server version mismatch: %s" + (substitute-command-keys + "type `\\[irony-install-server]' to reinstall")))))) + (irony-server-error + (signal (car err) (cdr err))) + (error + (signal 'irony-server-error + (if (and exe + (file-executable-p exe)) + ;; failed to execute due to a runtime problem, i.e: + ;; libclang.so isn't in the ld paths + (list (format "irony-server is broken! %s" + (error-message-string err))) + ;; irony-server doesn't exists, first time irony-mode is used? + ;; inform the user about how to build the executable + (list + (format "irony-server not installed! %s" + (substitute-command-keys + "Type `\\[irony-install-server]' to install"))))))))) + + +;; +;; irony-server process management. +;; + +(defvar irony--server-executable nil) +(defvar irony--server-process nil) +(defvar irony--server-buffer " *Irony*" + "The name of the buffer for the irony process to run in. + +When using a leading space, the buffer is hidden from the buffer +list (and undo information is not kept).") + +(defun irony--start-server-process () + (unless irony--server-executable + ;; if not found, an `irony-server-error' error is signaled + (setq irony--server-executable (irony--find-server-executable))) + (let ((process-connection-type nil) + (process-adaptive-read-buffering nil) + (w32-pipe-buffer-size (when (boundp 'w32-pipe-buffer-size) + (or irony-server-w32-pipe-buffer-size + w32-pipe-buffer-size))) + process) + (setq process + (start-process-shell-command + "Irony" ;process name + irony--server-buffer ;buffer + (format "%s -i 2> %s" ;command + (shell-quote-argument irony--server-executable) + (expand-file-name + (format-time-string "irony.%Y-%m-%d_%Hh-%Mm-%Ss.log") + temporary-file-directory)))) + (set-process-query-on-exit-flag process nil) + (irony-iotask-setup-process process) + process)) + +;;;###autoload +(defun irony-server-kill () + "Kill the running irony-server process, if any." + (interactive) + (when (process-live-p irony--server-process) + (kill-process irony--server-process) + (setq irony--server-process nil))) + +(defun irony--get-server-process-create () + (unless (process-live-p irony--server-process) + (setq irony--server-process (irony--start-server-process))) + irony--server-process) + +(defun irony--run-task (task) + (irony-iotask-run (irony--get-server-process-create) task)) + +(defun irony--run-task-asynchronously (task callback) + (irony-iotask-schedule (irony--get-server-process-create) task callback)) + +(defun irony--quote-strings (strings &optional separator) + "Like `combine-and-quote-strings', but when the string is \"\" or nil, +`irony--quote-strings' will convert it to \"\" instead of . +That is: + + (irony--quote-strings \'(\"a\" \"\" \"b\")) => \"a \\\"\\\" b\" + (combine-and-quote-strings \'(\"a\" \"\" \"b\")) => \"a b\" +" + (let* ((sep (or separator " ")) + (re (concat "[\\\"]" "\\|" (regexp-quote sep)))) + (mapconcat + (lambda (str) + (cond + ((or (not str) (string= str "")) + "\"\"") + ((string-match re str) + (concat "\"" (replace-regexp-in-string "[\\\"]" "\\\\\\&" str) "\"")) + (t str))) + strings sep))) + +(defun irony--server-send-command (command &rest args) + (let ((command-line (concat (irony--quote-strings + (mapcar (lambda (arg) + (if (numberp arg) + (number-to-string arg) + arg)) + (cons command args))) + "\n"))) + (irony-iotask-send-string command-line))) + +;; XXX: this code can run in very tight very sensitive on big inputs, +;; every change should be measured +(defun irony--server-command-update (&rest _args) + (when (and (>= (buffer-size) (length irony--eot)) + (string-equal (buffer-substring-no-properties + (- (point-max) (length irony--eot)) (point-max)) + irony--eot)) + (condition-case-unless-debug nil + (let ((result (read (current-buffer)))) + (cl-case (car result) + (success + (irony-iotask-set-result (cdr result))) + (error + (apply #'irony-iotask-set-error 'irony-server-error + (cdr result))))) + (error + (throw 'invalid-msg t))))) + +;; FIXME: code duplication with `irony--server-command-update' +;; XXX: this code can run in very tight very sensitive on big inputs, +;; every change should be measured +(defun irony--server-query-update (&rest _args) + (when (and (>= (buffer-size) (length irony--eot)) + (string-equal (buffer-substring-no-properties + (- (point-max) (length irony--eot)) (point-max)) + irony--eot)) + (condition-case-unless-debug nil + (irony-iotask-set-result (read (current-buffer))) + (error + (throw 'invalid-msg t))))) + + +;; +;; Server commands +;; + +(irony-iotask-define-task irony--t-get-compile-options + "`get-compile-options' server command." + :start (lambda (build-dir file) + (irony--server-send-command "get-compile-options" build-dir file)) + :update irony--server-command-update) + +(defun irony--get-compile-options-task (build-dir file) + (irony-iotask-package-task irony--t-get-compile-options build-dir file)) + +(cl-defstruct (irony--buffer-state + (:constructor irony--buffer-state-create-1)) + file + exists + modified + tick) + +(defun irony--buffer-state-create (buffer) + (let ((file (buffer-file-name buffer))) + (irony--buffer-state-create-1 :file file + :exists (and file (file-exists-p file)) + :modified (buffer-modified-p buffer) + :tick (buffer-chars-modified-tick buffer)))) + +(defun irony--buffer-state-compare (old new) + (unless (equal old new) + (cond + ((irony--buffer-state-modified new) 'set-unsaved) + ((null old) nil) ;noop + ((and + (irony--buffer-state-modified old) + (irony--buffer-state-exists old)) 'reset-unsaved)))) + +(irony-iotask-define-task irony--t-set-unsaved + "`set-unsaved' server command." + :start (lambda (process buffer buf-state) + (let ((elem (assq buffer (process-get process :unsaved-buffers))) + temp-file) + (if (eq (cdr elem) buf-state) + ;; early exit if already cached + (irony-iotask-set-result t) + (setq temp-file (make-temp-file "irony-unsaved-")) + (irony-iotask-put :temp-file temp-file) + (irony-iotask-put :buffer-state buf-state) + (process-put process :buffer-state buf-state) + (with-current-buffer buffer + (irony--without-narrowing + (let ((write-region-inhibit-fsync t)) + (write-region nil nil temp-file nil 0))) + (irony--server-send-command "set-unsaved" + (irony--get-buffer-path-for-server) + temp-file))))) + :update irony--server-command-update + :finish (lambda (&rest _args) + (delete-file (irony-iotask-get :temp-file))) + :on-success + (lambda (process buffer &rest _args) + (let* ((unsaved-buffers (process-get process :unsaved-buffers)) + (elem (assq buffer unsaved-buffers)) + (buf-state (irony-iotask-get :buffer-state))) + (if elem + (setcdr elem buf-state) + (process-put process :unsaved-buffers (cons (cons buffer buf-state) + unsaved-buffers)))))) + +(defun irony--set-unsaved-task (process buffer buf-state) + (irony-iotask-package-task irony--t-set-unsaved process buffer buf-state)) + +(irony-iotask-define-task irony--t-reset-unsaved + "`reset-unsaved' server command." + :start (lambda (process buffer) + (if (assq buffer (process-get process :unsaved-buffers)) + (irony--server-send-command "reset-unsaved" + (irony--get-buffer-path-for-server + buffer)) + ;; exit early if already reset + (irony-iotask-set-result t))) + :update irony--server-command-update + :finish (lambda (process buffer) + (process-put + process + :unsaved-buffers + (assq-delete-all buffer (process-get process :unsaved-buffers))))) + +(defun irony--reset-unsaved-task (process buffer) + (irony-iotask-package-task irony--t-reset-unsaved process buffer)) + +(defun irony--list-unsaved-irony-mode-buffers (&optional ignore-list) + (delq nil (mapcar (lambda (buf) + (unless (memq buf ignore-list) + (when (buffer-modified-p buf) + (with-current-buffer buf + (and irony-mode buf))))) + (buffer-list)))) + +(defun irony--get-buffer-change-alist (process) + "Return a list of (buffer . old-state). + +old-state can be nil if the old state isn't known." + (let ((unsaved-list (process-get process :unsaved-buffers))) + (append unsaved-list + (mapcar (lambda (buf) + (cons buf nil)) + (irony--list-unsaved-irony-mode-buffers + (mapcar #'car unsaved-list)))))) + +(defun irony--unsaved-buffers-tasks () + (let ((process (irony--get-server-process-create)) + result) + (dolist (buffer-old-state-cons (irony--get-buffer-change-alist process) + result) + (let* ((buffer (car buffer-old-state-cons)) + (old-state (cdr buffer-old-state-cons)) + (new-state (irony--buffer-state-create buffer)) + (task + (cl-case (irony--buffer-state-compare old-state new-state) + (set-unsaved + (irony--set-unsaved-task process buffer new-state)) + (reset-unsaved + (irony--reset-unsaved-task process buffer))))) + (when task + (setq result (if result + (irony-iotask-chain result task) + task))))))) + +(irony-iotask-define-task irony--t-parse + "`parse' server command." + :start (lambda (file compile-options) + (apply #'irony--server-send-command "parse" file "--" + compile-options)) + :update irony--server-command-update) + +(defun irony--parse-task-1 (&optional buffer) + (with-current-buffer (or buffer (current-buffer)) + (irony-iotask-package-task irony--t-parse + (irony--get-buffer-path-for-server) + (irony--adjust-compile-options)))) + +(defun irony--parse-task (&optional buffer) + (let ((unsaved-tasks (irony--unsaved-buffers-tasks)) + (parse-task (irony--parse-task-1 buffer))) + (if unsaved-tasks + (irony-iotask-chain unsaved-tasks parse-task) + parse-task))) + +(irony-iotask-define-task irony--t-diagnostics + "`parse' server command." + :start (lambda () + (irony--server-send-command "diagnostics")) + :update irony--server-query-update) + +(defun irony--diagnostics-task (&optional buffer) + (irony-iotask-chain + (irony--parse-task buffer) + (irony-iotask-package-task irony--t-diagnostics))) + +(irony-iotask-define-task irony--t-get-type + "`get-type' server command." + :start (lambda (line col) + (irony--server-send-command "get-type" line col)) + :update irony--server-query-update) + +(defun irony--get-type-task (&optional buffer pos) + (let ((line-column (irony--completion-line-column pos))) + (irony-iotask-chain + (irony--parse-task buffer) + (irony-iotask-package-task irony--t-get-type + (car line-column) (cdr line-column))))) + +;;;###autoload +(defun irony-get-type () + "Get the type of symbol under cursor." + (interactive) + (let ((types (irony--run-task (irony--get-type-task)))) + (unless types + (user-error "Type not found")) + (if (and (cdr types) (not (string= (car types) (cadr types)))) + (message "%s (aka '%s')" (car types) (cadr types)) + (message "%s" (car types))))) + +(defun irony-parse-buffer-async (&optional callback) + "Parse the current buffer sending results to an optional + CALLBACK function." + (irony--run-task-asynchronously (irony--parse-task) + (or callback #'ignore))) + +(provide 'irony) + +;; Local Variables: +;; byte-compile-warnings: (not cl-functions) +;; End: + +;;; irony.el ends here -- cgit v1.2.1