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/.dir-locals.el | 10 + elpa/irony-20220110.849/irony-autoloads.el | 169 ++++ .../irony-20220110.849/irony-cdb-clang-complete.el | 79 ++ .../irony-cdb-clang-complete.elc | Bin 0 -> 1655 bytes elpa/irony-20220110.849/irony-cdb-json.el | 336 ++++++++ elpa/irony-20220110.849/irony-cdb-json.elc | Bin 0 -> 10353 bytes elpa/irony-20220110.849/irony-cdb-libclang.el | 73 ++ elpa/irony-20220110.849/irony-cdb-libclang.elc | Bin 0 -> 1744 bytes elpa/irony-20220110.849/irony-cdb.el | 218 +++++ elpa/irony-20220110.849/irony-cdb.elc | Bin 0 -> 6228 bytes elpa/irony-20220110.849/irony-completion.el | 426 ++++++++++ elpa/irony-20220110.849/irony-completion.elc | Bin 0 -> 10814 bytes elpa/irony-20220110.849/irony-diagnostics.el | 90 ++ elpa/irony-20220110.849/irony-diagnostics.elc | Bin 0 -> 2215 bytes elpa/irony-20220110.849/irony-iotask.el | 441 ++++++++++ elpa/irony-20220110.849/irony-iotask.elc | Bin 0 -> 33819 bytes elpa/irony-20220110.849/irony-pkg.el | 13 + elpa/irony-20220110.849/irony-snippet.el | 135 +++ elpa/irony-20220110.849/irony-snippet.elc | Bin 0 -> 2997 bytes elpa/irony-20220110.849/irony.el | 917 +++++++++++++++++++++ elpa/irony-20220110.849/irony.elc | Bin 0 -> 31843 bytes elpa/irony-20220110.849/server/.clang-format | 8 + elpa/irony-20220110.849/server/.clang-tidy | 24 + elpa/irony-20220110.849/server/CMakeLists.txt | 33 + .../server/build-aux/run-clang-tidy/LICENSE.TXT | 43 + .../server/build-aux/run-clang-tidy/README | 12 + .../build-aux/run-clang-tidy/run-clang-tidy.py | 236 ++++++ .../server/cmake/CheckClangResourceDir.cmake | 90 ++ .../server/cmake/LibClangDiagnosticsChecker.cpp | 47 ++ .../server/cmake/modules/FindLibClang.cmake | 106 +++ elpa/irony-20220110.849/server/src/CMakeLists.txt | 111 +++ elpa/irony-20220110.849/server/src/Command.cpp | 278 +++++++ elpa/irony-20220110.849/server/src/Command.h | 73 ++ elpa/irony-20220110.849/server/src/Commands.def | 39 + elpa/irony-20220110.849/server/src/CompDBCache.cpp | 71 ++ elpa/irony-20220110.849/server/src/CompDBCache.h | 86 ++ elpa/irony-20220110.849/server/src/Irony.cpp | 638 ++++++++++++++ elpa/irony-20220110.849/server/src/Irony.h | 147 ++++ elpa/irony-20220110.849/server/src/Style.h | 17 + elpa/irony-20220110.849/server/src/TUManager.cpp | 182 ++++ elpa/irony-20220110.849/server/src/TUManager.h | 109 +++ elpa/irony-20220110.849/server/src/main.cpp | 235 ++++++ .../irony-20220110.849/server/src/support/CIndex.h | 33 + .../server/src/support/CommandLineParser.cpp | 119 +++ .../server/src/support/CommandLineParser.h | 21 + .../server/src/support/NonCopyable.h | 34 + .../server/src/support/TemporaryFile.cpp | 74 ++ .../server/src/support/TemporaryFile.h | 36 + .../server/src/support/iomanip_quoted.h | 52 ++ elpa/irony-20220110.849/server/test/CMakeLists.txt | 3 + .../server/test/elisp/CMakeLists.txt | 47 ++ .../server/test/elisp/irony-cdb-json.el | 87 ++ .../server/test/elisp/irony-iotask.el | 249 ++++++ elpa/irony-20220110.849/server/test/elisp/irony.el | 121 +++ .../server/test/elisp/test-config.el | 14 + 55 files changed, 6382 insertions(+) create mode 100644 elpa/irony-20220110.849/.dir-locals.el create mode 100644 elpa/irony-20220110.849/irony-autoloads.el create mode 100644 elpa/irony-20220110.849/irony-cdb-clang-complete.el create mode 100644 elpa/irony-20220110.849/irony-cdb-clang-complete.elc create mode 100644 elpa/irony-20220110.849/irony-cdb-json.el create mode 100644 elpa/irony-20220110.849/irony-cdb-json.elc create mode 100644 elpa/irony-20220110.849/irony-cdb-libclang.el create mode 100644 elpa/irony-20220110.849/irony-cdb-libclang.elc create mode 100644 elpa/irony-20220110.849/irony-cdb.el create mode 100644 elpa/irony-20220110.849/irony-cdb.elc create mode 100644 elpa/irony-20220110.849/irony-completion.el create mode 100644 elpa/irony-20220110.849/irony-completion.elc create mode 100644 elpa/irony-20220110.849/irony-diagnostics.el create mode 100644 elpa/irony-20220110.849/irony-diagnostics.elc create mode 100644 elpa/irony-20220110.849/irony-iotask.el create mode 100644 elpa/irony-20220110.849/irony-iotask.elc create mode 100644 elpa/irony-20220110.849/irony-pkg.el create mode 100644 elpa/irony-20220110.849/irony-snippet.el create mode 100644 elpa/irony-20220110.849/irony-snippet.elc create mode 100644 elpa/irony-20220110.849/irony.el create mode 100644 elpa/irony-20220110.849/irony.elc create mode 100644 elpa/irony-20220110.849/server/.clang-format create mode 100644 elpa/irony-20220110.849/server/.clang-tidy create mode 100644 elpa/irony-20220110.849/server/CMakeLists.txt create mode 100644 elpa/irony-20220110.849/server/build-aux/run-clang-tidy/LICENSE.TXT create mode 100644 elpa/irony-20220110.849/server/build-aux/run-clang-tidy/README create mode 100755 elpa/irony-20220110.849/server/build-aux/run-clang-tidy/run-clang-tidy.py create mode 100644 elpa/irony-20220110.849/server/cmake/CheckClangResourceDir.cmake create mode 100644 elpa/irony-20220110.849/server/cmake/LibClangDiagnosticsChecker.cpp create mode 100644 elpa/irony-20220110.849/server/cmake/modules/FindLibClang.cmake create mode 100644 elpa/irony-20220110.849/server/src/CMakeLists.txt create mode 100644 elpa/irony-20220110.849/server/src/Command.cpp create mode 100644 elpa/irony-20220110.849/server/src/Command.h create mode 100644 elpa/irony-20220110.849/server/src/Commands.def create mode 100644 elpa/irony-20220110.849/server/src/CompDBCache.cpp create mode 100644 elpa/irony-20220110.849/server/src/CompDBCache.h create mode 100644 elpa/irony-20220110.849/server/src/Irony.cpp create mode 100644 elpa/irony-20220110.849/server/src/Irony.h create mode 100644 elpa/irony-20220110.849/server/src/Style.h create mode 100644 elpa/irony-20220110.849/server/src/TUManager.cpp create mode 100644 elpa/irony-20220110.849/server/src/TUManager.h create mode 100644 elpa/irony-20220110.849/server/src/main.cpp create mode 100644 elpa/irony-20220110.849/server/src/support/CIndex.h create mode 100644 elpa/irony-20220110.849/server/src/support/CommandLineParser.cpp create mode 100644 elpa/irony-20220110.849/server/src/support/CommandLineParser.h create mode 100644 elpa/irony-20220110.849/server/src/support/NonCopyable.h create mode 100644 elpa/irony-20220110.849/server/src/support/TemporaryFile.cpp create mode 100644 elpa/irony-20220110.849/server/src/support/TemporaryFile.h create mode 100644 elpa/irony-20220110.849/server/src/support/iomanip_quoted.h create mode 100644 elpa/irony-20220110.849/server/test/CMakeLists.txt create mode 100644 elpa/irony-20220110.849/server/test/elisp/CMakeLists.txt create mode 100644 elpa/irony-20220110.849/server/test/elisp/irony-cdb-json.el create mode 100644 elpa/irony-20220110.849/server/test/elisp/irony-iotask.el create mode 100644 elpa/irony-20220110.849/server/test/elisp/irony.el create mode 100644 elpa/irony-20220110.849/server/test/elisp/test-config.el (limited to 'elpa/irony-20220110.849') diff --git a/elpa/irony-20220110.849/.dir-locals.el b/elpa/irony-20220110.849/.dir-locals.el new file mode 100644 index 0000000..5e9bd93 --- /dev/null +++ b/elpa/irony-20220110.849/.dir-locals.el @@ -0,0 +1,10 @@ +((nil + (indent-tabs-mode . nil)) + (c++-mode + (c-basic-offset . 2) + (c-file-style . "gnu") + (eval . (c-set-offset 'innamespace 0)))) + +;; Local Variables: +;; no-byte-compile: t +;; End diff --git a/elpa/irony-20220110.849/irony-autoloads.el b/elpa/irony-20220110.849/irony-autoloads.el new file mode 100644 index 0000000..8e7cf2b --- /dev/null +++ b/elpa/irony-20220110.849/irony-autoloads.el @@ -0,0 +1,169 @@ +;;; irony-autoloads.el --- automatically extracted autoloads +;; +;;; Code: + +(add-to-list 'load-path (directory-file-name + (or (file-name-directory #$) (car load-path)))) + + +;;;### (autoloads nil "irony" "irony.el" (0 0 0 0)) +;;; Generated autoloads from irony.el + +(defvar 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.") + +(custom-autoload 'irony-additional-clang-options "irony" t) + +(autoload 'irony-mode "irony" "\ +Minor mode for C, C++ and Objective-C, powered by libclang. + +If called interactively, enable Irony mode if ARG is positive, +and disable it if ARG is zero or negative. If called from Lisp, +also enable the mode if ARG is omitted or nil, and toggle it if +ARG is `toggle'; disable the mode otherwise. + +\(fn &optional ARG)" t nil) + +(autoload 'irony-version "irony" "\ +Return the version number of the file irony.el. + +If called interactively display the version in the echo area. + +\(fn &optional SHOW-VERSION)" t nil) + +(autoload 'irony-server-kill "irony" "\ +Kill the running irony-server process, if any." t nil) + +(autoload 'irony-get-type "irony" "\ +Get the type of symbol under cursor." t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony" '("irony-"))) + +;;;*** + +;;;### (autoloads nil "irony-cdb" "irony-cdb.el" (0 0 0 0)) +;;; Generated autoloads from irony-cdb.el + +(autoload 'irony-cdb-autosetup-compile-options "irony-cdb" nil t nil) + +(autoload 'irony-cdb-menu "irony-cdb" nil t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-cdb" '("irony-cdb-"))) + +;;;*** + +;;;### (autoloads nil "irony-cdb-clang-complete" "irony-cdb-clang-complete.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from irony-cdb-clang-complete.el + +(autoload 'irony-cdb-clang-complete "irony-cdb-clang-complete" "\ + + +\(fn COMMAND &rest ARGS)" nil nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-cdb-clang-complete" '("irony-cdb-clang-complete--"))) + +;;;*** + +;;;### (autoloads nil "irony-cdb-json" "irony-cdb-json.el" (0 0 0 +;;;;;; 0)) +;;; Generated autoloads from irony-cdb-json.el + +(autoload 'irony-cdb-json "irony-cdb-json" "\ + + +\(fn COMMAND &rest ARGS)" nil nil) + +(autoload 'irony-cdb-json-add-compile-commands-path "irony-cdb-json" "\ +Add an out-of-source compilation database. + +Files below the PROJECT-ROOT directory will use the JSON +Compilation Database as specified by COMPILE-COMMANDS-PATH. + +The JSON Compilation Database are often generated in the build +directory. This functions helps mapping out-of-source build +directories to project directory. + +\(fn PROJECT-ROOT COMPILE-COMMANDS-PATH)" t nil) + +(autoload 'irony-cdb-json-select "irony-cdb-json" "\ +Select CDB to use with a prompt. + +It is useful when you have several CDBs with the same project +root. + +The completion function used internally is `completing-read' so +it could easily be used with other completion functions by +temporarily using a let-bind on `completing-read-function'. Or +even helm by enabling `helm-mode' before calling the function." t nil) + +(autoload 'irony-cdb-json-select-most-recent "irony-cdb-json" "\ +Select CDB that is most recently modified." t nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-cdb-json" '("irony-cdb-json--"))) + +;;;*** + +;;;### (autoloads nil "irony-cdb-libclang" "irony-cdb-libclang.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from irony-cdb-libclang.el + +(autoload 'irony-cdb-libclang "irony-cdb-libclang" "\ + + +\(fn COMMAND &rest ARGS)" nil nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-cdb-libclang" '("irony-cdb-libclang--"))) + +;;;*** + +;;;### (autoloads nil "irony-completion" "irony-completion.el" (0 +;;;;;; 0 0 0)) +;;; Generated autoloads from irony-completion.el + +(autoload 'irony-completion-at-point "irony-completion" nil nil nil) + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-completion" '("irony-"))) + +;;;*** + +;;;### (autoloads nil "irony-diagnostics" "irony-diagnostics.el" +;;;;;; (0 0 0 0)) +;;; Generated autoloads from irony-diagnostics.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-diagnostics" '("irony-diagnostics-"))) + +;;;*** + +;;;### (autoloads nil "irony-iotask" "irony-iotask.el" (0 0 0 0)) +;;; Generated autoloads from irony-iotask.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-iotask" '("irony-iotask-"))) + +;;;*** + +;;;### (autoloads nil "irony-snippet" "irony-snippet.el" (0 0 0 0)) +;;; Generated autoloads from irony-snippet.el + +(if (fboundp 'register-definition-prefixes) (register-definition-prefixes "irony-snippet" '("irony-snippet-"))) + +;;;*** + +;;;### (autoloads nil nil ("irony-pkg.el") (0 0 0 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; coding: utf-8 +;; End: +;;; irony-autoloads.el ends here diff --git a/elpa/irony-20220110.849/irony-cdb-clang-complete.el b/elpa/irony-20220110.849/irony-cdb-clang-complete.el new file mode 100644 index 0000000..a9143a8 --- /dev/null +++ b/elpa/irony-20220110.849/irony-cdb-clang-complete.el @@ -0,0 +1,79 @@ +;;; irony-cdb-clang-complete.el --- .clang_complete compilation database + +;; Copyright (C) 2014 Guillaume Papin + +;; Author: Guillaume Papin +;; Keywords: c, convenience, tools + +;; 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 defines a compilation database for .clang_complete and +;; compile_flags.txt, both of which have the same format. +;; + +;;; Code: + +(require 'irony-cdb) + +(require 'cl-lib) + +;;;###autoload +(defun irony-cdb-clang-complete (command &rest args) + (cl-case command + (get-compile-options (irony-cdb-clang-complete--get-compile-options)))) + +(defun irony-cdb-clang-complete--get-compile-options () + (irony--awhen (irony-cdb-clang-complete--locate-db) + (irony-cdb-clang-complete--load-db it))) + +(defun irony-cdb-clang-complete--locate-db () + (when buffer-file-name + (catch 'fname + (locate-dominating-file + buffer-file-name + ;; locate-dominating-file will invoke the lambda on suitable + ;; directories, and if we have either of our files there, we + ;; return its filename, by throwing it. + (lambda (d) + (let ((cfname (concat (file-name-as-directory d) "compile_flags.txt")) + (ccname (concat (file-name-as-directory d) ".clang_complete"))) + (if (file-exists-p cfname) + (throw 'fname cfname) + (if (file-exists-p ccname) + (throw 'fname ccname) + nil)))))))) + +(defun irony-cdb-clang-complete--load-db (cc-file) + (with-temp-buffer + (insert-file-contents cc-file) + (list + (cons + ;; compile options with trailing whitespaces removed + (mapcar #'(lambda (line) + (if (string-match "[ \t]+$" line) + (replace-match "" t t line) + line)) + (split-string (buffer-string) "\n" t)) + ;; working directory + (expand-file-name (file-name-directory cc-file)))))) + +(provide 'irony-cdb-clang-complete) + +;; Local Variables: +;; byte-compile-warnings: (not cl-functions) +;; End: + +;;; irony-cdb-clang-complete ends here diff --git a/elpa/irony-20220110.849/irony-cdb-clang-complete.elc b/elpa/irony-20220110.849/irony-cdb-clang-complete.elc new file mode 100644 index 0000000..841def6 Binary files /dev/null and b/elpa/irony-20220110.849/irony-cdb-clang-complete.elc differ diff --git a/elpa/irony-20220110.849/irony-cdb-json.el b/elpa/irony-20220110.849/irony-cdb-json.el new file mode 100644 index 0000000..f24e144 --- /dev/null +++ b/elpa/irony-20220110.849/irony-cdb-json.el @@ -0,0 +1,336 @@ +;;; irony-cdb-json.el --- JSON Compilation Database support for irony + +;; Copyright (C) 2014 Guillaume Papin + +;; Author: Guillaume Papin +;; Keywords: c, convenience, tools + +;; 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: +;; +;; JSON Compilation Database support for Irony, see +;; http://clang.llvm.org/docs/JSONCompilationDatabase.html. +;; + +;;; Code: + +(require 'irony-cdb) + +(require 'cl-lib) + +(require 'json) +(require 'pp) + +(defvar irony-cdb-json--project-alist nil + "Alist of source directory and compile_commands.json locations. + +Note, the compile_commands.json location may be relative to the +source directory.") + +(defconst irony-cdb-json--project-alist-file + (concat irony-user-dir "cdb-json-projects")) + +;;;###autoload +(defun irony-cdb-json (command &rest args) + (cl-case command + (get-compile-options (irony-cdb-json--get-compile-options)))) + +;;;###autoload +(defun irony-cdb-json-add-compile-commands-path (project-root + compile-commands-path) + "Add an out-of-source compilation database. + +Files below the PROJECT-ROOT directory will use the JSON +Compilation Database as specified by COMPILE-COMMANDS-PATH. + +The JSON Compilation Database are often generated in the build +directory. This functions helps mapping out-of-source build +directories to project directory." + (interactive + (progn + (let ((proot (read-directory-name "Project root:" nil nil t))) + (list proot (read-file-name "Compile commands:" proot nil t + "compile_commands.json"))))) + (add-to-list 'irony-cdb-json--project-alist + (cons (expand-file-name project-root) + (expand-file-name compile-commands-path))) + (irony-cdb-json--save-project-alist) + + ; and tell irony to load it now + (irony-cdb-autosetup-compile-options)) + +(defun irony-cdb-json--put-first (pos target-list) + (if (>= pos (length target-list)) + target-list + (let ((elm (nth pos target-list))) + (append (list elm) (delete elm target-list))))) + +(defun irony-cdb-json--choose-cdb () + "Prompt to select CDB from current project root." + (let* ((proot (irony-cdb-json--find-best-prefix-path + (irony-cdb-json--target-path) + (mapcar 'car irony-cdb-json--project-alist))) + (cdbs (mapcar 'cdr + (cl-remove-if-not (lambda (x) (string-equal proot (car x))) + irony-cdb-json--project-alist)))) + (completing-read "Choose Irony CDB: " cdbs nil 'require-match nil))) + +;;;###autoload +(defun irony-cdb-json-select () + "Select CDB to use with a prompt. + +It is useful when you have several CDBs with the same project +root. + +The completion function used internally is `completing-read' so +it could easily be used with other completion functions by +temporarily using a let-bind on `completing-read-function'. Or +even helm by enabling `helm-mode' before calling the function." + (interactive) + (let ((pos (cl-position (irony-cdb-json--choose-cdb) + irony-cdb-json--project-alist + :test (lambda (x y) (string-equal x (cdr y)))))) + (setq irony-cdb-json--project-alist + (irony-cdb-json--put-first pos irony-cdb-json--project-alist)) + (irony-cdb-json--save-project-alist) + (irony-cdb-autosetup-compile-options))) + +(defun irony-cdb-json--last-mod (file) + "File modification time or null time if file doesn't exist." + (or (nth 5 (file-attributes file)) + '(0 0 0 0))) + +;;;###autoload +(defun irony-cdb-json-select-most-recent () + "Select CDB that is most recently modified." + (interactive) + (setq irony-cdb-json--project-alist + (sort irony-cdb-json--project-alist + (lambda (x y) + (time-less-p (irony-cdb-json--last-mod (cdr y)) + (irony-cdb-json--last-mod (cdr x)))))) + (irony-cdb-json--save-project-alist) + (irony-cdb-autosetup-compile-options)) + +(defun irony-cdb-json--get-compile-options () + (irony--awhen (irony-cdb-json--locate-db) + (let ((db (irony-cdb-json--load-db it))) + (irony--aif (irony-cdb-json--exact-flags db) + it + (let ((dir-cdb (irony-cdb-json--compute-directory-cdb db))) + (irony-cdb-json--guess-flags dir-cdb)))))) + +(defsubst irony-cdb-json--target-path () + (or buffer-file-name (expand-file-name default-directory))) + +(defun irony-cdb-json--ensure-project-alist-loaded () + (unless irony-cdb-json--project-alist + (irony-cdb-json--load-project-alist))) + +(defun irony-cdb-json--save-project-alist () + (with-temp-file irony-cdb-json--project-alist-file + (insert ";; -*- emacs-lisp -*-\n\ +;;\n\ +;; JSON Compilation Database project list.\n\ +;;\n\ +;; File auto-generated by irony-cdb-json.\n\ +;;\n") + (pp irony-cdb-json--project-alist (current-buffer)) + (insert "\n"))) + +(defun irony-cdb-json--load-project-alist () + (when (file-exists-p irony-cdb-json--project-alist-file) + (setq irony-cdb-json--project-alist + (with-temp-buffer + (insert-file-contents irony-cdb-json--project-alist-file) + (read (current-buffer)))))) + +(defun irony-cdb-json--find-best-prefix-path (file prefixes) + (cl-loop for prefix in prefixes + with found = nil + ;; keep the closest directory + if (and (string-prefix-p prefix file) + (> (length prefix) (length found))) + do (setq found prefix) + finally return found)) + +(defun irony-cdb-json--locate-db () + (irony-cdb-json--ensure-project-alist-loaded) + (irony--aif (irony-cdb-json--find-best-prefix-path + (irony-cdb-json--target-path) + (mapcar 'car irony-cdb-json--project-alist)) + (expand-file-name + (cdr (assoc it irony-cdb-json--project-alist)) + it) + ;; If not in the project table, look in the dominating directories + (irony--awhen (irony-cdb--locate-dominating-file-with-dirs + (irony-cdb-json--target-path) + "compile_commands.json" + irony-cdb-search-directory-list) + (expand-file-name it)))) + +(defvar irony-cdb-json--cache-key nil + "The name of the last loaded JSON file and its modification time.") +(defvar irony-cdb-json--cache-cdb nil + "The last loaded compilation database.") + +(defun irony-cdb-json--make-cache-key (file) + (irony--aif (file-attributes file) + (cons file (nth 5 it)))) + +(defun irony-cdb-json--load-db (json-file) + (let ((cache-key (irony-cdb-json--make-cache-key json-file))) + (unless (and cache-key (equal irony-cdb-json--cache-key cache-key)) + (setq irony-cdb-json--cache-cdb + (delq nil (mapcar #'irony-cdb-json--transform-compile-command + ;; JSON read may throw + (json-read-file json-file)))) + (setq irony-cdb-json--cache-key cache-key))) + irony-cdb-json--cache-cdb) + +(defun irony-cdb-json--exact-flags (file-cdb) + (when buffer-file-name + (mapcar #'(lambda (e) + (cons (nth 1 e) (nth 2 e))) + (irony--assoc-all buffer-file-name file-cdb)))) + +(defun irony-cdb-json--guess-flags (dir-cdb) + (cl-loop for e in dir-cdb + with buf-path = (irony-cdb-json--target-path) + with found = nil + for dir = (car e) + ;; keep the closest directory + if (and (string-prefix-p dir buf-path) + (> (length dir) (length (car found)))) + do (setq found e) + finally return (list (cons (nth 1 found) (nth 2 found))))) + +(defsubst irony-cdb-json--compile-command-directory (compile-command) + (cdr (assq 'directory compile-command))) + +(defsubst irony-cdb-json--compile-command-file (compile-command) + (cdr (assq 'file compile-command))) + +(defun irony-cdb-json--compile-command-options (compile-command) + "Return the compile options of COMPILE-COMMAND as a list." + (let ((command (assq 'command compile-command)) + (arguments (assq 'arguments compile-command))) + (irony-cdb--remove-compiler-from-flags + (cond (command (irony--split-command-line (cdr command))) + (arguments (append (cdr arguments) nil)))))) + +(defun irony-cdb-json--adjust-compile-options (compile-options file default-dir) + "Adjust COMPILE-OPTIONS to only use options useful for parsing. + +COMPILE-OPTIONS is modified by side effects but the returned list +should be used since elements can change at the head. + +Removes the input file, the output file, ... + +Relative paths are relative to DEFAULT-DIR." + ;; compute the truename of the absolute path for FILE only once + (setq file (file-truename (expand-file-name file default-dir))) + (let* ((head (cons 'nah compile-options)) + (it head) + opt) + (while (setq opt (cadr it)) + (cond + ;; end of options, skip all positional arguments (source files) + ((string= opt "--") + (setcdr it nil)) + ;; strip -c + ((string= "-c" opt) + (setcdr it (nthcdr 2 it))) + ;; strip -o and -o + ((string-prefix-p "-o" opt) + (if (string= opt "-o") + (setcdr it (nthcdr 3 it)) + (setcdr it (nthcdr 2 it)))) + ;; skip input file; avoid invoking file commands if an option argument + ((and (not (string-prefix-p "-" opt)) (string= file (file-truename (expand-file-name opt default-dir)))) + (setcdr it (nthcdr 2 it))) + (t + ;; if head of cdr hasn't been skipped, iterate, otherwise check if the + ;; new cdr need skipping + (setq it (cdr it))))) + (cdr head))) + +(defun irony-cdb-json--transform-compile-command (compile-command) + "Transform a compile command in the JSON compilation database +into a friendlier format. + +The returned value is a list composed of the following elements: +0. The absolute path to the file. +1. The compile options. +2. The invocation directory. Relative paths in the compile + options elements are relative to this directory. + +Return nil if the compile command is invalid or the compile +options are empty." + (let* ((directory (irony-cdb-json--compile-command-directory compile-command)) + (path (expand-file-name + (irony-cdb-json--compile-command-file compile-command) directory)) + (options (irony-cdb-json--compile-command-options compile-command))) + (when (and path directory options) + (list path + (irony-cdb-json--adjust-compile-options options path directory) + directory)))) + +(defun irony-cdb-json--compute-directory-cdb (file-cdb) + ;; collect flags by directory, e.g: for headers in source directories or + ;; new files that are not yet present in the compilation database + (let ((dir-cdb (irony-cdb-json--collect-compile-options-by-dir file-cdb))) + (nconc dir-cdb + ;; collect flags for header search paths too + (irony-cdb-json--collect-compile-options-for-include-dirs dir-cdb)))) + +(defun irony-cdb-json--collect-compile-options-by-dir (file-cdb) + "Collect the compile options per directory from a file compilation database. + +The returned value similar to +`irony-cdb-json--transform-compile-command' except for the first +argument which represents a whole directory (ending with slash on +Unix, `file-name-as-directory') instead of a single file." + (let ((dir-cdb (delete-dups + (mapcar #'(lambda (e) + (cons (file-name-directory (car e)) (cdr e))) + file-cdb)))) + ;; TODO: remove directories when a parent directory has the same flags, for + ;; example, writing the following in CMake: + ;; add_executable(exe foo.cpp sub/bar.cpp) + ;; will result in duplicated compile options for the subdirectory 'sub/'. + dir-cdb)) + +(defun irony-cdb-json--collect-compile-options-for-include-dirs (dir-cdb) + "Guess the compile options to use for directories in the search path. + +The returned value is in the same format as the input value, see +`irony-cdb-json--collect-compile-options-for-include-dirs'." + (let ((include-dirs (delete-dups (mapcar 'car dir-cdb))) + out) + (dolist (e dir-cdb) + (dolist (dir (irony--extract-user-search-paths (nth 1 e) (nth 2 e))) + (unless (member dir include-dirs) + (setq include-dirs (cons dir include-dirs) + out (cons (cons dir (cdr e)) out))))) + out)) + +(provide 'irony-cdb-json) + +;; Local Variables: +;; byte-compile-warnings: (not cl-functions) +;; End: + +;;; irony-cdb-json ends here diff --git a/elpa/irony-20220110.849/irony-cdb-json.elc b/elpa/irony-20220110.849/irony-cdb-json.elc new file mode 100644 index 0000000..d43d948 Binary files /dev/null and b/elpa/irony-20220110.849/irony-cdb-json.elc differ diff --git a/elpa/irony-20220110.849/irony-cdb-libclang.el b/elpa/irony-20220110.849/irony-cdb-libclang.el new file mode 100644 index 0000000..79d5849 --- /dev/null +++ b/elpa/irony-20220110.849/irony-cdb-libclang.el @@ -0,0 +1,73 @@ +;;; irony-cdb-libclang.el --- Compilation Database for irony using libclang + +;; Copyright (C) 2015 Karl Hylén + +;; Author: Karl Hylén +;; Keywords: c, convenience, tools + +;; 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: +;; +;; Compilation Database support for Irony using libclangs CXCompilationDatabase, +;; http://clang.llvm.org/doxygen/group__COMPILATIONDB.html + +;;; Code: + +(require 'irony-cdb) +(require 'irony-cdb-json) + +(require 'cl-lib) + +;;;###autoload +(defun irony-cdb-libclang (command &rest args) + (cl-case command + (get-compile-options (irony-cdb-libclang--get-compile-options)))) + +(defun irony-cdb-libclang--get-compile-options () + (irony--awhen (irony-cdb-json--locate-db) + (irony-cdb-libclang--server-exact-flags it))) + +(defun irony-cdb-libclang--server-exact-flags (db-file) + "Get compilation options from irony-server. + +The parameter DB-FILE is the database file." + (when buffer-file-name + (let* ((build-dir (file-name-directory db-file)) + (file buffer-file-name) + (task (irony--get-compile-options-task build-dir file)) + (compile-options (irony--run-task task))) + (irony-cdb-libclang--adjust-options-and-remove-compiler + file compile-options)))) + +(defun irony-cdb-libclang--adjust-options-and-remove-compiler (file cmds) + "Remove compiler, target file FILE and output file from CMDS. + +The parameter CMDS is a list of conses. In each cons, the car holds the options +and the cdr holds the working directory where the compile command was issued." + (mapcar (lambda (cmd) + (let ((opt (irony-cdb--remove-compiler-from-flags (car cmd))) + (wdir (cdr cmd))) + (cons + (irony-cdb-json--adjust-compile-options opt file wdir) + wdir))) + cmds)) + +(provide 'irony-cdb-libclang) + +;; Local Variables: +;; byte-compile-warnings: (not cl-functions) +;; End: + +;;; irony-cdb-libclang ends here diff --git a/elpa/irony-20220110.849/irony-cdb-libclang.elc b/elpa/irony-20220110.849/irony-cdb-libclang.elc new file mode 100644 index 0000000..da23bf2 Binary files /dev/null and b/elpa/irony-20220110.849/irony-cdb-libclang.elc differ diff --git a/elpa/irony-20220110.849/irony-cdb.el b/elpa/irony-20220110.849/irony-cdb.el new file mode 100644 index 0000000..e7d678b --- /dev/null +++ b/elpa/irony-20220110.849/irony-cdb.el @@ -0,0 +1,218 @@ +;;; irony-cdb.el --- compilation databases support for irony + +;; Copyright (C) 2012-2014 Guillaume Papin + +;; Author: Guillaume Papin +;; Keywords: c, convenience, tools + +;; 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 defines the compilation database interface of irony-mode. +;; +;; Note:For compilation database that looks for a specific file, such as +;; .clang_complete or compile_commands.json, favor `locate-dominating-file' to a +;; handwritten logic if possible as it may be configured by the user to do "the +;; Right Thing (TM)". See `locate-dominating-stop-dir-regexp'. +;; + +;;; Code: + +(require 'irony) + +(require 'cl-lib) + +(autoload 'irony-cdb-clang-complete "irony-cdb-clang-complete") +(autoload 'irony-cdb-json "irony-cdb-json") +(autoload 'irony-cdb-libclang "irony-cdb-libclang") + + +;; +;; Customizable variables +;; + +(defgroup irony-cdb nil + "Irony's compilation database interface." + :group 'irony) + +(defcustom irony-cdb-compilation-databases '(irony-cdb-clang-complete + irony-cdb-libclang + irony-cdb-json) + "List of active compilation databases. + +The compilation database should respond for the following commands: + +`get-compile-options': Takes no argument. This function finds the +compile options used for the current buffer. It must return a +list of cons where the first element is a set of compile options +and the second element the working directory expected for these +commands. The compilation database should return an empty list +for files that it cannot handle." + :type '(repeat function) + :group 'irony-cdb) + +(defcustom irony-cdb-search-directory-list '("." "build") + "List of relative subdirectory paths to be searched for cdb files + +Irony looks for cdb files in any of the supported format by checking +each directory from the currently loaded file and recursively through +parent directories until it hits the root directory or a cdb is +found. At each level of the search irony looks at the subdirectories +listed in `irony-cdb-search-directory-list' for the files. Customize this +list if your cdb is held in a custom directory within you project, +such as a custom named build directory. +" + :type '(repeat string) + :group 'irony-cdb) + + +;; +;; Internal variables +;; + +(defvar-local irony-cdb--compilation-database nil) + + +;; +;; Irony Compilation Database Interface +;; + +;;;###autoload +(defun irony-cdb-autosetup-compile-options () + (interactive) + (irony--awhen (irony-cdb--autodetect-compile-options) + (setq irony-cdb--compilation-database (nth 0 it)) + (irony-cdb--update-compile-options (nth 1 it) (nth 2 it)))) + +;;;###autoload +(defun irony-cdb-menu () + (interactive) + (let ((compilation-database irony-cdb--compilation-database) + (working-directory irony--working-directory) + (compile-options irony--compile-options)) + (save-excursion + (save-window-excursion + (delete-other-windows) + (let ((buffer (get-buffer-create "*Irony/Compilation DB Menu*"))) + (with-current-buffer buffer + (erase-buffer) + (if (null compilation-database) + (insert "No compilation database in use.\n") + (insert (format "Compilation Database: %s\n\n" + (symbol-name compilation-database))) + (insert (format " Working Directory: %s\n" working-directory)) + (insert (format " Compile Options: %s\n" + (mapconcat 'identity compile-options " ")))) + (insert "\n[q] to quit")) + (let ((pop-up-windows t)) + (display-buffer buffer t)) + (fit-window-to-buffer (get-buffer-window buffer)) + (irony--read-char-choice "Irony CDB Buffer" (list ?q))))) + ;; clear `read-char-choice' prompt + (message ""))) + + +;; +;; Functions +;; + +(defun irony-cdb--choose-closest-path (file paths) + "Find the \"best\" path in PATHS matching FILE + +If any paths in PATHS is belongs to the same directory +or a subdirectory of file, we disregard other candidates. + +For remaining candidates, \"nearest\" is measured as abs. difference +in path depth. +- We prefer deeper paths at level +N to those at level -N. +- If multiple paths are equally good, we return the last one. + +Returns nil if paths isn't a list of at least one element. +" + (when (listp paths) + (let ((paths (or + ;; if we find a cdb in cwd or below, don't consider other candidates + (cl-remove-if-not (lambda (x) (string-prefix-p (file-name-directory file) x)) paths) + paths))) + (cl-loop for path in paths + with best-depth-delta = 999999 ; start at +inf + with best-path = nil ; we keep the best so far here + ;; all candidates have their depth compared to that of target file + with file-depth = (length (split-string (expand-file-name file) "/")) ; + for candidate-depth = (length (split-string (expand-file-name path) "/")) + ;; Our metric. We use signum as a tie-breaker to choose deeper candidates + for depth-delta = (+ (abs (- file-depth candidate-depth)) + (* 0.1 (- file-depth candidate-depth))) + do (when (< depth-delta best-depth-delta) + (progn + (setq best-depth-delta depth-delta) + (setq best-path path))) + finally return best-path)))) + +(defun irony-cdb--locate-dominating-file-with-dirs (file + name + subdirectories) + "Convenience wrapper around `locate-dominating-file' + +Looks up the directory hierarchy from FILE for to locate any directory +in `subdirectories` which contains NAME. If multiple files are found, +chooses the one located at the nearest directory level. if multiple +files are found at the same level, picks the first one encountered. +returns the full path to file if found, or nil otherwise." + (let ((candidates + (cl-loop for subdir in subdirectories + for relpath = (concat (file-name-as-directory subdir) name) + for match-maybe = (locate-dominating-file file relpath) + when match-maybe collect (expand-file-name (concat match-maybe relpath))))) + (irony-cdb--choose-closest-path file candidates))) + + +(defun irony-cdb--update-compile-options (compile-options + &optional working-directory) + (setq irony--compile-options compile-options + irony--working-directory working-directory)) + +(defun irony-cdb--autodetect-compile-options () + (catch 'found + (dolist (compilation-database irony-cdb-compilation-databases) + (with-demoted-errors "Irony CDB: error in compilation database: %S" + (irony--awhen (funcall compilation-database 'get-compile-options) + (throw 'found (list compilation-database + (caar it) + (cdar it)))))))) + +(defun irony-cdb--string-suffix-p (suffix string &optional ignore-case) + "Return non-nil if SUFFIX is a suffix of STRING." + (let ((start-pos (- (length string) (length suffix)))) + (and (>= start-pos 0) + (eq t (compare-strings suffix nil nil + string start-pos nil ignore-case))))) + +(defun irony-cdb--remove-compiler-from-flags (flags) + "Remove the compiler from FLAGS read from a compilation database. + +When using ccache, the compiler might be present in FLAGS since +the compiler is `ccache compiler'." + (let* ((first (car flags)) + (flags (cdr flags))) + (if (irony-cdb--string-suffix-p "ccache" first) (cdr flags) flags))) + +(provide 'irony-cdb) + +;; Local Variables: +;; byte-compile-warnings: (not cl-functions) +;; End: + +;;; irony-cdb.el ends here diff --git a/elpa/irony-20220110.849/irony-cdb.elc b/elpa/irony-20220110.849/irony-cdb.elc new file mode 100644 index 0000000..dca07a8 Binary files /dev/null and b/elpa/irony-20220110.849/irony-cdb.elc differ diff --git a/elpa/irony-20220110.849/irony-completion.el b/elpa/irony-20220110.849/irony-completion.el new file mode 100644 index 0000000..a994c32 --- /dev/null +++ b/elpa/irony-20220110.849/irony-completion.el @@ -0,0 +1,426 @@ +;;; irony-completion.el --- irony-mode completion interface -*- lexical-binding: t -*- + +;; Copyright (C) 2012-2014 Guillaume Papin + +;; Author: Guillaume Papin +;; Keywords: c, convenience, tools + +;; 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: + +;; Handle the search of completion points, the triggering of the +;; completion when needed and the "parsing" of completion results. + +;;; Code: + +(require 'irony) +(require 'irony-snippet) + +(require 'cl-lib) + + +;; +;; Customizable variables +;; + +(defgroup irony-completion nil + "Irony's completion interface." + :group 'irony) + +(defcustom irony-completion-trigger-commands '(self-insert-command + newline-and-indent + c-context-line-break + c-scope-operator + ;; electric commands + c-electric-backspace + c-electric-brace + c-electric-colon + c-electric-lt-gt + c-electric-paren + c-electric-pound + c-electric-semi&comma + c-electric-slash + c-electric-star) + "List of commands to watch for asynchronous completion triggering." + :type '(repeat function) + :group 'irony-completion) + +(defcustom irony-duplicate-candidates-filter nil + "Remove duplicate candidates. + +If non-nil, the completion candidate list will not contain +duplicate entries. As an example, duplicate candidates are +displayed when a derived class overrides virtual methods." + :type 'boolean + :group 'irony-completion) + + +;; +;; Utility functions +;; + +(defun irony-completion-symbol-bounds () + (let ((pt (point)) + (syntax (syntax-ppss))) + ;; no prefix for strings or comments + ;; TODO: Use fontlock faces instead? at least + ;; #warning In the middle of a warning| + ;; will be handled properly but things like links when + ;; `goto-address-prog-mode' is enabled will mess up things: + ;; #error see bug report XY: http://example.com/XY + (unless (or (nth 3 syntax) ;skip strings + (nth 4 syntax)) ;skip comments + (save-excursion + (skip-chars-backward "_a-zA-Z0-9") + (let ((ch (char-after))) + (unless (and ch (>= ch ?0) (<= ch ?9)) ;skip numbers + (when (eq (char-before) ?~) + (backward-char)) + (setq pt (point)) + (skip-chars-forward "_a-zA-Z0-9~") + (cons pt (point)))))))) + +(defun irony-completion-beginning-of-symbol () + (car (irony-completion-symbol-bounds))) + +(defun irony-completion-end-of-symbol () + (cdr (irony-completion-symbol-bounds))) + +(defsubst irony-completion--skip-whitespaces-backward () + ;;(skip-syntax-backward "-") doesn't seem to care about newlines + (skip-chars-backward " \t\n\r")) + +(defun irony-completion--parse-context-position (&optional pos) + (save-excursion + (when pos + (goto-char pos)) + (irony-completion--skip-whitespaces-backward) + (point))) + +(defun irony--completion-line-column (&optional pos) + (save-excursion + (when pos + (goto-char pos)) + ;; `position-bytes' to handle multibytes and 'multicolumns' (i.e + ;; tabulations) characters properly + (irony--without-narrowing + (cons + (line-number-at-pos) + (1+ (- (position-bytes (point)) + (position-bytes (point-at-bol)))))))) + + +;; +;; Functions +;; + +(defun irony-completion--enter () + (add-hook 'completion-at-point-functions 'irony-completion-at-point nil t)) + +(defun irony-completion--exit () + (remove-hook 'completion-at-point-functions 'irony-completion-at-point t)) + +(defun irony-completion--post-complete-yas-snippet (str placeholders) + (let ((ph-count 0) + (from 0) + to snippet) + (while + (setq to (car placeholders) + snippet (concat + snippet + (substring str from to) + (format "${%d:%s}" + (cl-incf ph-count) + (substring str + (car placeholders) + (cadr placeholders)))) + from (cadr placeholders) + placeholders (cddr placeholders))) + ;; handle the remaining non-snippet string, if any. + (concat snippet (substring str from) "$0"))) + + +;; +;; Interface with irony-server +;; + +(irony-iotask-define-task irony--t-complete + "`complete' server command." + :start (lambda (file line column compile-options) + (apply #'irony--server-send-command "complete" file line column "--" + compile-options)) + :update irony--server-command-update) + +(defun irony--complete-task-1 (&optional buffer pos) + (with-current-buffer (or buffer (current-buffer)) + (let ((line-column (irony--completion-line-column pos))) + (irony-iotask-package-task irony--t-complete + (irony--get-buffer-path-for-server) + (car line-column) + (cdr line-column) + (irony--adjust-compile-options))))) + +(defun irony--complete-task (&optional buffer pos) + (let ((unsaved-tasks (irony--unsaved-buffers-tasks)) + (complete-task (irony--complete-task-1 buffer pos))) + (if unsaved-tasks + (irony-iotask-chain unsaved-tasks complete-task) + complete-task))) + +(irony-iotask-define-task irony--t-candidates + "`candidates' server command." + :start (lambda (prefix style) + (irony--server-send-command + "candidates" prefix + (cl-case style + (case-insensitive "case-insensitive") + (smart-case "smart-case") + (t "exact")))) + :update irony--server-query-update) + +(defun irony--candidates-task (&optional buffer pos prefix style) + (irony-iotask-chain + (irony--complete-task buffer pos) + (irony-iotask-package-task irony--t-candidates prefix style))) + + +;; +;; Irony Completion Interface +;; + +(defun irony-completion-typed-text (candidate) + (nth 0 candidate)) + +(defun irony-completion-priority (candidate) + (nth 1 candidate)) + +(defun irony-completion-type (candidate) + (nth 2 candidate)) + +(defun irony-completion-brief (candidate) + (nth 3 candidate)) + +(defun irony-completion-annotation (candidate) + (substring (nth 4 candidate) (nth 5 candidate))) + +(defun irony-completion-post-comp-str (candidate) + (car (nth 6 candidate))) + +(defun irony-completion-post-comp-placeholders (candidate) + (cdr (nth 6 candidate))) + +(defun irony-completion--filter-candidates (candidates) + "Filter candidates by removing duplicates if +`irony-duplicate-candidates-filter' is non nil; Duplicate +candidates are those that have the same +`irony-completion-typed-text', `irony-completion-annotation' and +`irony-completion-type'. An example of when this is useful is +when there are many derived classes that override a virtual +method resulting in redundant duplicate entries being displayed +in the list of completions." + (let (unique-candidates) + (cl-remove-if-not + (lambda (candidate) + (or (not irony-duplicate-candidates-filter) + (let ((unique-key (list (irony-completion-typed-text candidate) + (irony-completion-annotation candidate) + (irony-completion-type candidate)))) + (and (not (member unique-key unique-candidates)) + (push unique-key unique-candidates))))) + candidates))) + +(defun irony-completion-candidates (&optional prefix style) + "Return the list of candidates at point. + +A candidate is composed of the following elements: + 0. The typed text. Multiple candidates can share the same string + because of overloaded functions, default arguments, etc. + 1. The priority. + 2. The [result-]type of the candidate, if any. + 3. If non-nil, contains the Doxygen brief documentation of the + candidate. + 4. The signature of the candidate excluding the result-type + which is available separately. + Example: \"foo(int a, int b) const\" + 5. The annotation start, a 0-based index in the prototype string. + 6. Post-completion data. The text to insert followed by 0 or + more indices. These indices work by pairs and describe ranges + of placeholder text. + Example: (\"(int a, int b)\" 1 6 8 13)" + (irony--awhen (irony-completion-symbol-bounds) + (irony-completion--filter-candidates + (irony--run-task + (irony--candidates-task nil (car it) prefix style))))) + +(defun irony-completion-candidates-async (callback &optional prefix style) + (irony--aif (irony-completion-symbol-bounds) + (irony--run-task-asynchronously + (irony--candidates-task nil (car it) prefix style) + (lambda (candidates-result) + (funcall callback (irony-completion--filter-candidates + (irony-iotask-result-get candidates-result))))) + (funcall callback nil))) + +(defun irony-completion-post-complete (candidate) + (let ((str (irony-completion-post-comp-str candidate)) + (placeholders (irony-completion-post-comp-placeholders candidate))) + (if (and placeholders (irony-snippet-available-p)) + (irony-snippet-expand + (irony-completion--post-complete-yas-snippet str placeholders)) + (insert (substring str 0 (car placeholders)))))) + +(defun irony-completion-at-trigger-point-p () + (when (eq (point) (irony-completion-beginning-of-symbol)) + (save-excursion + (cond + ;; use `re-search-backward' so that the cursor is moved just before the + ;; member access, if any + ((re-search-backward + (format "%s\\=" (regexp-opt '("." ;object member access + "->" ;pointer member access + "::"))) ;scope operator + (point-at-bol) t) + (unless + ;; ignore most common uses of '.' where it's not a member access + (and (eq (char-after) ?.) + (or + ;; include statements: #include + (looking-back "^#\\s-*include\\s-+[<\"][^>\"]*" + (point-at-bol)) + ;; floating point numbers (not thorough, see: + ;; http://en.cppreference.com/w/cpp/language/floating_literal) + (looking-back "[^_a-zA-Z0-9][[:digit:]]+" (point-at-bol)))) + ;; except the above exceptions we use a "whitelist" for the places + ;; where it looks like a member access + (irony-completion--skip-whitespaces-backward) + (or + ;; after brackets consider it's a member access so things like + ;; 'getFoo().|' match + (memq (char-before) (list ?\) ?\] ?} ?>)) + ;; identifiers but ignoring some keywords + ;; + ;; handle only a subset of template parameter packs, where the + ;; ellipsis is preceded by a keyword, in situation like: + ;; template class X {...}; + ;; template +;; Keywords: c, convenience, tools + +;; 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: + +;; Interface libclang's "Diagnostic Reporting", see +;; http://clang.llvm.org/doxygen/group__CINDEX__DIAG.html + +;;; Code: + +(require 'irony) +(require 'irony-iotask) + +(eval-when-compile + (require 'cl)) ;for lexical-let macro + + +;; +;; Irony Diagnostics Interface +;; + +(defun irony-diagnostics-file (diagnostic) + (nth 0 diagnostic)) + +(defun irony-diagnostics-line (diagnostic) + (nth 1 diagnostic)) + +(defun irony-diagnostics-column (diagnostic) + (nth 2 diagnostic)) + +(defun irony-diagnostics-severity (diagnostic) + (nth 4 diagnostic)) + +(defun irony-diagnostics-message (diagnostic) + (nth 5 diagnostic)) + +(defun irony-diagnostics-async (callback) + "Perform an asynchronous diagnostic request for the current +buffer. + +CALLBACK is called with at least one argument, a symbol +representing the status of the request. Depending on the status +more argument are provided. Possible values are explained below: + +- success + + When quering the diagnostics work, the additional argument is a + list of diagnostic object, diagnostics fields can be queried + with the functions `irony-diagnostics-'. + +- error + + Retrieving the diagnostics wasn't possible. A string explaining + the reason is passed as a second argument. + +- cancelled + + Retrieving the diagnostics was cancelled, e.g: because the + buffer has changed since the beginning of the request, and as + such the diagnostics are considered no longer relevant. A + reason string is passed as a second argument." + (lexical-let ((cb callback)) + (irony--run-task-asynchronously + (irony--diagnostics-task) + (lambda (diagnostics-result) + (condition-case err + (funcall cb 'success (irony-iotask-result-get diagnostics-result)) + (error + (funcall cb 'error (error-message-string err)))))))) + +(provide 'irony-diagnostics) + +;;; irony-diagnostics.el ends here diff --git a/elpa/irony-20220110.849/irony-diagnostics.elc b/elpa/irony-20220110.849/irony-diagnostics.elc new file mode 100644 index 0000000..dfcae2a Binary files /dev/null and b/elpa/irony-20220110.849/irony-diagnostics.elc differ diff --git a/elpa/irony-20220110.849/irony-iotask.el b/elpa/irony-20220110.849/irony-iotask.el new file mode 100644 index 0000000..2226a69 --- /dev/null +++ b/elpa/irony-20220110.849/irony-iotask.el @@ -0,0 +1,441 @@ +;;; irony-iotask.el --- Abstraction for I/O-based tasks + +;; Copyright (C) 2015 Guillaume Papin + +;; Author: Guillaume Papin +;; Keywords: processes, convenience + +;; 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: + +;; Purpose is to work with both processes and network streams. +;; +;; Unlike tq.el we want tasks to be composed of 0, 1-1, or n-n communications. +;; tq.el only supports 1-1. +;; +;; 0 is useful if a server command has been cached, it means there is no need +;; for communication. We may only know if a task is still cached when the +;; callback is called (maybe after some other asynchronous tasks, so the context +;; may have changed since the task was initially posted) +;; +;; n-n is useful if the number of request depends on the answer to a previous +;; request but we still want to be able to push new tasks. + +;;; Code: + +(eval-when-compile + (require 'cl)) ;for lexical-let macro + +(require 'cl-lib) + + +;; +;; Error conditions +;; + +;; `define-error' breaks backward compatibility with Emacs < 24.4 +(defun irony-iotask--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-iotask--define-error 'irony-iotask-error "I/O task error") +(irony-iotask--define-error 'irony-iotask-filter-error "I/O task filter error") +(irony-iotask--define-error 'irony-iotask-bad-task "Bad I/O task") +(irony-iotask--define-error 'irony-iotask-bad-data "Bad I/O task data") + + +;; +;; Structures +;; + +(cl-defstruct (irony-iotask-result (:constructor irony-iotask-result-create)) + -tag ;; 'value or 'error, or nil when unset + -value + -error -error-data + ) + +(defun irony-iotask-result-valid-p (result) + (and (irony-iotask-result--tag result) t)) + +(defun irony-iotask-result-value-p (result) + (eq (irony-iotask-result--tag result) 'value)) + +(defun irony-iotask-result-error-p (result) + (eq (irony-iotask-result--tag result) 'error)) + +(defun irony-iotask-result-set-value (result value) + (setf (irony-iotask-result--tag result) 'value) + (setf (irony-iotask-result--value result) value)) + +(defun irony-iotask-result-set-error (result error &rest error-data) + (setf (irony-iotask-result--tag result) 'error) + (setf (irony-iotask-result--error result) error) + (setf (irony-iotask-result--error-data result) error-data)) + +(irony-iotask--define-error 'irony-iotask-result-get-error + "Result not set before call to get") + +(defun irony-iotask-result-get (result) + (cl-case (irony-iotask-result--tag result) + ('value (irony-iotask-result--value result)) + ('error (signal (irony-iotask-result--error result) + (irony-iotask-result--error-data result))) + (t + (signal 'irony-iotask-result-get-error (list result))))) + +;; FIXME: quoting issues? I cannot write "#'(lambda ()...)" as property value +(defmacro irony-iotask-define-task (var docstring &rest properties) + "A task is simply a property list. + +Each of these function are called in the buffer they were +originally created (at schedule time). + +The functions `irony-iotask-put', `irony-iotask-get', +`irony-iotask-set-result' and `irony-iotask-set-error' are +available to the task's functions to set the task's result. + +Properties: + +`:start' (mandatory) + Function to call to launch the task. + + Usually the function sends a string/command/message to the + execution context. If the task do some caching it's possible + that nothing is send, instead the execution context result + should be set to indicate that the task is ready. + + The following additional functions are available to call + inside the `:start' function to communicate with the + underlying process: + + - `irony-iotask-send-string' + - `irony-iotask-send-region' + - `irony-iotask-send-eof' + +`:update' (mandatory) + Function to call when some process output is available. + + The function should determine whether a message is complete + and set the result when it is. It should also detect if the + message is invalid and throw the 'invalid-msg tag with a + value of t in this case. If the message is incomplete, the + function should do nothing. + + The process output is the current buffer. + +`:finish' (optional) + + Function to call after the result has been set but before + the callback is called. + + Usually performs some kind of cleanup operation. + + Note: it makes no sense to set a result or error in this + function as it is necessarily been set beforehand. + +`:on-success' (optional) + + Same as `:finish' but called only if the result IS NOT an error. + +`:on-error' (optional) + + Same as `:finish' but called only if the result IS an error." + (declare (indent 1) + (doc-string 2)) + `(progn + (defvar ,var nil ,docstring) + ;; Use `setq' to reset the var every time the macro is called. + ;; This is useful, for example when evaluating using C-M-x (`eval-defun'). + ;; Trick stolen from auto-complete's `ac-define-source' + (setq ,var '(,@properties)))) + +(cl-defstruct (irony-iotask-packaged-task + (:constructor irony-iotask-packaged-task--create)) + task + args + result + plist + continuation) + +(defun irony-iotask-package-task (task &rest args) + (irony-iotask-packaged-task--create :task task + :result (irony-iotask-result-create) + :args args)) + +(defvar irony-iotask--current-packaged-task) ;dynamically bound +(defun irony-iotask-package-task-invoke (packaged-task prop-fn + &optional ignore-missing + &rest leading-args) + (let* ((task (irony-iotask-packaged-task-task packaged-task)) + (args (irony-iotask-packaged-task-args packaged-task)) + (fn (plist-get task prop-fn))) + (condition-case err + (if fn + ;; let binding for irony-iotask-{get,put} + ;; and irony-iotask-set-{result,error} + (let ((irony-iotask--current-packaged-task packaged-task)) + (apply fn (append leading-args args))) + (unless ignore-missing + (signal 'irony-iotask-bad-task + (list task (format "no %s function" prop-fn))))) + (error + (apply #'irony-iotask-result-set-error + (irony-iotask-packaged-task-result packaged-task) + (car err) (cdr err)))))) + +(defun irony-iotask--chain-1 (packaged-task-1 packaged-task-2) + (while (irony-iotask-packaged-task-continuation packaged-task-1) + (setq packaged-task-1 + (irony-iotask-packaged-task-continuation packaged-task-1))) + (setf (irony-iotask-packaged-task-continuation packaged-task-1) + packaged-task-2)) + +(defun irony-iotask-chain (packaged-task-1 packaged-task-2 &rest others) + (setq others (cons packaged-task-2 others)) + (while others + (irony-iotask--chain-1 packaged-task-1 (car others)) + (setq others (cdr others))) + packaged-task-1) + +(cl-defstruct (irony-iotask-ectx + (:constructor irony-iotask-ectx--create)) + started + packaged-task + callback + schedule-buffer) + +(defun irony-iotask-ectx-call-callback (ectx result) + (let ((cb-buffer (irony-iotask-ectx-schedule-buffer ectx))) + (when (buffer-live-p cb-buffer) ;[GH-427] + (with-demoted-errors "Irony I/O task: error in callback: %S" + (with-current-buffer cb-buffer + (funcall (irony-iotask-ectx-callback ectx) result)))))) + +(defvar irony-iotask--process) + +(defun irony-iotask--start-next (process) + (let* ((ectx (car (process-get process :ectx-q))) + (packaged-task (irony-iotask-ectx-packaged-task ectx))) + (setf (irony-iotask-ectx-started ectx) t) + ;; erase the buffer before we call :start so the next :update starts anew + (erase-buffer) + ;; for `irony-iotask-send-string', `irony-iotask-send-region' and + ;; `irony-iotask-send-eof' + (let ((irony-iotask--process process)) + (save-current-buffer + (irony-iotask-package-task-invoke packaged-task :start))) + (irony-iotask--check-result process))) + +(defun irony-iotask--start-next-safe (process) + "Run the next task, if any." + (let ((ectx-q (process-get process :ectx-q))) + (when (and ectx-q (not (irony-iotask-ectx-started (car ectx-q)))) + (irony-iotask--start-next process)))) + +(defun irony-iotask--check-result (process) + (let* ((ectx (car (process-get process :ectx-q))) + (packaged-task (irony-iotask-ectx-packaged-task ectx)) + (result (irony-iotask-packaged-task-result packaged-task))) + (when (irony-iotask-result-valid-p result) + (save-current-buffer + (if (irony-iotask-result-value-p result) + (irony-iotask-package-task-invoke packaged-task :on-success t) + (irony-iotask-package-task-invoke packaged-task :on-error t))) + (save-current-buffer + (irony-iotask-package-task-invoke packaged-task :finish t)) + (if (and (irony-iotask-packaged-task-continuation packaged-task) + (irony-iotask-result-value-p result)) + ;; we got a non-error, we can chain to the next continuation + (progn + (setf (irony-iotask-ectx-packaged-task ectx) + (irony-iotask-packaged-task-continuation packaged-task)) + (irony-iotask--start-next process)) + ;; no continuation or an error, call the callback + ;; and skip any potential continuation + (setq ectx (pop (process-get process :ectx-q))) + (irony-iotask-ectx-call-callback ectx result) + (irony-iotask--start-next-safe process))))) + +(irony-iotask--define-error 'irony-iotask-aborted "I/O task aborted") + +(defun irony-iotask--abort-all (process &rest reasons) + (let ((result (irony-iotask-result-create)) + ectx) + (apply #'irony-iotask-result-set-error result 'irony-iotask-aborted reasons) + (while (setq ectx (pop (process-get process :ectx-q))) + (irony-iotask-ectx-call-callback ectx result)))) + + +;; +;; Implementation details, internal mechanic +;; + +(defun irony-iotask-process-filter (process output) + (when (buffer-live-p (process-buffer process)) + (with-current-buffer (process-buffer process) + (let ((ectx (car (process-get process :ectx-q))) + packaged-task + result) + ;; if no task this is an error, a spurious message is an error + (unless ectx + (signal 'irony-iotask-filter-error (list "spurious output" output))) + (setq packaged-task (irony-iotask-ectx-packaged-task ectx)) + (setq result (irony-iotask-packaged-task-result packaged-task)) + (goto-char (process-mark process)) + (insert output) + (set-marker (process-mark process) (point)) + ;; go to the first char, so the `:update' function is called at the + ;; beginning of the buffer, it is more convenient + (goto-char (point-min)) + (when (catch 'invalid-msg + (save-current-buffer + (irony-iotask-package-task-invoke packaged-task :update)) + nil) + (irony-iotask-result-set-error result + 'irony-iotask-bad-data + packaged-task + (buffer-string))) + (irony-iotask--check-result process))))) + +(defun irony-iotask-process-sentinel (process event) + ;; events usually ends with a newline, we don't want it + (setq event (replace-regexp-in-string "\n\\'" "" event)) + (unless (process-live-p process) + (irony-iotask--abort-all process "process stopped running" event) + (message "%s process stopped!" (process-name process)))) + +(defun irony-iotask-check-process (process) + (let ((pfilter (process-filter process)) + (psentinel (process-sentinel process))) + (unless (eq pfilter 'irony-iotask-process-filter) + (signal 'irony-iotask-error + (list "invalid process filter" pfilter + "did you call `irony-iotask-setup-process'?"))) + (unless (eq psentinel 'irony-iotask-process-sentinel) + (signal 'irony-iotask-error + (list "invalid process sentinel" psentinel + "did you call `irony-iotask-setup-process'?")))) + (unless (process-live-p process) + (signal 'irony-iotask-error (list "Process ain't running!"))) + (unless (buffer-live-p (process-buffer process)) + (signal 'irony-iotask-error (list "Process' buffer dead!")))) + + +;; +;; Public API +;; + +(defun irony-iotask-setup-process (process) + "Call after creating the asynchronous process to let +irony-iotask setup the PROCESS filter and anything else that may +be needed." + (with-current-buffer (process-buffer process) + (set-process-filter process #'irony-iotask-process-filter) + (set-process-sentinel process #'irony-iotask-process-sentinel) + (buffer-disable-undo))) + +(defun irony-iotask-schedule (process packaged-task callback) + (let ((ectx (irony-iotask-ectx--create :packaged-task packaged-task + :callback callback + :schedule-buffer (current-buffer)))) + (irony-iotask-check-process process) + (with-current-buffer (process-buffer process) + ;; append the new task to the queue + (process-put process :ectx-q (append (process-get process :ectx-q) + (list ectx))) + ;; run task if none were running + (unless (cdr (process-get process :ectx-q)) + (irony-iotask--start-next process))))) + +(defun irony-iotask-run (process packaged-task) + "Blocking/waiting counterpart of `irony-iotask-schedule'. + +Return the result (or signal the stored error) instead of passing +it to a callback. + +Returns nil when quitting. + +This function isn't reentrant, do not call it from another task." + (lexical-let (run-result) + ;; schedule an asynchronous task that set result when done + (irony-iotask-schedule process + packaged-task + (lambda (result) + (setq run-result result))) + + ;; wait for the task to complete + ;; quitting is allowed, in this case the task will still run but + ;; asynchronously, it won't block the user interface but the result will be + ;; lost + (if (with-local-quit + (while (not run-result) + (accept-process-output process 0.05)) + t) + ;; didn't quit, task was completed + (irony-iotask-result-get run-result) + ;; C-g was used + ;; TODO: We cannot abort the task because that may break the other tasks, + ;; the process will be in an unpredictable state. Howewer we can cancel + ;; the continuations. + ))) + +(defun irony-iotask-get (propname) + (plist-get (irony-iotask-packaged-task-plist + irony-iotask--current-packaged-task) + propname)) + +(defun irony-iotask-put (propname value) + (setf (irony-iotask-packaged-task-plist irony-iotask--current-packaged-task) + (plist-put (irony-iotask-packaged-task-plist + irony-iotask--current-packaged-task) + propname + value))) + +(defun irony-iotask--result () + (irony-iotask-packaged-task-result irony-iotask--current-packaged-task)) + +(defun irony-iotask-set-result (value) + (irony-iotask-result-set-value (irony-iotask--result) value)) + +(defun irony-iotask-set-error (err &rest error-data) + (apply #'irony-iotask-result-set-error (irony-iotask--result) err error-data)) + +(defun irony-iotask-send-string (string) + (process-send-string irony-iotask--process string)) + +(defun irony-iotask-send-region (start end) + (process-send-region irony-iotask--process start end)) + +(defun irony-iotask-send-eof (string) + (process-send-eof irony-iotask--process)) + +(provide 'irony-iotask) +;;; irony-iotask.el ends here diff --git a/elpa/irony-20220110.849/irony-iotask.elc b/elpa/irony-20220110.849/irony-iotask.elc new file mode 100644 index 0000000..b575c2d Binary files /dev/null and b/elpa/irony-20220110.849/irony-iotask.elc differ diff --git a/elpa/irony-20220110.849/irony-pkg.el b/elpa/irony-20220110.849/irony-pkg.el new file mode 100644 index 0000000..d685cdf --- /dev/null +++ b/elpa/irony-20220110.849/irony-pkg.el @@ -0,0 +1,13 @@ +(define-package "irony" "20220110.849" "C/C++ minor mode powered by libclang" + '((cl-lib "0.5") + (json "1.2")) + :commit "870d1576fb279bb93f776a71e65f45283c423a9e" :authors + '(("Guillaume Papin" . "guillaume.papin@epitech.eu")) + :maintainer + '("Guillaume Papin" . "guillaume.papin@epitech.eu") + :keywords + '("c" "convenience" "tools") + :url "https://github.com/Sarcasm/irony-mode") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/irony-20220110.849/irony-snippet.el b/elpa/irony-20220110.849/irony-snippet.el new file mode 100644 index 0000000..b12b587 --- /dev/null +++ b/elpa/irony-20220110.849/irony-snippet.el @@ -0,0 +1,135 @@ +;;; irony-snippet.el --- Snippet support for Irony-Mode + +;; Copyright (C) 2013-2014 Guillaume Papin + +;; Author: Guillaume Papin +;; Keywords: c, convenience + +;; 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: + +;; Snippet support functions, support all version of YASnippet and +;; maybe further frameworks later. +;; +;; It is possible to check snippet support by calling +;; `irony-snippet-available-p'. +;; +;; Note: Some YASnippet versions require to have +;; `yas/minor-mode' or `yas-minor-mode' enabled to work. + +;;; Code: + + +;; Private variables +;; +(defvar irony-snippet--expand-function nil + "Function to expand a snippet at a given point (by default to +the current position). + +e.g: (defun my-expand-snippet (snippet-str &optional pos) + (do-stuff (or pos (point))) + +Will be set to nil if no snippet expansion function is found.") + + +;; +;; "Public" functions +;; +(defun irony-snippet-available-p () + "Return t if snippets are supported." + (and (irony-snippet--get-expand-function) + (not (irony-snippet--yas-disabled-p)))) + +(defun irony-snippet-expand (snippet-str &optional pos) + "Expand SNIPPET-STR starting at POS. + +If `irony-snippet-available-p' return t then" + (let ((expand-func (irony-snippet--get-expand-function))) + (funcall expand-func snippet-str pos))) + + +;; Private functions +;; +(defun irony-snippet--get-expand-function () + (unless irony-snippet--expand-function + (irony-snippet--init-yas)) + irony-snippet--expand-function) + +(defun irony-snippet--init-yas () + ;; find the snippet expand function + (when (require 'yasnippet nil t) + (let ((yas-version (or (and (boundp 'yas--version) yas--version) + (and (boundp 'yas/version) yas/version)))) ;for old versions + (when (stringp yas-version) + (setq yas-version (replace-regexp-in-string "(\\|)" "" yas-version)) + (setq irony-snippet--expand-function + ;; (string= ... "0.6.0c"), the 'c' suffix is not supported by + ;; `version-to-list' in emacs versions < 24, treat this one + ;; specifically. + (cond + ((or (string= yas-version "0.6.0c") + (version<= yas-version "0.6.0b")) + 'irony-snippet--expand-yas-1) + ;; `version<' thinks "0.8beta" < "0.8", we want to consider + ;; anything starting with "0.8" as "0.8" and more. + ((and (version< yas-version "0.8") + (not (string-prefix-p "0.8" yas-version))) + 'irony-snippet--expand-yas-2) + (t + 'irony-snippet--expand-yas-3))))))) + +(defun irony-snippet--yas-disabled-p () + "If the current yasnippet version offers a minor-mode, check if +this mode is disable by returning t, otherwise returns nil and +it's partially safe to assume that yasnippet expansion can be +used." + ;; XXX: work only when yasnippet is enabled, otherwise some + ;; variables used for the snippet expansion are not set and it + ;; causes some errors. + (if (boundp 'yas-minor-mode) + (not yas-minor-mode) + (if (boundp 'yas/minor-mode) + (not yas/minor-mode)))) + +(defun irony-snippet--expand-yas-1 (snippet-str &optional pos) + "Expand snippets for YASnippet version <= 0.6.0c." + (declare-function yas/expand-snippet "ext:yasnippet" t nil) + (unless (irony-snippet--yas-disabled-p) + (yas/expand-snippet (or pos (point)) + (or pos (point)) + snippet-str))) + +(defun irony-snippet--expand-yas-2 (snippet-str &optional pos) + "Expand snippets for YASnippet version < 0.8. + +See also `irony-snippet--expand-yas-1'." + (declare-function yas/expand-snippet "ext:yasnippet" t nil) + (unless (irony-snippet--yas-disabled-p) + (when pos + (goto-char pos)) + (yas/expand-snippet snippet-str))) + +(defun irony-snippet--expand-yas-3 (snippet-str &optional pos) + "Expand snippets for YASnippet version >= 0.8. + +See also `irony-snippet--expand-yas-2'." + (declare-function yas-expand-snippet "ext:yasnippet" t nil) + (unless (irony-snippet--yas-disabled-p) + (when pos + (goto-char pos)) + (yas-expand-snippet snippet-str))) + +(provide 'irony-snippet) +;;; irony-snippet.el ends here diff --git a/elpa/irony-20220110.849/irony-snippet.elc b/elpa/irony-20220110.849/irony-snippet.elc new file mode 100644 index 0000000..a5762f6 Binary files /dev/null and b/elpa/irony-20220110.849/irony-snippet.elc differ 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 diff --git a/elpa/irony-20220110.849/irony.elc b/elpa/irony-20220110.849/irony.elc new file mode 100644 index 0000000..1e85f29 Binary files /dev/null and b/elpa/irony-20220110.849/irony.elc differ diff --git a/elpa/irony-20220110.849/server/.clang-format b/elpa/irony-20220110.849/server/.clang-format new file mode 100644 index 0000000..68e7062 --- /dev/null +++ b/elpa/irony-20220110.849/server/.clang-format @@ -0,0 +1,8 @@ +BasedOnStyle: LLVM +AllowShortFunctionsOnASingleLine: false +AlwaysBreakTemplateDeclarations: true +BinPackParameters: false +BreakConstructorInitializersBeforeComma: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +IndentFunctionDeclarationAfterType: false diff --git a/elpa/irony-20220110.849/server/.clang-tidy b/elpa/irony-20220110.849/server/.clang-tidy new file mode 100644 index 0000000..57e038a --- /dev/null +++ b/elpa/irony-20220110.849/server/.clang-tidy @@ -0,0 +1,24 @@ +Checks: '-*,readability-identifier-naming,misc-assert-side-effect,readability-container-size-empty' +CheckOptions: + - key: readability-identifier-naming.TypedefCase + value: CamelCase + - key: readability-identifier-naming.StructCase + value: CamelCase + - key: readability-identifier-naming.ClassCase + value: CamelCase + - key: readability-identifier-naming.VariableCase + value: camelBack + - key: readability-identifier-naming.ParameterCase + value: camelBack + - key: readability-identifier-naming.FunctionCase + value: camelBack + - key: readability-identifier-naming.NamespaceCase + value: lower_case + - key: readability-identifier-naming.GlobalConstantCase + value: UPPER_CASE + - key: readability-identifier-naming.PrivateMemberSuffix + value: _ + - key: misc-assert-side-effect.CheckFunctionCalls + value: 1 + - key: misc-assert-side-effect.AssertMacros + value: 'assert' diff --git a/elpa/irony-20220110.849/server/CMakeLists.txt b/elpa/irony-20220110.849/server/CMakeLists.txt new file mode 100644 index 0000000..21f9c9f --- /dev/null +++ b/elpa/irony-20220110.849/server/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.0.2) + +project(IronyMode) + +set(CMAKE_MODULE_PATH + ${PROJECT_SOURCE_DIR}/cmake + ${PROJECT_SOURCE_DIR}/cmake/modules + ${CMAKE_MODULE_PATH}) + +include(CTest) +include(GNUInstallDirs) + +# Starting from CMake >= 3.1, if a specific standard is required, +# it can be set from the command line with: +# cmake -DCMAKE_CXX_STANDARD=[11|14|17] +function(irony_target_set_cxx_standard target) + set(cxx_standard 11) + if (CMAKE_VERSION VERSION_LESS "3.1") + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${target} PRIVATE -std=c++${cxx_standard}) + endif() + elseif (CMAKE_VERSION VERSION_LESS "3.8") + set_property(TARGET ${target} PROPERTY CXX_STANDARD ${cxx_standard}) + else() + target_compile_features(${target} PUBLIC cxx_std_${cxx_standard}) + endif() +endfunction() + +add_subdirectory(src) + +if (BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/LICENSE.TXT b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/LICENSE.TXT new file mode 100644 index 0000000..a7f579f --- /dev/null +++ b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/LICENSE.TXT @@ -0,0 +1,43 @@ +============================================================================== +LLVM Release License +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/README b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/README new file mode 100644 index 0000000..ed43509 --- /dev/null +++ b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/README @@ -0,0 +1,12 @@ +Origin: +- http://llvm.org/svn/llvm-project/clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py?p=294607 +- https://github.com/llvm-mirror/clang-tools-extra/blob/c2e903ec98385b82e35bdb303e411854a2e8c032/clang-tidy/tool/run-clang-tidy.py + +Modifications: +- the python version has been frozen to python2, + as the script is not python3-compatible +- added -warnings-as-errors option +- the run-clang-tidy.py script has been modified + to return a sensible exit code when running on Travis CI, + i.e. it honors exit code of the underlying processes + causing build failures on pull requests diff --git a/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/run-clang-tidy.py b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/run-clang-tidy.py new file mode 100755 index 0000000..17db708 --- /dev/null +++ b/elpa/irony-20220110.849/server/build-aux/run-clang-tidy/run-clang-tidy.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python2 +# +#===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# FIXME: Integrate with clang-tidy-diff.py + +""" +Parallel clang-tidy runner +========================== + +Runs clang-tidy over all files in a compilation database. Requires clang-tidy +and clang-apply-replacements in $PATH. + +Example invocations. +- Run clang-tidy on all files in the current working directory with a default + set of checks and show warnings in the cpp files and all project headers. + run-clang-tidy.py $PWD + +- Fix all header guards. + run-clang-tidy.py -fix -checks=-*,llvm-header-guard + +- Fix all header guards included from clang-tidy and header guards + for clang-tidy headers. + run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ + -header-filter=extra/clang-tidy + +Compilation database setup: +http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +""" + +import argparse +import json +import multiprocessing +import os +import Queue +import re +import shutil +import subprocess +import sys +import tempfile +import threading + + +def find_compilation_database(path): + """Adjusts the directory until a compilation database is found.""" + result = './' + while not os.path.isfile(os.path.join(result, path)): + if os.path.realpath(result) == '/': + print 'Error: could not find compilation database.' + sys.exit(1) + result += '../' + return os.path.realpath(result) + + +def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, + header_filter, extra_arg, extra_arg_before, quiet, + warnings_as_errors): + """Gets a command line for clang-tidy.""" + start = [clang_tidy_binary] + if header_filter is not None: + start.append('-header-filter=' + header_filter) + else: + # Show warnings in all in-project headers by default. + start.append('-header-filter=^' + build_path + '/.*') + if checks: + start.append('-checks=' + checks) + if tmpdir is not None: + start.append('-export-fixes') + # Get a temporary file. We immediately close the handle so clang-tidy can + # overwrite it. + (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir) + os.close(handle) + start.append(name) + for arg in extra_arg: + start.append('-extra-arg=%s' % arg) + for arg in extra_arg_before: + start.append('-extra-arg-before=%s' % arg) + start.append('-p=' + build_path) + if quiet: + start.append('-quiet') + if warnings_as_errors is not None: + start.append('-warnings-as-errors=%s' % warnings_as_errors) + start.append(f) + return start + + +def apply_fixes(args, tmpdir): + """Calls clang-apply-fixes on a given directory. Deletes the dir when done.""" + invocation = [args.clang_apply_replacements_binary] + if args.format: + invocation.append('-format') + invocation.append(tmpdir) + ret = subprocess.call(invocation) + shutil.rmtree(tmpdir) + return ret + + +def run_tidy(args, tmpdir, build_path, queue, results, i): + """Takes filenames out of queue and runs clang-tidy on them.""" + results[i] = 0 + while True: + name = queue.get() + invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks, + tmpdir, build_path, args.header_filter, + args.extra_arg, args.extra_arg_before, + args.quiet, args.warnings_as_errors) + sys.stdout.write(' '.join(invocation) + '\n') + if subprocess.call(invocation) != 0: + results[i] = 1 + queue.task_done() + + +def main(): + parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' + 'in a compilation database. Requires ' + 'clang-tidy and clang-apply-replacements in ' + '$PATH.') + parser.add_argument('-clang-tidy-binary', metavar='PATH', + default='clang-tidy', + help='path to clang-tidy binary') + parser.add_argument('-clang-apply-replacements-binary', metavar='PATH', + default='clang-apply-replacements', + help='path to clang-apply-replacements binary') + parser.add_argument('-checks', default=None, + help='checks filter, when not specified, use clang-tidy ' + 'default') + parser.add_argument('-header-filter', default=None, + help='regular expression matching the names of the ' + 'headers to output diagnostics from. Diagnostics from ' + 'the main file of each translation unit are always ' + 'displayed.') + parser.add_argument('-warnings-as-errors', default=None, metavar='STRING', + help='Upgrades warnings to errors. ' + 'Same format as "-checks".' + "This option's value is appended to the value of " + "the 'WarningsAsErrors' option in .clang-tidy " + "file, if any.'") + parser.add_argument('-j', type=int, default=0, + help='number of tidy instances to be run in parallel.') + parser.add_argument('files', nargs='*', default=['.*'], + help='files to be processed (regex on path)') + parser.add_argument('-fix', action='store_true', help='apply fix-its') + parser.add_argument('-format', action='store_true', help='Reformat code ' + 'after applying fixes') + parser.add_argument('-p', dest='build_path', + help='Path used to read a compile command database.') + parser.add_argument('-extra-arg', dest='extra_arg', + action='append', default=[], + help='Additional argument to append to the compiler ' + 'command line.') + parser.add_argument('-extra-arg-before', dest='extra_arg_before', + action='append', default=[], + help='Additional argument to prepend to the compiler ' + 'command line.') + parser.add_argument('-quiet', action='store_true', + help='Run clang-tidy in quiet mode') + args = parser.parse_args() + + db_path = 'compile_commands.json' + + if args.build_path is not None: + build_path = args.build_path + else: + # Find our database + build_path = find_compilation_database(db_path) + + try: + invocation = [args.clang_tidy_binary, '-list-checks'] + invocation.append('-p=' + build_path) + if args.checks: + invocation.append('-checks=' + args.checks) + invocation.append('-') + print subprocess.check_output(invocation) + except: + print >>sys.stderr, "Unable to run clang-tidy." + sys.exit(1) + + # Load the database and extract all files. + database = json.load(open(os.path.join(build_path, db_path))) + files = [entry['file'] for entry in database] + + max_task = args.j + if max_task == 0: + max_task = multiprocessing.cpu_count() + + tmpdir = None + if args.fix: + tmpdir = tempfile.mkdtemp() + + # Build up a big regexy filter from all command line arguments. + file_name_re = re.compile('(' + ')|('.join(args.files) + ')') + + try: + # Spin up a bunch of tidy-launching threads. + results = [None] * max_task + queue = Queue.Queue(max_task) + for i in range(max_task): + t = threading.Thread(target=run_tidy, + args=(args, tmpdir, build_path, queue, results, i)) + t.daemon = True + t.start() + + # Fill the queue with files. + for name in files: + if file_name_re.search(name): + queue.put(name) + + # Wait for all threads to be done. + queue.join() + + except KeyboardInterrupt: + # This is a sad hack. Unfortunately subprocess goes + # bonkers with ctrl-c and we start forking merrily. + print '\nCtrl-C detected, goodbye.' + if args.fix: + shutil.rmtree(tmpdir) + os.kill(0, 9) + + for ret in results: + if ret != 0: + sys.exit(ret) + + if args.fix: + print 'Applying fixes ...' + ret = apply_fixes(args, tmpdir) + if ret != 0: + sys.exit(ret) + +if __name__ == '__main__': + main() diff --git a/elpa/irony-20220110.849/server/cmake/CheckClangResourceDir.cmake b/elpa/irony-20220110.849/server/cmake/CheckClangResourceDir.cmake new file mode 100644 index 0000000..20bcda9 --- /dev/null +++ b/elpa/irony-20220110.849/server/cmake/CheckClangResourceDir.cmake @@ -0,0 +1,90 @@ +# +# Get the Clang resource directory. +# +# If found the following variable will be set: +# - CLANG_RESOURCE_DIR +# +set(CHECK_CLANG_RESOURCE_DIR_CHECKER_CODE_IN + ${CMAKE_CURRENT_LIST_DIR}/LibClangDiagnosticsChecker.cpp) + +function(check_clang_resource_dir) + if (CLANG_RESOURCE_DIR) + return() # already in cache + endif() + + message(STATUS "Detecting Clang resource directory") + find_package (LibClang REQUIRED) + + set(checker_code + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/LibClangDiagnosticsChecker.cpp) + set(checked_file + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/check-libclang-stddef.cpp") + + configure_file(${CHECK_CLANG_RESOURCE_DIR_CHECKER_CODE_IN} + ${checker_code} COPYONLY) + file(WRITE "${checked_file}" "#include \n") + + # Paths stolen from Rip-Rip/clang_complete#getBuiltinHeaderPath() + find_path(CHECK_CLANG_RESOURCE_DIR include/stddef.h + NO_DEFAULT_PATH + # the default path, favor this one over the other, in case a specific + # libclang has been chosen. + HINTS "${LIBCLANG_LIBRARY_DIR}/../lib/clang" + # other, distribution specific, paths + PATHS + "${LIBCLANG_LIBRARY_DIR}/../clang" # Gentoo + "${LIBCLANG_LIBRARY_DIR}/clang" # openSUSE, Windows + "${LIBCLANG_LIBRARY_DIR}/" # Google + "/usr/lib64/clang" # x86_64 (openSUSE, Fedora) + "/usr/lib/clang" + PATH_SUFFIXES ${LIBCLANG_KNOWN_LLVM_VERSIONS}) + + if (CHECK_CLANG_RESOURCE_DIR) + # On Windows the paths weren't escaped correctly, similar to: + # http://public.kitware.com/pipermail/cmake/2006-February/008473.html + list(APPEND run_args -resource-dir \"${CHECK_CLANG_RESOURCE_DIR}\") + endif() + + list(APPEND run_args ${checked_file}) + + try_run( + CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS + CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT + ${CMAKE_BINARY_DIR} + ${checker_code} + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES:STRING=${LIBCLANG_INCLUDE_DIRS}" + "-DLINK_LIBRARIES:STRING=${LIBCLANG_LIBRARIES}" + COMPILE_OUTPUT_VARIABLE compile_output + RUN_OUTPUT_VARIABLE run_output + ARGS ${run_args} + ) + + if (NOT CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT) + set(CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS 1) + endif() + + if (CHECK_CLANG_RESOURCE_DIR_NUM_DIAGNOSTICS EQUAL 0) + message(STATUS "Detecting libclang builtin headers directory -- success") + if (CHECK_CLANG_RESOURCE_DIR) + set(CLANG_RESOURCE_DIR "${CHECK_CLANG_RESOURCE_DIR}" + CACHE INTERNAL "Clang resource directory.") + endif() + else() + message(STATUS "Detecting Clang resource directory -- fail") + + if (NOT CHECK_CLANG_RESOURCE_DIR_COMPILE_RESULT) + message(WARNING "CheckClangResourceDir: failed to compile checker, please report. + Compile output: + ${compile_output} +") + else() + message(WARNING "CheckClangResourceDir: unsupported configuration, please report. + + Check with args: ${run_args} + Check output: + ${run_output} +") + endif() + endif() +endfunction() diff --git a/elpa/irony-20220110.849/server/cmake/LibClangDiagnosticsChecker.cpp b/elpa/irony-20220110.849/server/cmake/LibClangDiagnosticsChecker.cpp new file mode 100644 index 0000000..64ea7aa --- /dev/null +++ b/elpa/irony-20220110.849/server/cmake/LibClangDiagnosticsChecker.cpp @@ -0,0 +1,47 @@ +/* + This program takes some forward its command line arguments to libclang and + returns the number of diagnostics that occured during the parsing. + + It is used during CMake generation to adjust the default parameters to + libclang. +*/ + +#include + +#include + +int main(int argc, const char *argv[]) { + for (int i = 1; i < argc; ++i) { + fprintf(stdout, "argv[%d]: %s\n", i, argv[i]); + } + + CXIndex Idx = clang_createIndex(0, 0); + CXTranslationUnit TU = clang_parseTranslationUnit( + Idx, NULL, &argv[1], argc - 1, 0, 0, CXTranslationUnit_None); + int NumDiagnostics; + + if (TU == NULL) { + NumDiagnostics = 1; + fprintf(stderr, "failed to create translation unit!\n"); + } else { + int i; + + NumDiagnostics = clang_getNumDiagnostics(TU); + + for (i = 0; i < NumDiagnostics; ++i) { + CXDiagnostic Diag = clang_getDiagnostic(TU, i); + CXString DiagStr = + clang_formatDiagnostic(Diag, clang_defaultDiagnosticDisplayOptions()); + + fprintf(stderr, "%s\n", clang_getCString(DiagStr)); + + clang_disposeString(DiagStr); + clang_disposeDiagnostic(Diag); + } + + clang_disposeTranslationUnit(TU); + } + + clang_disposeIndex(Idx); + return NumDiagnostics; +} diff --git a/elpa/irony-20220110.849/server/cmake/modules/FindLibClang.cmake b/elpa/irony-20220110.849/server/cmake/modules/FindLibClang.cmake new file mode 100644 index 0000000..edc786d --- /dev/null +++ b/elpa/irony-20220110.849/server/cmake/modules/FindLibClang.cmake @@ -0,0 +1,106 @@ +# Find libclang. +# +# This module defines the following variables: +# LIBCLANG_FOUND - true if libclang has been found and can be used +# LIBCLANG_KNOWN_LLVM_VERSIONS - known LLVM release numbers +# LIBCLANG_INCLUDE_DIRS - the libclang include directories +# LIBCLANG_LIBRARIES - the libraries needed to use libclang +# LIBCLANG_LIBRARY_DIR - the path to the directory containing libclang +# +# This module defines the following IMPORTED target: +# - irony_libclang + +# most recent versions come first +# http://llvm.org/apt/ +set(LIBCLANG_KNOWN_LLVM_VERSIONS 9.0.0 9.0 9 + 8.0.0 8.0 8 + 7.0.1 7.0.0 7.0 7 + 6.0.1 6.0.0 6.0 6 + 5.0.2 5.0.1 5.0.0 5.0 5 + 4.0.1 4.0.0 4.0 4 + 3.9.1 3.9.0 3.9 + 3.8.1 3.8.0 3.8 + 3.7.1 3.7.0 3.7 + 3.6.2 3.6.1 3.6.0 3.6 + 3.5.2 3.5.1 3.5.0 3.5 + 3.4.2 3.4.1 3.4 + 3.3 + 3.2 + 3.1) + +set(libclang_llvm_header_search_paths) +set(libclang_llvm_lib_search_paths + # LLVM Fedora + /usr/lib/llvm + ) + +foreach (version ${LIBCLANG_KNOWN_LLVM_VERSIONS}) + string(REPLACE "." "" undotted_version "${version}") + list(APPEND libclang_llvm_header_search_paths + # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ + "/usr/lib/llvm-${version}/include/" + # LLVM MacPorts + "/opt/local/libexec/llvm-${version}/include" + # LLVM Homebrew + "/usr/local/Cellar/llvm/${version}/include" + # LLVM Homebrew/versions + "/usr/local/lib/llvm-${version}/include" + # FreeBSD ports versions + "/usr/local/llvm${undotted_version}/include" + # Gentoo clang-4 + "/usr/lib/llvm/${version}/include" + ) + + list(APPEND libclang_llvm_lib_search_paths + # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ + "/usr/lib/llvm-${version}/lib/" + # LLVM MacPorts + "/opt/local/libexec/llvm-${version}/lib" + # LLVM Homebrew + "/usr/local/Cellar/llvm/${version}/lib" + # LLVM Homebrew/versions + "/usr/local/lib/llvm-${version}/lib" + # FreeBSD ports versions + "/usr/local/llvm${undotted_version}/lib" + # Gentoo clang-4 + "/usr/lib/llvm/${version}/lib" + ) +endforeach() + +find_path(LIBCLANG_INCLUDE_DIR clang-c/Index.h + PATHS ${libclang_llvm_header_search_paths} + PATH_SUFFIXES LLVM/include #Windows package from http://llvm.org/releases/ + DOC "The path to the directory that contains clang-c/Index.h") + +find_library(LIBCLANG_LIBRARY + NAMES + # On Windows with MSVC, the import library uses the ".imp" file extension + # instead of the comon ".lib" + libclang.imp + libclang + clang + PATHS ${libclang_llvm_lib_search_paths} + PATH_SUFFIXES LLVM/lib #Windows package from http://llvm.org/releases/ + DOC "The file that corresponds to the libclang library.") + +get_filename_component(LIBCLANG_LIBRARY_DIR ${LIBCLANG_LIBRARY} PATH) + +set(LIBCLANG_LIBRARIES ${LIBCLANG_LIBRARY}) +set(LIBCLANG_INCLUDE_DIRS ${LIBCLANG_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LIBCLANG_FOUND to TRUE if +# all listed variables are TRUE +find_package_handle_standard_args(LibClang DEFAULT_MSG + LIBCLANG_LIBRARY LIBCLANG_INCLUDE_DIR) + +mark_as_advanced(LIBCLANG_INCLUDE_DIR LIBCLANG_LIBRARY) + +if (LIBCLANG_FOUND AND NOT TARGET irony_libclang) + add_library(irony_libclang UNKNOWN IMPORTED) + set_target_properties(irony_libclang PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + IMPORTED_LOCATION "${LIBCLANG_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBCLANG_INCLUDE_DIR}" + ) +endif() diff --git a/elpa/irony-20220110.849/server/src/CMakeLists.txt b/elpa/irony-20220110.849/server/src/CMakeLists.txt new file mode 100644 index 0000000..c516e70 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/CMakeLists.txt @@ -0,0 +1,111 @@ +# XXX: if there are issues +# due to https://reviews.llvm.org/D30911 / SVN Revision 298424 +# then use the fallback when LLVM_VERSION VERSION_LESS 5.0.1 +find_package(Clang) + +if (Clang_FOUND) + # XXX: at least since Clang 8.0.0 + # it's looks like the 'libclang' IMPORTED target + # does not specify the following target property: + # INTERFACE_INCLUDE_DIRECTORIES ${CLANG_INCLUDE_DIRS} + # which is confirmed by https://github.com/Sarcasm/irony-mode/issues/530 + # so we work around this, + # but ideally Clang upstream should export fully usable IMPORTED targets + add_library(irony_libclang INTERFACE) + target_link_libraries(irony_libclang INTERFACE libclang) + target_include_directories(irony_libclang INTERFACE ${CLANG_INCLUDE_DIRS}) + + get_property(IRONY_CLANG_EXECUTABLE TARGET clang PROPERTY LOCATION) + execute_process( + COMMAND "${IRONY_CLANG_EXECUTABLE}" -print-resource-dir + OUTPUT_VARIABLE CLANG_RESOURCE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else() + # fallback to historically FindLibClang.cmake + # and -resource-dir guess mecanism + include(CheckClangResourceDir) + find_package(LibClang REQUIRED) + check_clang_resource_dir() +endif() + +# not to be taken as a module-definition file to link on Windows +set_source_files_properties(Commands.def PROPERTIES HEADER_FILE_ONLY TRUE) + +if(MSVC) + # irony-server uses some code that breaks when iterator debugging is enabled + # + # The culprit is CommandLineArgumentParser who initialize its member + # 'Position', of type 'std::string::const_iterator', to 'Input.begin() - 1'. + # With checked iterator the begin() - 1 breaks in debug build. + add_definitions(/D_ITERATOR_DEBUG_LEVEL=0) +endif() + +add_executable(irony-server + support/CommandLineParser.cpp + support/CommandLineParser.h + support/iomanip_quoted.h + support/NonCopyable.h + support/CIndex.h + support/TemporaryFile.cpp + support/TemporaryFile.h + + Command.cpp + Commands.def + Command.h + CompDBCache.h + CompDBCache.cpp + Irony.cpp + Irony.h + TUManager.cpp + TUManager.h + + main.cpp) + +irony_target_set_cxx_standard(irony-server) + +target_include_directories(irony-server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +# retrieve the package version from irony.el +function(irony_find_package_version OUTPUT_VAR) + # this is a hack that force CMake to reconfigure, it is necessary to see if + # the version in irony.el has changed, this is not possible to add the + # definitions at build time + configure_file(${PROJECT_SOURCE_DIR}/../irony.el + ${CMAKE_CURRENT_BINARY_DIR}/irony.el + COPYONLY) + + set(version_header "\;\; Version: ") + file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/irony.el version + LIMIT_COUNT 1 + REGEX "^${version_header}*") + + if (NOT version) + message (FATAL_ERROR "couldn't find irony.el's version header!") + endif() + + string(LENGTH ${version_header} version_header_length) + string(SUBSTRING ${version} ${version_header_length} -1 package_version) + set(${OUTPUT_VAR} ${package_version} PARENT_SCOPE) +endfunction() + +irony_find_package_version(IRONY_PACKAGE_VERSION) +message(STATUS "Irony package version is '${IRONY_PACKAGE_VERSION}'") + +set_source_files_properties(main.cpp + PROPERTIES + COMPILE_DEFINITIONS IRONY_PACKAGE_VERSION=\"${IRONY_PACKAGE_VERSION}\") + +if (CLANG_RESOURCE_DIR) + # look for CLANG_RESOURCE_DIR_DIR usage in the code for an explanation + set_source_files_properties(TUManager.cpp + PROPERTIES + COMPILE_DEFINITIONS CLANG_RESOURCE_DIR=\"${CLANG_RESOURCE_DIR}\") +endif() + +target_link_libraries(irony-server irony_libclang) + +set_target_properties(irony-server + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) + +install(TARGETS irony-server DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/elpa/irony-20220110.849/server/src/Command.cpp b/elpa/irony-20220110.849/server/src/Command.cpp new file mode 100644 index 0000000..363b6cb --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Command.cpp @@ -0,0 +1,278 @@ +/** + * \file + * \author Guillaume Papin + * + * \brief Command parser definitions. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "Command.h" + +#include "support/CommandLineParser.h" + +#include +#include +#include +#include +#include +#include + + +namespace { + +struct StringConverter { + StringConverter(std::string *dest) : dest_(dest) { + } + + bool operator()(const std::string &str) { + *dest_ = str; + return true; + } + +private: + std::string *dest_; +}; + +struct UnsignedIntConverter { + UnsignedIntConverter(unsigned *dest) : dest_(dest) { + } + + bool operator()(const std::string &str) { + char *end; + long num = std::strtol(str.c_str(), &end, 10); + + if (end != (str.c_str() + str.size())) + return false; + + if (errno == ERANGE) + return false; + + if (num < 0) + return false; + + unsigned long unum = static_cast(num); + if (unum > std::numeric_limits::max()) + return false; + + *dest_ = unum; + return true; + } + +private: + unsigned *dest_; +}; + +/// Convert "on" and "off" to a boolean +struct OptionConverter { + OptionConverter(bool *dest) : dest_(dest) { + } + + bool operator()(const std::string &str) { + if (str == "on") { + *dest_ = true; + } else if (str == "off") { + *dest_ = false; + } else { + return false; + } + return true; + } + +private: + bool *dest_; +}; + +const std::map PREFIX_MATCH_STYLE_MAP = { + { "exact", PrefixMatchStyle::Exact }, + { "case-insensitive", PrefixMatchStyle::CaseInsensitive }, + { "smart-case", PrefixMatchStyle::SmartCase}, +}; + +/// Convert style to a PrefixMatchStyle +struct PrefixMatchStyleConverter { + PrefixMatchStyleConverter(PrefixMatchStyle *dest) : dest_(dest) { + } + + bool operator()(const std::string &str) { + auto res = PREFIX_MATCH_STYLE_MAP.find(str); + + if (res == PREFIX_MATCH_STYLE_MAP.cend()) { + return false; + } + *dest_ = res->second; + return true; + } + +private: + PrefixMatchStyle *dest_; +}; + +std::ostream &operator<<(std::ostream &os, PrefixMatchStyle style) { + for (auto it : PREFIX_MATCH_STYLE_MAP) { + if (it.second == style) { + os << it.first; + return os; + } + } + os << "UnknownStyle"; + return os; +} + + +} // unnamed namespace + +std::ostream &operator<<(std::ostream &os, const Command::Action &action) { + os << "Command::"; + + switch (action) { +#define X(sym, str, help) \ + case Command::sym: \ + os << #sym; \ + break; +#include "Commands.def" + } + return os; +} + +std::ostream &operator<<(std::ostream &os, const Command &command) { + os << "Command{action=" << command.action << ", " + << "file='" << command.file << "', " + << "unsavedFile='" << command.unsavedFile << "', " + << "dir='" << command.dir << "', " + << "line=" << command.line << ", " + << "column=" << command.column << ", " + << "prefix='" << command.prefix << "', " + << "caseStyle='" << command.style << "', " + << "flags=["; + bool first = true; + for (const std::string &flag : command.flags) { + if (!first) + os << ", "; + os << "'" << flag << "'"; + first = false; + } + os << "], " + << "opt=" << (command.opt ? "on" : "off"); + + return os << "}"; +} + +static Command::Action actionFromString(const std::string &actionStr) { +#define X(sym, str, help) \ + if (actionStr == str) \ + return Command::sym; + +#include "Commands.def" + + return Command::Unknown; +} + +CommandParser::CommandParser() : tempFile_("irony-server") { +} + +Command *CommandParser::parse(const std::vector &argv) { + command_.clear(); + + if (argv.begin() == argv.end()) { + std::clog << "error: no command specified.\n" + "See 'irony-server help' to list available commands\n"; + return 0; + } + + const std::string &actionStr = argv[0]; + + command_.action = actionFromString(actionStr); + + bool readCompileOptions = false; + std::vector> positionalArgs; + + switch (command_.action) { + case Command::SetDebug: + positionalArgs.push_back(OptionConverter(&command_.opt)); + break; + + case Command::Parse: + positionalArgs.push_back(StringConverter(&command_.file)); + readCompileOptions = true; + break; + + case Command::Complete: + positionalArgs.push_back(StringConverter(&command_.file)); + positionalArgs.push_back(UnsignedIntConverter(&command_.line)); + positionalArgs.push_back(UnsignedIntConverter(&command_.column)); + readCompileOptions = true; + break; + + case Command::GetType: + positionalArgs.push_back(UnsignedIntConverter(&command_.line)); + positionalArgs.push_back(UnsignedIntConverter(&command_.column)); + break; + + case Command::SetUnsaved: + positionalArgs.push_back(StringConverter(&command_.file)); + positionalArgs.push_back(StringConverter(&command_.unsavedFile)); + break; + + case Command::ResetUnsaved: + positionalArgs.push_back(StringConverter(&command_.file)); + break; + + case Command::Candidates: + positionalArgs.push_back(StringConverter(&command_.prefix)); + positionalArgs.push_back(PrefixMatchStyleConverter(&command_.style)); + break; + case Command::CompletionDiagnostics: + case Command::Diagnostics: + case Command::Help: + case Command::Exit: + // no-arguments commands + break; + + case Command::GetCompileOptions: + positionalArgs.push_back(StringConverter(&command_.dir)); + positionalArgs.push_back(StringConverter(&command_.file)); + break; + + case Command::Unknown: + std::clog << "error: invalid command specified: " << actionStr << "\n"; + return 0; + } + + auto argsBegin = argv.begin() + 1; + const auto argsEnd = std::find(argsBegin, argv.end(), "--"); + const int argCount = std::distance(argsBegin, argsEnd); + + // compile options are provided after '--' + if (readCompileOptions && argsEnd != argv.end()) { + command_.flags.assign(std::next(argsEnd), argv.end()); + } + + if (argCount != static_cast(positionalArgs.size())) { + std::clog << "error: invalid number of arguments for '" << actionStr + << "' (requires " << positionalArgs.size() << " got " << argCount + << ")\n"; + return 0; + } + + for (auto fn : positionalArgs) { + if (!fn(*argsBegin)) { + std::clog << "error: parsing command '" << actionStr + << "': invalid argument '" << *argsBegin << "'\n"; + return 0; + } + ++argsBegin; + } + + // '-' is used as a special file to inform that the buffer hasn't been saved + // on disk and only the buffer content is available. libclang needs a file, so + // this is treated as a special value for irony-server to create a temporary + // file for this. note that libclang will gladly accept '-' as a filename but + // we don't want to let this happen since irony already reads stdin. + if (command_.file == "-") { + command_.file = tempFile_.getPath(); + } + + return &command_; +} diff --git a/elpa/irony-20220110.849/server/src/Command.h b/elpa/irony-20220110.849/server/src/Command.h new file mode 100644 index 0000000..9f36aa4 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Command.h @@ -0,0 +1,73 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin + * + * \brief Command parser declarations. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_COMMAND_H_ +#define IRONY_MODE_SERVER_COMMAND_H_ + +#include "support/CIndex.h" +#include "support/TemporaryFile.h" +#include "Style.h" + +#include +#include +#include + +class TemporaryFile; + +// TODO: a tagged union? +struct Command { + Command() { + clear(); + } + + void clear() { + action = Unknown; + flags.clear(); + file.clear(); + unsavedFile.clear(); + dir.clear(); + prefix.clear(); + style = PrefixMatchStyle::Exact; + line = 0; + column = 0; + opt = false; + } + +#define X(sym, str, desc) sym, + enum Action { +#include "Commands.def" + } action; + + std::vector flags; + std::string file; + std::string unsavedFile; + std::string dir; + std::string prefix; + PrefixMatchStyle style; + unsigned line; + unsigned column; + bool opt; +}; + +std::ostream &operator<<(std::ostream &os, const Command::Action &action); +std::ostream &operator<<(std::ostream &os, const Command &command); + +class CommandParser { +public: + CommandParser(); + + Command *parse(const std::vector &argv); + +private: + Command command_; + TemporaryFile tempFile_; +}; + +#endif // IRONY_MODE_SERVER_COMMAND_H_ diff --git a/elpa/irony-20220110.849/server/src/Commands.def b/elpa/irony-20220110.849/server/src/Commands.def new file mode 100644 index 0000000..f99d8af --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Commands.def @@ -0,0 +1,39 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin + * + * \brief Command list. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef X +#error Please define the 'X(id, command, description)' macro before inclusion! +#endif + +X(Candidates, "candidates", + "PREFIX STYLE - print completion candidates (require previous complete). " + "STYLE is \"exact\", \"case-insensitive\" or \"smart-case\"") +X(Complete, "complete", + "FILE LINE COL [-- [COMPILE_OPTIONS...]] - perform code completion at a given location") +X(CompletionDiagnostics, "completion-diagnostics", + "print the diagnostics generated during complete") +X(Diagnostics, "diagnostics", "print the diagnostics of the last parse") +X(Exit, "exit", "exit interactive mode, print nothing") +X(GetCompileOptions, "get-compile-options", "BUILD_DIR FILE - " + "get compile options for FILE from JSON database in BUILD_DIR") +X(GetType, "get-type", "LINE COL - get type of symbol at a given location") +X(Help, "help", "show this message") +X(Parse, "parse", "FILE [-- [COMPILE_OPTIONS...]] - parse the given file") +X(ResetUnsaved, "reset-unsaved", "FILE - reset FILE, its content is up to date") +X(SetDebug, "set-debug", "ON|OFF - enable or disable verbose logging") +X(SetUnsaved, + "set-unsaved", + "FILE UNSAVED-CONTENT-FILE - tell irony-server that " + "UNSAVED-CONTENT-FILE contains the effective content of FILE") + +// sentinel value, should be the last one +X(Unknown, "", "") + +#undef X diff --git a/elpa/irony-20220110.849/server/src/CompDBCache.cpp b/elpa/irony-20220110.849/server/src/CompDBCache.cpp new file mode 100644 index 0000000..f79ec8c --- /dev/null +++ b/elpa/irony-20220110.849/server/src/CompDBCache.cpp @@ -0,0 +1,71 @@ +#include "CompDBCache.h" + +#include + +#include + +CompDBCache::CompDBCache() + : db_(nullptr), mtime_(0) { +} + +CompDBCache::~CompDBCache() { + clear(); +} + +CXCompilationDatabase CompDBCache::fromDirectory(const std::string &buildDir, + CXCompilationDatabase_Error *error) { + assert(error != nullptr); + + const std::string jsonFilename = constructJsonDbFilename(buildDir); + const time_t mtime = modificationTime(jsonFilename); + + if (jsonFilename == filename_ && mtime != 0 && mtime == mtime_) { + // Using the cached compilation database. + // Just set the provided error code to indicate success. + *error = CXCompilationDatabase_NoError; + } else { + clear(); + + db_ = clang_CompilationDatabase_fromDirectory(buildDir.c_str(), error); + + if (mtime != 0 && *error == CXCompilationDatabase_NoError) { + // Successfully loaded a JSON compilation database. + // Cache the result. + filename_ = jsonFilename; + mtime_ = mtime; + } + } + + return db_; +} + +void CompDBCache::clear() { + if (db_) { + clang_CompilationDatabase_dispose(db_); + db_ = nullptr; + filename_.clear(); + mtime_ = 0; + } +} + +std::string CompDBCache::constructJsonDbFilename(const std::string &buildDir) const { + std::string ret = buildDir; + if (!buildDir.empty() && buildDir.back() != '/') + ret += '/'; + ret += "compile_commands.json"; + return ret; +} + +time_t CompDBCache::modificationTime(const std::string &filename) const { + time_t mtime = 0; +#ifdef _WIN32 + struct _stat st; + const int statRes = _stat(filename.c_str(), &st); +#else + struct stat st; + const int statRes = stat(filename.c_str(), &st); +#endif + if (statRes == 0) + mtime = st.st_mtime; + return mtime; +} diff --git a/elpa/irony-20220110.849/server/src/CompDBCache.h b/elpa/irony-20220110.849/server/src/CompDBCache.h new file mode 100644 index 0000000..568b790 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/CompDBCache.h @@ -0,0 +1,86 @@ +/**-*-C++-*- + * \file + * \author Idar Tollefsen + * + * \brief Creates a compilation database and caches it. + * + * Keeps a cache of the loaded compilation database, only creating a new + * one when needed. + * + * This file is distributed under the GNU General Public License. + * See COPYING for details. + */ + +#ifndef IRONY_MODE_COMPDBCACHE_H_ +#define IRONY_MODE_COMPDBCACHE_H_ + +#include "support/CIndex.h" +#include "support/NonCopyable.h" + +#include +#include + +class CompDBCache : public util::NonCopyable { +public: + CompDBCache(); + ~CompDBCache(); + + /** + * \brief Get a compilation database from a database found in a directory. + * + * This in essence a wrapper around + * \c clang_CompilationDatabase_fromDirectory() with added caching. + * It will either create a new compilation database or return the + * already loaded one (if any). + * + * This class owns the resulting compilation database. + * Therefore, unlike \c clang_CompilationDatabase_fromDirectory(), + * callers must NOT call \c clang_CompilationDatabase_dispose() on the + * returned compilation database. This class will handle that internally. + * + * \param buildDir Directory containing the database (such as + * "compile_commands.json") to create a compilation + * database from. + * \param error Error code from attempting to create a compilation + * database (\c CXCompilationDatabase_NoError on success). + * + * \return The compilation database or nullptr. + */ + CXCompilationDatabase fromDirectory(const std::string &buildDir, + CXCompilationDatabase_Error *error); + +private: + /** + * \brief Clear the cache. + * + * This will dispose the currently loaded compilation database (if any) by + * calling \c clang_CompilationDatabase_dispose() on it. And it will reset + * other internal housekeeping variables related to the caching of the + * compilation database. + */ + void clear(); + + /** + * \brief Construct JSON compilation database filename. + * + * \param buildDir Directory that might contain "compile_commands.json". + * + * \return Path to "compilation_commands.json" in \c buildDir. + */ + std::string constructJsonDbFilename(const std::string &buildDir) const; + + /** + * \brief Get modification time of a file. + * + * \param filename The file to get last modification time of. + * + * \return The modification time of \c filename or 0. + */ + time_t modificationTime(const std::string &filename) const; + + CXCompilationDatabase db_; + std::string filename_; + time_t mtime_; +}; + +#endif diff --git a/elpa/irony-20220110.849/server/src/Irony.cpp b/elpa/irony-20220110.849/server/src/Irony.cpp new file mode 100644 index 0000000..2157b32 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Irony.cpp @@ -0,0 +1,638 @@ +/** + * \file + * \author Guillaume Papin + * + * \brief irony-server "API" definitions. + * + * \sa Irony.h for more information. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "Irony.h" + +#include "support/iomanip_quoted.h" + +#include +#include +#include +#include +#include + +namespace { + +std::string cxStringToStd(CXString cxString) { + std::string stdStr; + + if (const char *cstr = clang_getCString(cxString)) { + stdStr = cstr; + } + + clang_disposeString(cxString); + return stdStr; +} + +const char *diagnosticSeverity(const CXDiagnostic &diagnostic) { + switch (clang_getDiagnosticSeverity(diagnostic)) { + case CXDiagnostic_Ignored: + return "ignored"; + case CXDiagnostic_Note: + return "note"; + case CXDiagnostic_Warning: + return "warning"; + case CXDiagnostic_Error: + return "error"; + case CXDiagnostic_Fatal: + return "fatal"; + } + + return "unknown"; +} + +void dumpDiagnostic(const CXDiagnostic &diagnostic) { + std::string file; + unsigned line = 0, column = 0, offset = 0; + CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); + if (!clang_equalLocations(location, clang_getNullLocation())) { + CXFile cxFile; + +// clang_getInstantiationLocation() has been marked deprecated and +// is aimed to be replaced by clang_getExpansionLocation(). +#if CINDEX_VERSION >= 6 + clang_getExpansionLocation(location, &cxFile, &line, &column, &offset); +#else + clang_getInstantiationLocation(location, &cxFile, &line, &column, &offset); +#endif + + file = cxStringToStd(clang_getFileName(cxFile)); + } + + const char *severity = diagnosticSeverity(diagnostic); + + std::string message = cxStringToStd(clang_getDiagnosticSpelling(diagnostic)); + + std::cout << '(' << support::quoted(file) // + << ' ' << line // + << ' ' << column // + << ' ' << offset // + << ' ' << severity // + << ' ' << support::quoted(message) // + << ")\n"; +} + +bool readFileContent(const std::string &filename, + Irony::UnsavedBuffer &outBuf) { + std::ifstream ifs(filename.c_str(), + std::ios::in | std::ios::binary | std::ios::ate); + + if (!ifs.is_open()) { + return false; + } + + // FIXME: it's possible that this method of reading the file is 100% reliable, + // I can't confirm that tellg() is guaranteed to return a byte count. + // std::streamoff does not mention 'byte'. + // In practice it seems to work but this may be just luck. + // See also this discussion: + // - http://stackoverflow.com/questions/22984956/tellg-function-give-wrong-size-of-file/22986486#22986486 + auto nbytes = ifs.tellg(); + + if (nbytes == std::ifstream::pos_type(-1)) { + return false; + } + + outBuf.resize(nbytes); + ifs.seekg(0, std::ios::beg); + + ifs.read(&outBuf[0], outBuf.size()); + + if (!ifs){ + outBuf.clear(); + return false; + } + + return true; +} + +} // unnamed namespace + +Irony::Irony() + : activeTu_(nullptr), activeCompletionResults_(nullptr), debug_(false) { +} + +void Irony::parse(const std::string &file, + const std::vector &flags) { + resetCache(); + activeTu_ = tuManager_.parse(file, flags, cxUnsavedFiles_); + file_ = file; + + if (activeTu_ == nullptr) { + std::cout << "(error . (" + << "parse-error" + << " \"failed to parse file\"" + << " " << support::quoted(file) << "))\n"; + return; + } + + std::cout << "(success . t)\n"; +} + +void Irony::diagnostics() const { + unsigned diagnosticCount; + + if (activeTu_ == nullptr) { + diagnosticCount = 0; + } else { + diagnosticCount = clang_getNumDiagnostics(activeTu_); + } + + std::cout << "(\n"; + + for (unsigned i = 0; i < diagnosticCount; ++i) { + CXDiagnostic diagnostic = clang_getDiagnostic(activeTu_, i); + + dumpDiagnostic(diagnostic); + + clang_disposeDiagnostic(diagnostic); + } + + std::cout << ")\n"; +} + +void Irony::resetCache() { + activeTu_ = nullptr; + + if (activeCompletionResults_ != nullptr) { + clang_disposeCodeCompleteResults(activeCompletionResults_); + activeCompletionResults_ = nullptr; + } +} + +void Irony::getType(unsigned line, unsigned col) const { + if (activeTu_ == nullptr) { + std::clog << "W: get-type - parse wasn't called\n"; + + std::cout << "nil\n"; + return; + } + + CXFile cxFile = clang_getFile(activeTu_, file_.c_str()); + CXSourceLocation sourceLoc = clang_getLocation(activeTu_, cxFile, line, col); + CXCursor cursor = clang_getCursor(activeTu_, sourceLoc); + + if (clang_Cursor_isNull(cursor)) { + // TODO: "error: no type at point"? + std::cout << "nil"; + return; + } + + CXType cxTypes[2]; + cxTypes[0] = clang_getCursorType(cursor); + cxTypes[1] = clang_getCanonicalType(cxTypes[0]); + + std::cout << "("; + + for (const CXType &cxType : cxTypes) { + CXString typeDescr = clang_getTypeSpelling(cxType); + std::string typeStr = clang_getCString(typeDescr); + clang_disposeString(typeDescr); + + if (typeStr.empty()) + break; + + std::cout << support::quoted(typeStr) << " "; + } + + std::cout << ")\n"; +} + +namespace { + +class CompletionChunk { +public: + explicit CompletionChunk(CXCompletionString completionString) + : completionString_(completionString) + , numChunks_(clang_getNumCompletionChunks(completionString_)) + , chunkIdx_(0) { + } + + bool hasNext() const { + return chunkIdx_ < numChunks_; + } + + void next() { + if (!hasNext()) { + assert(0 && "out of range completion chunk"); + abort(); + } + + ++chunkIdx_; + } + + CXCompletionChunkKind kind() const { + return clang_getCompletionChunkKind(completionString_, chunkIdx_); + } + + // TODO: operator>> so that one can re-use string allocated buffer + std::string text() const { + return cxStringToStd( + clang_getCompletionChunkText(completionString_, chunkIdx_)); + } + +private: + CXCompletionString completionString_; + unsigned int numChunks_; + unsigned chunkIdx_; +}; + +} // unnamed namespace + +void Irony::complete(const std::string &file, + unsigned line, + unsigned col, + const std::vector &flags) { + resetCache(); + + if (CXTranslationUnit tu = + tuManager_.getOrCreateTU(file, flags, cxUnsavedFiles_)) { + activeCompletionResults_ = + clang_codeCompleteAt(tu, + file.c_str(), + line, + col, + const_cast(cxUnsavedFiles_.data()), + cxUnsavedFiles_.size(), + (clang_defaultCodeCompleteOptions() & + ~CXCodeComplete_IncludeCodePatterns) +#if HAS_BRIEF_COMMENTS_IN_COMPLETION + | + CXCodeComplete_IncludeBriefComments +#endif + ); + } + + if (activeCompletionResults_ == nullptr) { + std::cout << "(error . (" + << "complete-error" + << " \"failed to perform code completion\"" + << " " << support::quoted(file) << " " << line << " " << col + << "))\n"; + return; + } + + clang_sortCodeCompletionResults(activeCompletionResults_->Results, + activeCompletionResults_->NumResults); + + std::cout << "(success . t)\n"; +} + +namespace { + +bool hasUppercase(const std::string &prefix) +{ + for (char c : prefix) { + if (std::isupper(c)) { + return true; + } + } + return false; +} + +bool isEqual(const bool insensitive, const char a, const char b) +{ + if (insensitive) { + return std::tolower(a) == std::tolower(b); + } else { + return a == b; + } +} + +bool startsWith(const std::string& str, const std::string &prefix, bool caseInsensitive) +{ + if (str.length() < prefix.length()) { + return false; + } + + const auto charCmp = [&](const char a, const char b) { + return isEqual(caseInsensitive, a, b); + }; + + auto res = std::mismatch(prefix.begin(), prefix.end(), str.begin(), charCmp); + return res.first == prefix.end(); +} + +bool isStyleCaseInsensitive(const std::string &prefix, PrefixMatchStyle style) +{ + if (style == PrefixMatchStyle::SmartCase) { + // For SmartCase style, do case insensitive matching only there isn't upper + // case letter. + if (!hasUppercase(prefix)) { + style = PrefixMatchStyle::CaseInsensitive; + } + } + return style == PrefixMatchStyle::CaseInsensitive; +} + +} // unnamed namespace + +void Irony::completionDiagnostics() const { + unsigned diagnosticCount; + + if (activeCompletionResults_ == nullptr) { + diagnosticCount = 0; + } else { + diagnosticCount = + clang_codeCompleteGetNumDiagnostics(activeCompletionResults_); + } + + std::cout << "(\n"; + + for (unsigned i = 0; i < diagnosticCount; ++i) { + CXDiagnostic diagnostic = + clang_codeCompleteGetDiagnostic(activeCompletionResults_, i); + + dumpDiagnostic(diagnostic); + clang_disposeDiagnostic(diagnostic); + } + + std::cout << ")\n"; +} + +void Irony::candidates(const std::string &prefix, PrefixMatchStyle style) const { + if (activeCompletionResults_ == nullptr) { + std::cout << "nil\n"; + return; + } + + bool caseInsensitive = isStyleCaseInsensitive(prefix, style); + + CXCodeCompleteResults *completions = activeCompletionResults_; + + std::cout << "(\n"; + + // re-use the same buffers to avoid unnecessary allocations + std::string typedtext, brief, resultType, prototype, postCompCar; + + std::vector postCompCdr; + + for (unsigned i = 0; i < completions->NumResults; ++i) { + CXCompletionResult candidate = completions->Results[i]; + CXAvailabilityKind availability = + clang_getCompletionAvailability(candidate.CompletionString); + + unsigned priority = + clang_getCompletionPriority(candidate.CompletionString); + unsigned annotationStart = 0; + bool typedTextSet = false; + bool hasPrefix = true; + + + if (availability == CXAvailability_NotAccessible || + availability == CXAvailability_NotAvailable) { + continue; + } + + typedtext.clear(); + brief.clear(); + resultType.clear(); + prototype.clear(); + postCompCar.clear(); + postCompCdr.clear(); + + for (CompletionChunk chunk(candidate.CompletionString); chunk.hasNext(); + chunk.next()) { + char ch = 0; + + auto chunkKind = chunk.kind(); + + switch (chunkKind) { + case CXCompletionChunk_ResultType: + resultType = chunk.text(); + break; + + case CXCompletionChunk_TypedText: + case CXCompletionChunk_Text: + case CXCompletionChunk_Placeholder: + case CXCompletionChunk_Informative: + case CXCompletionChunk_CurrentParameter: + prototype += chunk.text(); + break; + + case CXCompletionChunk_LeftParen: ch = '('; break; + case CXCompletionChunk_RightParen: ch = ')'; break; + case CXCompletionChunk_LeftBracket: ch = '['; break; + case CXCompletionChunk_RightBracket: ch = ']'; break; + case CXCompletionChunk_LeftBrace: ch = '{'; break; + case CXCompletionChunk_RightBrace: ch = '}'; break; + case CXCompletionChunk_LeftAngle: ch = '<'; break; + case CXCompletionChunk_RightAngle: ch = '>'; break; + case CXCompletionChunk_Comma: ch = ','; break; + case CXCompletionChunk_Colon: ch = ':'; break; + case CXCompletionChunk_SemiColon: ch = ';'; break; + case CXCompletionChunk_Equal: ch = '='; break; + case CXCompletionChunk_HorizontalSpace: ch = ' '; break; + case CXCompletionChunk_VerticalSpace: ch = '\n'; break; + + case CXCompletionChunk_Optional: + // ignored for now + break; + } + + if (ch != 0) { + prototype += ch; + // commas look better followed by a space + if (ch == ',') { + prototype += ' '; + } + } + + if (typedTextSet) { + if (ch != 0) { + postCompCar += ch; + if (ch == ',') { + postCompCar += ' '; + } + } else if (chunkKind == CXCompletionChunk_Text || + chunkKind == CXCompletionChunk_TypedText) { + postCompCar += chunk.text(); + } else if (chunkKind == CXCompletionChunk_Placeholder || + chunkKind == CXCompletionChunk_CurrentParameter) { + postCompCdr.push_back(postCompCar.size()); + postCompCar += chunk.text(); + postCompCdr.push_back(postCompCar.size()); + } + } + + // Consider only the first typed text. The CXCompletionChunk_TypedText + // doc suggests that exactly one typed text will be given but at least + // in Objective-C it seems that more than one can appear, see: + // https://github.com/Sarcasm/irony-mode/pull/78#issuecomment-37115538 + if (chunkKind == CXCompletionChunk_TypedText && !typedTextSet) { + typedtext = chunk.text(); + if (!startsWith(typedtext, prefix, caseInsensitive)) { + hasPrefix = false; + break; + } + // annotation is what comes after the typedtext + annotationStart = prototype.size(); + typedTextSet = true; + } + } + + if (!hasPrefix) { + continue; + } + if (!typedTextSet) { + // clang may generate candidates without any typedText, and we may + // generate some output like: + // ("" 1 "bool" "" "hasUppercase(const std::string &prefix)" + // 0 ("") available) + // That will cause infinite completion in irony.el + continue; + } + +#if HAS_BRIEF_COMMENTS_IN_COMPLETION + brief = cxStringToStd( + clang_getCompletionBriefComment(candidate.CompletionString)); +#endif + + // see irony-completion.el#irony-completion-candidates + std::cout << '(' << support::quoted(typedtext) + << ' ' << priority + << ' ' << support::quoted(resultType) + << ' ' << support::quoted(brief) + << ' ' << support::quoted(prototype) + << ' ' << annotationStart + << " (" << support::quoted(postCompCar); + for (unsigned index : postCompCdr) + std::cout << ' ' << index; + std::cout << ")" + << ")\n"; + } + + std::cout << ")\n"; +} + +void Irony::computeCxUnsaved() { + cxUnsavedFiles_.clear(); + + for (const auto &p : filenameToContent_) { + CXUnsavedFile cxUnsavedFile; + + cxUnsavedFile.Filename = p.first.c_str(); + cxUnsavedFile.Contents = p.second.data(); + cxUnsavedFile.Length = p.second.size(); + cxUnsavedFiles_.push_back(cxUnsavedFile); + } +} + +void Irony::setUnsaved(const std::string &file, + const std::string &unsavedContentFile) { + resetCache(); + + UnsavedBuffer content; + if (!readFileContent(unsavedContentFile, content)) { + filenameToContent_.erase(file); + std::cout << "(error . (" + << "file-read-error" + << " \"failed to read unsaved buffer\"" + << " " << support::quoted(file) << " " + << support::quoted(unsavedContentFile) << ")\n"; + } else { + filenameToContent_[file] = content; + std::cout << "(success . t)\n"; + } + + computeCxUnsaved(); +} + +void Irony::resetUnsaved(const std::string &file) { + resetCache(); + + const auto erasedCount = filenameToContent_.erase(file); + + if (erasedCount == 0) { + std::cout << "(error . (" + << "no-such-entry" + << " \"failed reset unsaved buffer\"" + << " " << support::quoted(file) << ")\n"; + } else { + std::cout << "(success . t)\n"; + } + + computeCxUnsaved(); +} + +void Irony::getCompileOptions(const std::string &buildDir, + const std::string &file) const { +#if !(HAS_COMPILATION_DATABASE) + + (void)buildDir; + (void)file; + + CXString cxVersionString = clang_getClangVersion(); + + std::cout << "(error . (" + << "unsupported" + << " \"compilation database requires Clang >= 3.2\"" + << " " << support::quoted(clang_getCString(cxVersionString)) + << "))\n"; + + clang_disposeString(cxVersionString); + + return; + +#else + CXCompilationDatabase_Error error; + CXCompilationDatabase db = + compDBCache_.fromDirectory(buildDir.c_str(), &error); + + switch (error) { + case CXCompilationDatabase_CanNotLoadDatabase: + std::cout << "(error . (" + << "cannot-load-database" + << " \"failed to load compilation database from directory\"" + << " " << support::quoted(buildDir) << "))\n"; + return; + + case CXCompilationDatabase_NoError: + break; + } + + CXCompileCommands compileCommands = + clang_CompilationDatabase_getCompileCommands(db, file.c_str()); + + std::cout << "(success . (\n"; + + for (unsigned i = 0, numCompileCommands = + clang_CompileCommands_getSize(compileCommands); + i < numCompileCommands; ++i) { + CXCompileCommand compileCommand = + clang_CompileCommands_getCommand(compileCommands, i); + + std::cout << "(" + << "("; + for (unsigned j = 0, + numArgs = clang_CompileCommand_getNumArgs(compileCommand); + j < numArgs; ++j) { + CXString arg = clang_CompileCommand_getArg(compileCommand, j); + std::cout << support::quoted(clang_getCString(arg)) << " "; + clang_disposeString(arg); + } + + std::cout << ")" + << " . "; + + CXString directory = clang_CompileCommand_getDirectory(compileCommand); + std::cout << support::quoted(clang_getCString(directory)); + clang_disposeString(directory); + + std::cout << ")\n"; + } + + std::cout << "))\n"; + + clang_CompileCommands_dispose(compileCommands); +#endif +} diff --git a/elpa/irony-20220110.849/server/src/Irony.h b/elpa/irony-20220110.849/server/src/Irony.h new file mode 100644 index 0000000..66968f5 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Irony.h @@ -0,0 +1,147 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin + * + * \brief irony-server "API" declarations. + * + * Contains the commands that the Emacs package relies on. These commands are + * mostly wrappers around a subset of the features provided by libclang. Command + * results are printed to \c std::cout as s-expr, in order to make it easy for + * Emacs to consume. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_IRONY_H_ +#define IRONY_MODE_SERVER_IRONY_H_ + +#include "TUManager.h" + +#include "CompDBCache.h" +#include "Style.h" + +#include +#include + +class Irony { +public: + // use std::string over std::vector because I have some doubts + // that libclang expect unsaved buffers to be a null terminated C strings + typedef std::string UnsavedBuffer; + +public: + Irony(); + + bool isDebugEnabled() const { + return debug_; + } + + /// \name Command + /// \{ + + /// \brief Set or unset debugging of commands. + void setDebug(bool enable) { + debug_ = enable; + } + + /// Parse or reparse the given file and compile options. + /// + /// If the compile options have changed, the translation unit is re-created to + /// take this into account. + /// + /// Output \c nil or \c t, whether or not parsing the translation unit + /// succeeded. + /// + /// \sa diagnostics(), getType() + void parse(const std::string &file, const std::vector &flags); + + /// Parse the given file for code completion. + /// + /// Shares the same semantics and output as \c parse(). + /// + /// \sa candidates(), completionDiagnostics() + void complete(const std::string &file, + unsigned line, + unsigned col, + const std::vector &flags); + + void setUnsaved(const std::string &file, + const std::string &unsavedContentFile); + + void resetUnsaved(const std::string &file); + + /// \} + + /// \name Queries + /// \{ + + /// \brief Retrieve the last parse diagnostics for the given file. + /// + /// \pre parse() was called. + void diagnostics() const; + + /// \brief Get types of symbol at a given location. + /// + /// Example: + /// + /// \code + /// typedef int MyType; + /// MyType a; + /// \endcode + /// + /// Type of cursor location for 'a' is: + /// + /// \code{.el} + /// ("MyType" "int") + /// \endcode + /// + /// TODO: test with CXString(), seems to be twice the same string + /// + void getType(unsigned line, unsigned col) const; + + /// Get all the completion candidates. + /// + /// \pre complete() was called. + void candidates(const std::string &prefix, PrefixMatchStyle style) const; + + /// Get the diagnostics produced by the last \c complete(). + /// + /// \pre complete() was called. + void completionDiagnostics() const; + + /// \brief Get compile options from JSON database. + /// + /// \param buildDir Directory containing compile_commands.json + /// \param file File to obtain compile commands for. + /// + /// Example output: + /// + /// \code{.el} + /// ( + /// (("-Wfoo" "-DBAR" "-Iqux") . "/path/to/working/directory") + /// (("-Wfoo-alt" "-DBAR_ALT" "-Iqux/alt") . "/alt/working/directory") + /// ) + /// \endcode + /// + void getCompileOptions(const std::string &buildDir, + const std::string &file) const; + + /// \} + +private: + void resetCache(); + void computeCxUnsaved(); + +private: + mutable CompDBCache compDBCache_; + TUManager tuManager_; + std::map filenameToContent_; + CXTranslationUnit activeTu_; + std::string file_; + std::vector cxUnsavedFiles_; + CXCodeCompleteResults *activeCompletionResults_; + bool debug_; +}; + +#endif // IRONY_MODE_SERVER_IRONY_H_ diff --git a/elpa/irony-20220110.849/server/src/Style.h b/elpa/irony-20220110.849/server/src/Style.h new file mode 100644 index 0000000..52adb5e --- /dev/null +++ b/elpa/irony-20220110.849/server/src/Style.h @@ -0,0 +1,17 @@ +/**-*-C++-*- + * \file + * + * \brief Style definition. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ +#ifndef IRONY_MODE_SERVER_STYLE_H_ +#define IRONY_MODE_SERVER_STYLE_H_ + +enum class PrefixMatchStyle { + Exact, CaseInsensitive, SmartCase, +}; + + +#endif //IRONY_MODE_SERVER_STYLE_H_ diff --git a/elpa/irony-20220110.849/server/src/TUManager.cpp b/elpa/irony-20220110.849/server/src/TUManager.cpp new file mode 100644 index 0000000..afbdc82 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/TUManager.cpp @@ -0,0 +1,182 @@ +/** + * \file + * \author Guillaume Papin + * + * \brief See TUManager.hh + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + * + */ + +#include "TUManager.h" + +#include + +TUManager::TUManager() + : index_(clang_createIndex(0, 0)) + , translationUnits_() + , parseTUOptions_(clang_defaultEditingTranslationUnitOptions()) { + + // this seems necessary to trigger "correct" reparse (/codeCompleteAt) + // clang_reparseTranslationUnit documentation states: + // + // > * \param TU The translation unit whose contents will be re-parsed. The + // > * translation unit must originally have been built with + // > * \c clang_createTranslationUnitFromSourceFile(). + // + // clang_createTranslationUnitFromSourceFile() is just a call to + // clang_parseTranslationUnit() with + // CXTranslationUnit_DetailedPreprocessingRecord enabled but because we want + // some other flags to be set we can't just call + // clang_createTranslationUnitFromSourceFile() + parseTUOptions_ |= CXTranslationUnit_DetailedPreprocessingRecord; + +#if HAS_BRIEF_COMMENTS_IN_COMPLETION + parseTUOptions_ |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; +#endif + + // XXX: A bug in old version of Clang (at least '3.1-8') caused the completion + // to fail on the standard library types when + // CXTranslationUnit_PrecompiledPreamble is used. We disable this option for + // old versions of libclang. As a result the completion will work but + // significantly slower. + // + // -- https://github.com/Sarcasm/irony-mode/issues/4 + if (CINDEX_VERSION < 6) { + parseTUOptions_ &= ~CXTranslationUnit_PrecompiledPreamble; + } + +#if CINDEX_VERSION >= 34 + // Keep going even after fatal errors, or syntax checking will stop after the + // first error. For instance unused variables will not be reported until the + // error has been fixed. + parseTUOptions_ |= CXTranslationUnit_KeepGoing; +#endif +} + +TUManager::~TUManager() { + clang_disposeIndex(index_); +} + +CXTranslationUnit &TUManager::tuRef(const std::string &filename, + const std::vector &flags) { + CXTranslationUnit &tu = translationUnits_[filename]; + + // if the flags changed since the last time, invalidate the translation unit + auto &flagsCache = flagsPerFileCache_[filename]; + if (flagsCache.size() != flags.size() || + !std::equal(flagsCache.begin(), flagsCache.end(), flags.begin())) { + if (tu) { + clang_disposeTranslationUnit(tu); + tu = nullptr; + } + // remember the flags for the next parse + flagsCache = flags; + } + return tu; +} + +CXTranslationUnit +TUManager::parse(const std::string &filename, + const std::vector &flags, + const std::vector &unsavedFiles) { + CXTranslationUnit &tu = tuRef(filename, flags); + + if (tu == nullptr) { + std::vector argv; + +#ifdef CLANG_RESOURCE_DIR + // Make sure libclang find its builtin headers, this is a known issue with + // libclang, see: + // - http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-July/022893.html + // + // > Make sure that Clang is using its own . It will be in a directory + // > ending in clang/3.2/include/ where 3.2 is the version of clang that you + // > are using. You may need to explicitly add it to your header search. + // > Usually clang finds this directory relative to the executable with + // > CompilerInvocation::GetResourcesPath(Argv0, MainAddr), but using just + // > the libraries, it can't automatically find it. + argv.push_back("-resource-dir"); + argv.push_back(CLANG_RESOURCE_DIR); +#endif + + for (auto &flag : flags) { + argv.push_back(flag.c_str()); + } + + tu = clang_parseTranslationUnit( + index_, + filename.c_str(), + argv.data(), + static_cast(argv.size()), + const_cast(unsavedFiles.data()), + unsavedFiles.size(), + parseTUOptions_); + } + + if (tu == nullptr) { + std::clog << "error: libclang couldn't parse '" << filename << "'\n"; + return nullptr; + } + + // Reparsing is necessary to enable optimizations. + // + // From the clang mailing list (cfe-dev): + // From: Douglas Gregor + // Subject: Re: Clang indexing library performance + // ... + // You want to use the "default editing options" when parsing the translation + // unit + // clang_defaultEditingTranslationUnitOptions() + // and then reparse at least once. That will enable the various + // code-completion optimizations that should bring this time down + // significantly. + if (clang_reparseTranslationUnit( + tu, + unsavedFiles.size(), + const_cast(unsavedFiles.data()), + clang_defaultReparseOptions(tu))) { + // a 'fatal' error occured (even a diagnostic is impossible) + clang_disposeTranslationUnit(tu); + std::clog << "error: libclang couldn't reparse '" << filename << "'\n"; + tu = 0; + return nullptr; + } + + return tu; +} + +CXTranslationUnit +TUManager::getOrCreateTU(const std::string &filename, + const std::vector &flags, + const std::vector &unsavedFiles) { + if (auto tu = tuRef(filename, flags)) + return tu; + + return parse(filename, flags, unsavedFiles); +} + +void TUManager::invalidateCachedTU(const std::string &filename) { + TranslationUnitsMap::iterator it = translationUnits_.find(filename); + + if (it != translationUnits_.end()) { + if (CXTranslationUnit &tu = it->second) + clang_disposeTranslationUnit(tu); + + translationUnits_.erase(it); + } +} + +void TUManager::invalidateAllCachedTUs() { + TranslationUnitsMap::iterator it = translationUnits_.begin(); + + while (it != translationUnits_.end()) { + if (CXTranslationUnit &tu = it->second) { + clang_disposeTranslationUnit(tu); + translationUnits_.erase(it++); // post-increment keeps the iterator valid + } else { + ++it; + } + } +} diff --git a/elpa/irony-20220110.849/server/src/TUManager.h b/elpa/irony-20220110.849/server/src/TUManager.h new file mode 100644 index 0000000..bd7730d --- /dev/null +++ b/elpa/irony-20220110.849/server/src/TUManager.h @@ -0,0 +1,109 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin + * + * \brief Translation Unit manager. + * + * Keeps a cache of translation units, reparsing or recreating them as + * necessary. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_TUMANAGER_H_ +#define IRONY_MODE_SERVER_TUMANAGER_H_ + +#include "support/CIndex.h" +#include "support/NonCopyable.h" + +#include +#include +#include + +class TUManager : public util::NonCopyable { +public: + TUManager(); + ~TUManager(); + + /** + * \brief Parse \p filename with flag \p flags. + * + * The first time call \c clang_parseTranslationUnit() and save the TU in the + * member \c translationUnits_, The next call with the same \p filename will + * call \c clang_reparseTranslationUnit(). + * + * usage: + * \code + * std::vector flags; + * flags.push_back("-I../utils"); + * CXTranslationUnit tu = tuManager.parse("file.cpp", flags); + * + * if (! tu) + * std::cerr << "parsing translation unit failed\n"; + * \endcode + * + * \return The translation unit, if the parsing failed the translation unit + * will be \c NULL. + */ + CXTranslationUnit parse(const std::string &filename, + const std::vector &flags, + const std::vector &unsavedFiles); + + /** + * \brief Retrieve, creating it if necessary the TU associated to filename. + * + * \return The translation unit. Will be NULL if the translation unit couldn't + * be created. + */ + CXTranslationUnit getOrCreateTU(const std::string &filename, + const std::vector &flags, + const std::vector &unsavedFiles); + + /** + * \brief Invalidate a given cached TU, the next use of a TU will require + * reparsing. + * + * This can be useful for example: when the flags used to compile a file have + * changed. + * + * \param filename The filename for which the associated + * translation unit flags need to be invalidated. + * + * \sa invalidateAllCachedTUs() + */ + void invalidateCachedTU(const std::string &filename); + + /** + * \brief Invalidate all cached TU, the next use of a TU will require + * reparsing. + * + * \sa invalidateCachedTU() + */ + void invalidateAllCachedTUs(); + +private: + /** + * \brief Get a reference to the translation unit that matches \p filename + * with the given set of flags. + * + * The TU will be null if it has never been parsed or if the flags have + * changed. + * + * \todo Find a proper name. + */ + CXTranslationUnit &tuRef(const std::string &filename, + const std::vector &flags); + +private: + typedef std::map TranslationUnitsMap; + typedef std::map> FilenameFlagsMap; + +private: + CXIndex index_; + TranslationUnitsMap translationUnits_; // cache variable + FilenameFlagsMap flagsPerFileCache_; + unsigned parseTUOptions_; +}; + +#endif /* !IRONY_MODE_SERVER_TUMANAGER_H_ */ diff --git a/elpa/irony-20220110.849/server/src/main.cpp b/elpa/irony-20220110.849/server/src/main.cpp new file mode 100644 index 0000000..7797528 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/main.cpp @@ -0,0 +1,235 @@ +/** + * \file + * \author Guillaume Papin + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "Irony.h" +#include "Command.h" + +#include "support/CIndex.h" +#include "support/CommandLineParser.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static void printHelp() { + std::cout << "usage: irony-server [OPTIONS...] [COMMAND] [ARGS...]\n" + "\n" + "Options:\n" + " -v, --version\n" + " -h, --help\n" + " -i, --interactive\n" + " -d, --debug\n" + " --log-file PATH\n" + "\n" + "Commands:\n"; + +#define X(sym, str, desc) \ + if (Command::sym != Command::Unknown) \ + std::cout << std::left << std::setw(25) << " " str << desc << "\n"; +#include "Commands.def" +} + +static void printVersion() { + // do not change the format for the first line, external programs should be + // able to rely on it + std::cout << "irony-server version " IRONY_PACKAGE_VERSION "\n"; + + CXString cxVersionString = clang_getClangVersion(); + std::cout << clang_getCString(cxVersionString) << "\n"; + clang_disposeString(cxVersionString); +} + +struct CommandProviderInterface { + virtual ~CommandProviderInterface() { } + + virtual std::vector nextCommand() = 0; +}; + +struct CommandLineCommandProvider : CommandProviderInterface { + CommandLineCommandProvider(const std::vector &argv) + : argv_(argv), firstCall_(true) { + } + + std::vector nextCommand() { + if (firstCall_) { + firstCall_ = false; + return argv_; + } + + return std::vector(1, "exit"); + } + +private: + std::vector argv_; + bool firstCall_; +}; + +struct InteractiveCommandProvider : CommandProviderInterface { + std::vector nextCommand() { + std::string line; + + if (std::getline(std::cin, line)) { + return unescapeCommandLine(line); + } + + return std::vector(1, "exit"); + } +}; + +struct RestoreClogOnExit { + RestoreClogOnExit() : rdbuf_(std::clog.rdbuf()) { + } + + ~RestoreClogOnExit() { + std::clog.rdbuf(rdbuf_); + } + +private: + RestoreClogOnExit(const RestoreClogOnExit &); + RestoreClogOnExit &operator=(const RestoreClogOnExit &); + +private: + std::streambuf *rdbuf_; +}; + +int main(int ac, const char *av[]) { + std::vector argv(&av[1], &av[ac]); + + // stick to STL streams, no mix of C and C++ for IO operations + std::ios_base::sync_with_stdio(false); + + bool interactiveMode = false; + + Irony irony; + + if (ac == 1) { + printHelp(); + return 1; + } + + std::ofstream logFile; + + // When logging to a specific file, std::clog.rdbuf() is replaced by the log + // file's one. When we return from the main, this buffer is deleted (at the + // same time as logFile) but std::clog is still active, and will try to + // release the rdbuf() which has already been released in logFile's + // destructor. To avoid this we restore std::clog()'s original rdbuf on exit. + RestoreClogOnExit clogBufferRestorer; + + unsigned optCount = 0; + while (optCount < argv.size()) { + const std::string &opt = argv[optCount]; + + if (opt.c_str()[0] != '-') + break; + + if (opt == "--help" || opt == "-h") { + printHelp(); + return 0; + } + + if (opt == "--version" || opt == "-v") { + printVersion(); + return 0; + } + + if (opt == "--interactive" || opt == "-i") { + interactiveMode = true; + } else if (opt == "--debug" || opt == "-d") { + irony.setDebug(true); + } else if (opt == "--log-file" && (optCount + 1) < argv.size()) { + ++optCount; + logFile.open(argv[optCount]); + std::clog.rdbuf(logFile.rdbuf()); + } else { + std::cerr << "error: invalid option '" << opt << "'\n"; + return 1; + } + + ++optCount; + } + + argv.erase(argv.begin(), argv.begin() + optCount); + + CommandParser commandParser; + std::unique_ptr commandProvider; + + if (interactiveMode) { + commandProvider.reset(new InteractiveCommandProvider()); + } else { + commandProvider.reset(new CommandLineCommandProvider(argv)); + } + + while (Command *c = commandParser.parse(commandProvider->nextCommand())) { + if (c->action != Command::Exit) { + std::clog << "execute: " << *c << std::endl; + } + + switch (c->action) { + case Command::Help: + printHelp(); + break; + + case Command::Candidates: + irony.candidates(c->prefix, c->style); + break; + + case Command::CompletionDiagnostics: + irony.completionDiagnostics(); + break; + + case Command::Complete: + irony.complete(c->file, c->line, c->column, c->flags); + break; + + case Command::Diagnostics: + irony.diagnostics(); + break; + + case Command::Exit: + return 0; + + case Command::GetCompileOptions: + irony.getCompileOptions(c->dir, c->file); + break; + + case Command::GetType: + irony.getType(c->line, c->column); + break; + + case Command::Parse: + irony.parse(c->file, c->flags); + break; + + case Command::SetDebug: + irony.setDebug(c->opt); + break; + + case Command::SetUnsaved: + irony.setUnsaved(c->file, c->unsavedFile); + break; + + case Command::ResetUnsaved: + irony.resetUnsaved(c->file); + break; + + case Command::Unknown: + assert(0 && "unreacheable code...reached!"); + break; + } + + std::cout << "\n;;EOT\n" << std::flush; + } + + return 1; +} diff --git a/elpa/irony-20220110.849/server/src/support/CIndex.h b/elpa/irony-20220110.849/server/src/support/CIndex.h new file mode 100644 index 0000000..89d3f62 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/CIndex.h @@ -0,0 +1,33 @@ +/** + * \file + * \brief Wrapper around Clang Indexing Public C Interface header. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ +#define IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ + +#include + +/// Use \#if CINDEX_VERSION_VERSION > 10047 to test for +/// CINDEX_VERSION_MAJOR = 1 and CINDEX_VERSION_MINOR = 47. +#ifndef CINDEX_VERSION +#define CINDEX_VERSION 0 // pre-clang 3.2 support +#endif + +#if CINDEX_VERSION >= 6 +#define HAS_BRIEF_COMMENTS_IN_COMPLETION 1 +#else +#define HAS_BRIEF_COMMENTS_IN_COMPLETION 0 +#endif + +#if CINDEX_VERSION >= 6 +#define HAS_COMPILATION_DATABASE 1 +#include +#else +#define HAS_COMPILATION_DATABASE 0 +#endif + +#endif /* !IRONY_MODE_SERVER_SUPPORT_CINDEXVERSION_H_ */ diff --git a/elpa/irony-20220110.849/server/src/support/CommandLineParser.cpp b/elpa/irony-20220110.849/server/src/support/CommandLineParser.cpp new file mode 100644 index 0000000..a838092 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/CommandLineParser.cpp @@ -0,0 +1,119 @@ +/** + * \file + * \author Guillaume Papin + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "CommandLineParser.h" + +namespace { + +/// \brief A parser for escaped strings of command line arguments. +/// +/// Assumes \-escaping for quoted arguments (see the documentation of +/// unescapeCommandLine(...)). +class CommandLineArgumentParser { +public: + CommandLineArgumentParser(const std::string &commandLine) + : input_(commandLine), position_(input_.begin() - 1) { + } + + std::vector parse() { + bool hasMoreInput = true; + while (hasMoreInput && nextNonWhitespace()) { + std::string argument; + hasMoreInput = parseStringInto(argument); + commandLine_.push_back(argument); + } + return commandLine_; + } + +private: + // All private methods return true if there is more input available. + + bool parseStringInto(std::string &string) { + do { + if (*position_ == '"') { + if (!parseDoubleQuotedStringInto(string)) + return false; + } else if (*position_ == '\'') { + if (!parseSingleQuotedStringInto(string)) + return false; + } else { + if (!parseFreeStringInto(string)) + return false; + } + } while (*position_ != ' '); + return true; + } + + bool parseDoubleQuotedStringInto(std::string &string) { + if (!next()) + return false; + while (*position_ != '"') { + if (!skipEscapeCharacter()) + return false; + string.push_back(*position_); + if (!next()) + return false; + } + return next(); + } + + bool parseSingleQuotedStringInto(std::string &string) { + if (!next()) + return false; + while (*position_ != '\'') { + string.push_back(*position_); + if (!next()) + return false; + } + return next(); + } + + bool parseFreeStringInto(std::string &string) { + do { + if (!skipEscapeCharacter()) + return false; + string.push_back(*position_); + if (!next()) + return false; + } while (*position_ != ' ' && *position_ != '"' && *position_ != '\''); + return true; + } + + bool skipEscapeCharacter() { + if (*position_ == '\\') { + return next(); + } + return true; + } + + bool nextNonWhitespace() { + do { + if (!next()) + return false; + } while (*position_ == ' '); + return true; + } + + bool next() { + ++position_; + return position_ != input_.end(); + } + +private: + const std::string input_; + std::string::const_iterator position_; + std::vector commandLine_; +}; + +} // unnamed namespace + +std::vector +unescapeCommandLine(const std::string &escapedCommandLine) { + CommandLineArgumentParser parser(escapedCommandLine); + return parser.parse(); +} diff --git a/elpa/irony-20220110.849/server/src/support/CommandLineParser.h b/elpa/irony-20220110.849/server/src/support/CommandLineParser.h new file mode 100644 index 0000000..b764797 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/CommandLineParser.h @@ -0,0 +1,21 @@ +/** + * \file + * \brief Facility to parse a command line into a string array. + * + * \note Please note that the code borrowed from the Clang, + * lib/Tooling/JSONCompilationDatabase.cpp. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ +#define IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ + +#include +#include + +std::vector +unescapeCommandLine(const std::string &escapedCommandLine); + +#endif // IRONY_MODE_SERVER_SUPPORT_COMMAND_LINE_PARSER_H_ diff --git a/elpa/irony-20220110.849/server/src/support/NonCopyable.h b/elpa/irony-20220110.849/server/src/support/NonCopyable.h new file mode 100644 index 0000000..d30a5b2 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/NonCopyable.h @@ -0,0 +1,34 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin + * + * \brief NonCopyable class like in Boost. + * + * \see http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-copyable_Mixin + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ +#define IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ + +namespace util { + +class NonCopyable { +protected: + NonCopyable() { + } + + // Protected non-virtual destructor + ~NonCopyable() { + } + +private: + NonCopyable(const NonCopyable &); + NonCopyable &operator=(const NonCopyable &); +}; + +} // ! namespace util + +#endif /* !IRONY_MODE_SERVER_SUPPORT_NONCOPYABLE_H_ */ diff --git a/elpa/irony-20220110.849/server/src/support/TemporaryFile.cpp b/elpa/irony-20220110.849/server/src/support/TemporaryFile.cpp new file mode 100644 index 0000000..e7393e1 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/TemporaryFile.cpp @@ -0,0 +1,74 @@ +/** + * \file + * \author Guillaume Papin + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "TemporaryFile.h" + +#include +#include +#include +#include +#include +#include + +static std::string getTemporaryFileDirectory() { + const char *temporaryDirEnvVars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; + + for (const char *envVar : temporaryDirEnvVars) { + if (const char *dir = std::getenv(envVar)) + return dir; + } + + return "/tmp"; +} + +TemporaryFile::TemporaryFile(const std::string &prefix, + const std::string &suffix) + : pathOrPattern_(prefix + "-%%%%%%" + suffix) { +} + +TemporaryFile::~TemporaryFile() { + if (openedFile_) { + openedFile_.reset(); + std::remove(pathOrPattern_.c_str()); + } +} + +const std::string &TemporaryFile::getPath() { + if (!openedFile_) { + openedFile_.reset(new std::fstream); + + std::random_device rd; + std::default_random_engine e(rd()); + std::uniform_int_distribution dist(0, 15); + std::string pattern = pathOrPattern_; + std::string tmpDir = getTemporaryFileDirectory() + "/"; + int i = 0; + + do { + // exiting is better than infinite loop + if (++i > TemporaryFile::MAX_ATTEMPS) { + std::cerr << "error: couldn't create temporary file, please check your " + "temporary file directory (" << tmpDir << ")\n"; + exit(EXIT_FAILURE); + } + + // make the filename based on the pattern + std::transform(pattern.begin(), + pattern.end(), + pathOrPattern_.begin(), + [&e, &dist](char ch) { + return ch == '%' ? "0123456789abcdef"[dist(e)] : ch; + }); + // create the file + openedFile_->open(tmpDir + pathOrPattern_, std::ios_base::out); + } while (!openedFile_->is_open()); + pathOrPattern_ = tmpDir + pathOrPattern_; + } + + return pathOrPattern_; +} diff --git a/elpa/irony-20220110.849/server/src/support/TemporaryFile.h b/elpa/irony-20220110.849/server/src/support/TemporaryFile.h new file mode 100644 index 0000000..5bf77f4 --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/TemporaryFile.h @@ -0,0 +1,36 @@ +/**-*-C++-*- + * \file + * \author Guillaume Papin + * + * \brief Not the best piece of code out there. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ +#define IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ + +#include +#include +#include + +class TemporaryFile { + enum { + // if we can't create the temp file, exits. + MAX_ATTEMPS = 25 + }; + +public: + TemporaryFile(const std::string &prefix, const std::string &suffix = ""); + ~TemporaryFile(); + + /// Returns the path of this temporary filename + const std::string &getPath(); + +private: + std::string pathOrPattern_; + std::unique_ptr openedFile_; +}; + +#endif // IRONY_MODE_SERVER_SUPPORT_TEMPORARY_FILE_H_ diff --git a/elpa/irony-20220110.849/server/src/support/iomanip_quoted.h b/elpa/irony-20220110.849/server/src/support/iomanip_quoted.h new file mode 100644 index 0000000..a8ca38b --- /dev/null +++ b/elpa/irony-20220110.849/server/src/support/iomanip_quoted.h @@ -0,0 +1,52 @@ +/**-*-C++-*- + * \file + * \brief Dumb implementation of something that might look like C++14 + * std::quoted. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ +#define IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ + +#include +#include + +namespace support { +namespace detail { + +struct QuotedStringProxy { + QuotedStringProxy(const std::string &s) : s(s) { + } + + std::string s; +}; + +std::ostream &operator<<(std::ostream &os, const QuotedStringProxy &q) { + const std::string &s = q.s; + + os << '"'; + if (s.find_first_of("\"\\") == std::string::npos) { + os << s; + } else { + for (auto ch : s) { + if (ch == '\\' || ch == '"') + os << '\\'; + + os << ch; + } + } + os << '"'; + return os; +} + +} // namespace detail + +detail::QuotedStringProxy quoted(const std::string &s) { + return detail::QuotedStringProxy(s); +} + +} // namespace support + +#endif // IRONY_MODE_SERVER_SUPPORT_IOMANIP_QUOTED_H_ diff --git a/elpa/irony-20220110.849/server/test/CMakeLists.txt b/elpa/irony-20220110.849/server/test/CMakeLists.txt new file mode 100644 index 0000000..078b69a --- /dev/null +++ b/elpa/irony-20220110.849/server/test/CMakeLists.txt @@ -0,0 +1,3 @@ +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) + +add_subdirectory (elisp) diff --git a/elpa/irony-20220110.849/server/test/elisp/CMakeLists.txt b/elpa/irony-20220110.849/server/test/elisp/CMakeLists.txt new file mode 100644 index 0000000..8877a89 --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/CMakeLists.txt @@ -0,0 +1,47 @@ +# On MS-Windows, emacs_dir is a special environment variable, which +# indicates the full path of the directory in which Emacs is +# installed. +# +# There is no standard location that I know of for Emacs on Windows so +# using this special environment variable will at least help people +# who build the server from inside Emacs. +if(DEFINED ENV{emacs_dir}) + list(APPEND EMACS_EXECUTABLE_HINTS $ENV{emacs_dir}/bin) +endif() + +find_program(EMACS_EXECUTABLE emacs + HINTS ${EMACS_EXECUTABLE_HINTS}) + +if (EMACS_EXECUTABLE) + message(STATUS "Found emacs: ${EMACS_EXECUTABLE}") +else() + message(WARNING "emacs not found: elisp tests will be skipped!") + return() +endif() + +# +# add_ert_test() +# +# Create a test which run the given Elisp script using the Emacs ERT testing +# framework. +# +# The target is deduced from the ``FileName`` argument, e.g: the file foo.el +# will be the target 'check-foo-el'. +# +# FIXME: assumes MELPA is configured... +function(add_ert_test test_file) + get_filename_component(name ${test_file} NAME_WE) + add_test(check-${name}-el + ${EMACS_EXECUTABLE} -Q --batch + -l package + --eval "(package-initialize)" + --eval "(unless (require 'cl-lib nil t) (package-refresh-contents) (package-install 'cl-lib))" + -l ${CMAKE_CURRENT_SOURCE_DIR}/${test_file} + -f ert-run-tests-batch-and-exit) + + set_tests_properties(check-${name}-el PROPERTIES TIMEOUT 15) +endfunction() + +add_ert_test(irony.el) +add_ert_test(irony-iotask.el) +add_ert_test(irony-cdb-json.el) diff --git a/elpa/irony-20220110.849/server/test/elisp/irony-cdb-json.el b/elpa/irony-20220110.849/server/test/elisp/irony-cdb-json.el new file mode 100644 index 0000000..a810dae --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/irony-cdb-json.el @@ -0,0 +1,87 @@ +;; -*-no-byte-compile: t; -*- +(load + (concat (file-name-directory (or load-file-name buffer-file-name)) + "test-config")) + +(require 'irony-cdb-json) +(require 'cl-lib) + +(defconst irony-cdb/compile-command + '((file . "../src/file.cc") + (directory . "/home/user/project/build") + (command . "/usr/bin/clang++ -DSOMEDEF=1 -c -o file.o /home/user/project/src/file.cc"))) + +(ert-deftest cdb/parse/simple/path-is-absolute () + (should + (equal "/home/user/project/src/file.cc" + (nth 0 (irony-cdb-json--transform-compile-command + irony-cdb/compile-command))))) + +(ert-deftest cdb/parse/simple/compile-options () + (should + (equal '("-DSOMEDEF=1") + (nth 1 (irony-cdb-json--transform-compile-command + irony-cdb/compile-command))))) + +(ert-deftest cdb/parse/simple/invocation-directory () + (should + (equal "/home/user/project/build" + (nth 2 (irony-cdb-json--transform-compile-command + irony-cdb/compile-command))))) + +(ert-deftest cdb/choose-closest-path/chooses-closest () + (should + (equal "/tmp/a/cdb" + (irony-cdb--choose-closest-path "/tmp/a/1" + '("/tmp/a/cdb" "/tmp/cdb"))))) + +(ert-deftest cdb/choose-closest-path/chooses-closest2 () + (should + (equal "/tmp/a/cdb" + (irony-cdb--choose-closest-path "/tmp/a/1" + '("/tmp/cdb" "/tmp/a/cdb"))))) + +(ert-deftest cdb/choose-closest-path/prefers-deeper () + (should + (equal "/tmp/a/build/cdb" + (irony-cdb--choose-closest-path "/tmp/a/1" + '("/tmp/a/build/cdb" "/tmp/cdb"))))) + +(ert-deftest cdb/choose-closest-path/prefers-deeper2 () + (should + (equal "/tmp/a/build/cdb" + (irony-cdb--choose-closest-path "/tmp/a/1" + '("/tmp/cdb" "/tmp/a/build/cdb"))))) + +(ert-deftest cdb/choose-closest-path/will-survive-garbage () + (should + (equal nil + (irony-cdb--choose-closest-path "/tmp/a/1" + 'ordures)))) + +; http://endlessparentheses.com/understanding-letf-and-how-it-replaces-flet.html +(ert-deftest cdb/locate-db/choose-among-candidates () + (should + (equal "/foo/build/cdb" + (cl-letf (((symbol-function 'locate-dominating-file) + (lambda (file name) + (cond + ((string= name "./cdb") "/") ; found /cdb + ((string= name "build/cdb") "/foo/") ; found /foo/build/cdb + )))) + (irony-cdb--locate-dominating-file-with-dirs "/foo/bar/qux.cpp" + "cdb" + '("." "build" "out/x86_64")))))) + +(ert-deftest cdb/locate-dominating-file-with-dirs/children-first () + (should + (equal "/tmp/foo/bar/out/x86_64/cdb" + (cl-letf (((symbol-function 'locate-dominating-file) + (lambda (file name) + (cond + ((string= name "./cdb") "/tmp/foo/") ; found /tmp/foo/cdb + ((string= name "out/x86_64/cdb") "/tmp/foo/bar/") ;found /tmp/foo/bar/out/x86_64/cdb + )))) + (irony-cdb--locate-dominating-file-with-dirs "/tmp/foo/bar/qux.cpp" + "cdb" + '("." "out/x86_64" )))))) diff --git a/elpa/irony-20220110.849/server/test/elisp/irony-iotask.el b/elpa/irony-20220110.849/server/test/elisp/irony-iotask.el new file mode 100644 index 0000000..7ef213b --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/irony-iotask.el @@ -0,0 +1,249 @@ +;; -*-no-byte-compile: t; -*- +(load (concat (file-name-directory (or load-file-name + buffer-file-name)) + "test-config")) + +;; load irony-iotask +;; +;; XXX: No idea why this is necessary, test-config already adds the directory to +;; the load-path so irony is found... +(unless (require 'irony-iotask nil t) + (let ((irony-iotask-dir (expand-file-name "../../.." test-dir))) + (add-to-list 'load-path irony-iotask-dir) + (require 'irony-iotask))) + +(defun irony-iotask-echo-process-exit-filter (process output) + (when (buffer-live-p (process-buffer process)) + (with-current-buffer (process-buffer process) + (goto-char (process-mark process)) + (insert output) + (set-marker (process-mark process) (point)) + (when (>= (buffer-size) (length "exit\n")) + (should (string= (buffer-string) "exit\n")) + (erase-buffer))))) + +;; Note: these tests use process communication with the standard I/O streams. +;; The subprocess used for this communication is Emacs. +;; +;; The following article provides useful information for using Elisp as a +;; scripting language, Emacs as an interpreter, it details how the standard I/O +;; streams works in Elisp scripts: +;; - http://www.lunaryorn.com/2014/08/12/emacs-script-pitfalls.html + +(defmacro irony-iotask/with-echo-process (&rest body) + "Start an Emacs process that runs the given PROCESS-SCRIPT. + +The process is setup with `irony-iotask-setup-process'. + +It's possible to schedule some iotasks in the BODY for testing. + +There is an exposed variable named `process' available for use in +BODY. + +Elisp is used as a scripting language because it should be +available on all OSes irony-iotask support." + (declare (indent 1)) + `(let ((process-connection-type nil) + (process-adaptive-read-buffering nil) + process) + (setq process + (start-process "emacs-irony-test" + "*emacs-irony-test*" + (expand-file-name invocation-name + invocation-directory) + "-Q" + "--batch" + "--eval" + (prin1-to-string + '(let ((msg)) + (while (not (equal msg "exit")) + (setq msg (read-from-minibuffer "")) + (message msg)))))) + (unwind-protect + (progn + (irony-iotask-setup-process process) + ,@body) + ;; the iotask process filter does not clean the process buffer + ;; at the end of a request, but at the begining of a new one + (with-current-buffer (process-buffer process) + (erase-buffer)) + (set-process-filter process #'irony-iotask-echo-process-exit-filter) + (process-send-string process "exit\n") + ;; wait for the process to finish normally, or kill it if it doesn't + (with-timeout (1 (kill-process process)) + (while (process-live-p process) + (sit-for 0.05))) + ;; start with a clean buffer, + ;; Emacs 24.3 seems to keep some + (kill-buffer (process-buffer process)) + (delete-process process)))) + +;; irony-iotask-result + +(ert-deftest irony-iotask-result/ready-p-value () + (let ((result (irony-iotask-result-create))) + (should-not (irony-iotask-result-valid-p result)) + (irony-iotask-result-set-value result 1) + (should (irony-iotask-result-valid-p result)))) + +(ert-deftest irony-iotask-result/ready-p-error () + (let ((result (irony-iotask-result-create))) + (should-not (irony-iotask-result-valid-p result)) + (irony-iotask-result-set-error result 'irony-iotask-error (list "blah")) + (should (irony-iotask-result-valid-p result)))) + +(ert-deftest irony-iotask-result/set-value () + (let ((result (irony-iotask-result-create))) + (irony-iotask-result-set-value result 'blah) + (should (eq (irony-iotask-result-get result) 'blah)))) + +(irony--define-error 'irony-iotask-result/test-error + "Irony I/O task sample error") + +(ert-deftest irony-iotask-result/set-error () + (let ((result (irony-iotask-result-create))) + (irony-iotask-result-set-error result 'irony-iotask-result/test-error) + (should-error (irony-iotask-result-get result) + :type 'irony-iotask-result/test-error))) + +(ert-deftest irony-iotask-result/set-error-data () + (let ((result (irony-iotask-result-create))) + (irony-iotask-result-set-error result + 'irony-iotask-result/test-error + 'foo 'bar 'baz 'qux) + (condition-case err + (irony-iotask-result-get result) + (irony-iotask-result/test-error + (should (equal (cdr err) '(foo bar baz qux))))))) + +(ert-deftest irony-iotask-result/get-empty () + (let ((result (irony-iotask-result-create))) + (should-error (irony-iotask-result-get result) + :type 'irony-iotask-result-get-error))) + +;; task + +(irony-iotask-define-task irony-iotask/task-start-t + "doc" + :start (lambda (&optional value) + (irony-iotask-set-result (or value 42)))) + +(ert-deftest irony-iotask/task-start/simple () + (let ((task (irony-iotask-package-task irony-iotask/task-start-t))) + (irony-iotask/with-echo-process + (should (equal 42 (irony-iotask-run process task)))))) + +(ert-deftest irony-iotask/task-start/with-arguments () + (let ((task (irony-iotask-package-task irony-iotask/task-start-t 43))) + (irony-iotask/with-echo-process + (should (equal 43 (irony-iotask-run process task)))))) + +(irony-iotask-define-task irony-iotask/task-update-t + "doc" + :start (lambda (&optional hello) + (irony-iotask-send-string (format "%s\n" (or hello "hello")))) + :update (lambda (&optional hello) + (setq hello (or hello "hello")) + (when (string= (buffer-string) (format "%s\n" hello)) + (irony-iotask-set-result (format "%s ok" hello))))) + +(ert-deftest irony-iotask-schedule/task-update/simple () + (let ((task (irony-iotask-package-task irony-iotask/task-update-t))) + (irony-iotask/with-echo-process + (should (string= "hello ok" (irony-iotask-run process task)))))) + +(ert-deftest irony-iotask-schedule/task-update/with-arguments () + (let ((task (irony-iotask-package-task irony-iotask/task-update-t "bonjour"))) + (irony-iotask/with-echo-process + (should (string= "bonjour ok" (irony-iotask-run process task)))))) + +(irony-iotask-define-task irony-iotask/task-invalid-msg-t + "doc" + :start (lambda () + (irony-iotask-send-string "ping\n")) + :update (lambda () + (when (string= (buffer-string) "ping\n") + (throw 'invalid-msg t)))) + +(ert-deftest irony-iotask-schedule/task-update/invalid-msg () + (let ((task (irony-iotask-package-task irony-iotask/task-invalid-msg-t))) + (irony-iotask/with-echo-process + (should-error (irony-iotask-run process task) + :type 'irony-iotask-bad-data)))) + +(ert-deftest irony-iotask-chain/simple () + (let ((task (irony-iotask-chain + (irony-iotask-package-task irony-iotask/task-update-t "hi") + (irony-iotask-package-task irony-iotask/task-update-t "hej")))) + (irony-iotask/with-echo-process + (should (equal "hej ok" (irony-iotask-run process task)))))) + +(defvar irony-iotask/task-finish-var nil) +(defvar irony-iotask/task-on-var nil) +(irony-iotask-define-task irony-iotask/task-finish-t + "doc" + :start (lambda () + (irony-iotask-put :text "how") + (irony-iotask-send-string "hello\n")) + :update (lambda () + (cond + ((string= (buffer-string) "hello\n") + (irony-iotask-put :text (concat (irony-iotask-get :text) " are")) + (irony-iotask-set-result t)) + ((>= (buffer-size) (1+ (length "hello\n"))) + (throw 'invalid-msg t)))) + :on-success (lambda () + (setq irony-iotask/task-on-var "success")) + :finish (lambda () + (setq irony-iotask/task-finish-var (concat (irony-iotask-get :text) + " you?")))) + +(ert-deftest irony-iotask-schedule/task-finish/simple () + (let ((task (irony-iotask-package-task irony-iotask/task-finish-t))) + (irony-iotask/with-echo-process + (setq irony-iotask/task-finish-var nil) + (irony-iotask-run process task) + (should (equal "how are you?" irony-iotask/task-finish-var))))) + +(ert-deftest irony-iotask-schedule/task-on-success/simple () + (let ((task (irony-iotask-package-task irony-iotask/task-finish-t))) + (irony-iotask/with-echo-process + (setq irony-iotask/task-on-var nil) + (irony-iotask-run process task) + (should (equal "success" irony-iotask/task-on-var))))) + +(irony-iotask-define-task irony-iotask/task-on-error-t + "doc" + :start (lambda () + (irony-iotask-set-error 'irony-iotask-error)) + :on-error (lambda () + (setq irony-iotask/task-on-var "error"))) + +(ert-deftest irony-iotask-schedule/task-on-error/simple () + (let ((task (irony-iotask-package-task irony-iotask/task-on-error-t))) + (irony-iotask/with-echo-process + (setq irony-iotask/task-on-var nil) + (ignore-errors + (irony-iotask-run process task)) + (should (equal "error" irony-iotask/task-on-var))))) + +(ert-deftest irony-iotask-schedule/callback/recalls-schedule () + (let ((task (irony-iotask-package-task irony-iotask/task-update-t "a"))) + (irony-iotask/with-echo-process + (lexical-let ((run-process process) + results) + (irony-iotask-schedule process task + (lambda (result) + (setq results (list result)) + (irony-iotask-schedule + run-process + (irony-iotask-package-task + irony-iotask/task-update-t "b") + (lambda (result) + (setq results (append results (list result))))))) + (should (with-local-quit + (while (< (length results) 2) + (accept-process-output process 0.05)) + t)) + (should (string= "a ok" (irony-iotask-result-get (nth 0 results)))) + (should (string= "b ok" (irony-iotask-result-get (nth 1 results)))))))) diff --git a/elpa/irony-20220110.849/server/test/elisp/irony.el b/elpa/irony-20220110.849/server/test/elisp/irony.el new file mode 100644 index 0000000..fe2378b --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/irony.el @@ -0,0 +1,121 @@ +;; -*-no-byte-compile: t; -*- +(load (concat (file-name-directory (or load-file-name + buffer-file-name)) + "test-config")) + +(ert-deftest irony/buffer-size-in-bytes () + (with-temp-buffer + ;; this smiley takes 3 bytes apparently + (insert "☺") + (should (equal 3 (irony--buffer-size-in-bytes))) + (erase-buffer) + (insert "☺\n") + (should (equal 4 (irony--buffer-size-in-bytes))) + (erase-buffer) + (insert "\t") + (should (equal 1 (irony--buffer-size-in-bytes))))) + +(ert-deftest irony/find-server-executable/does-not-exists () + (let ((irony-server-install-prefix "/does/not/exists") + (exec-path nil)) + (should-error (irony--find-server-executable) + :type 'irony-server-error))) + +(ert-deftest irony/split-command-line/just-spaces () + (let ((cmd-line "clang -Wall -Wextra")) + (should (equal + '("clang" "-Wall" "-Wextra") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/start-with-space () + (let ((cmd-line " clang -Wall -Wextra")) + (should (equal + '("clang" "-Wall" "-Wextra") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/end-with-space () + (let ((cmd-line "clang -Wall -Wextra ")) + (should (equal + '("clang" "-Wall" "-Wextra") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/space-everywhere () + (let ((cmd-line " \t clang \t -Wall \t -Wextra\t")) + (should (equal + '("clang" "-Wall" "-Wextra") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/with-quotes () + (let ((cmd-line "clang -Wall -Wextra \"-I/tmp/dir with spaces\"")) + (should (equal + '("clang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/with-quotes () + "Test if files are removed from the arguments list. + +https://github.com/Sarcasm/irony-mode/issues/101" + (let ((cmd-line "g++ -DFOO=\\\"\\\"")) + (should (equal + '("g++" "-DFOO=\"\"") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/start-with-quotes () + (let ((cmd-line "\"cl ang\" -Wall -Wextra \"-I/tmp/dir with spaces\"")) + (should (equal + '("cl ang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/quotes-in-word () + (let ((cmd-line "clang -Wall -Wextra -I\"/tmp/dir with spaces\"")) + (should (equal + '("clang" "-Wall" "-Wextra" "-I/tmp/dir with spaces") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/ill-end-quote () + (let ((cmd-line "clang -Wall -Wextra\"")) + (should-error (irony--split-command-line cmd-line) + :type 'irony-parse-error))) + +(ert-deftest irony/split-command-line/backslash-1 () + (let ((cmd-line "clang\\ -Wall")) + (should (equal + '("clang -Wall") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/split-command-line/backslash-2 () + (let ((cmd-line "\\\\\\ clang\\ -Wall\\")) + (should (equal + '("\\ clang -Wall\\") + (irony--split-command-line cmd-line))))) + +(ert-deftest irony/extract-working-directory-option/not-specified () + (let ((compile-flags '("-Wall"))) + (should + (not (irony--extract-working-directory-option compile-flags))))) + +(ert-deftest irony/extract-working-directory-option/specified-1 () + (let ((compile-flags '("-working-directory" "/tmp/lol"))) + (should (equal "/tmp/lol" + (irony--extract-working-directory-option compile-flags))))) + +(ert-deftest irony/extract-working-directory-option/specified-2 () + (let ((compile-flags '("-Wall" "-working-directory=/tmp/lol" "-Wshadow"))) + (should (equal "/tmp/lol" + (irony--extract-working-directory-option compile-flags))))) + +;; TODO: restore functionality +;; (ert-deftest irony/include-directories-1 () +;; (let ((irony-compile-flags '("-Iinclude" "-I/tmp/foo")) +;; (irony-compile-flags-work-dir "/tmp/blah/")) +;; (should (equal +;; '("/tmp/blah/include" "/tmp/foo") +;; (irony-user-search-paths))))) + +;; (ert-deftest irony/include-directories-2 () +;; (let ((irony-compile-flags '("-Wextra" "-Iinclude" "-I" "foo" "-Wall")) +;; (irony-compile-flags-work-dir "/tmp/blah/")) +;; (should (equal +;; '("/tmp/blah/include" +;; "/tmp/blah/foo") +;; (irony-user-search-paths))))) diff --git a/elpa/irony-20220110.849/server/test/elisp/test-config.el b/elpa/irony-20220110.849/server/test/elisp/test-config.el new file mode 100644 index 0000000..224cdd0 --- /dev/null +++ b/elpa/irony-20220110.849/server/test/elisp/test-config.el @@ -0,0 +1,14 @@ +;; -*-no-byte-compile: t; -*- +(defvar test-dir (if load-file-name + (file-name-as-directory + (expand-file-name (concat (file-name-directory + load-file-name))))) + "Elisp test directory path.") + +;; load irony +(unless (require 'irony nil t) + (let ((irony-dir (expand-file-name "../../.." test-dir))) + (add-to-list 'load-path irony-dir) + (require 'irony))) + +(require 'ert) -- cgit v1.2.1