Index: docs/include-fixer.rst =================================================================== --- docs/include-fixer.rst +++ docs/include-fixer.rst @@ -75,6 +75,25 @@ See ``clang-include-fixer.py`` for more details. +Integrate with Emacs +------------------- +To run `clang-include-fixer` on a potentially unsaved buffer in Emacs. +Ensure that Emacs finds ``clang-include-fixer.el`` by adding the diretory containing the file to the ``load-path`` +and requiring the `clang-include-fixer` in your ```.emacs``: + +.. code-block:: console + + (add-to-list 'load-path "path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/" + (require 'clang-include-fixer) + +Within Emacs the tool can be invoked with the command ``M-x clang-include-fixer``. + +Make sure Emacs can find :program:`clang-include-fixer`: + +- Add the path to :program:`clang-include-fixer` to the PATH environment variable. + +See ``clang-include-fixer.el`` for more details. + How it Works ============ Index: include-fixer/tool/clang-include-fixer.el =================================================================== --- /dev/null +++ include-fixer/tool/clang-include-fixer.el @@ -0,0 +1,203 @@ +;;; clang-include-fxier.el --- Emacs integration of the clang include fixer + +;; Keywords: tools, c +;; Package-Requires: ((json "1.2")) + +;;; Commentary: + +;; This package allows to invoke the 'clang-include-fixer' within Emacs. +;; 'clang-include-fixer' provides an automated way of adding #include +;; directives for missing symbols in one translation unit, see +;; . + +;;; Code: + +(require 'json) + +(defgroup clang-include-fixer nil + "Include fixer." + :group 'tools) + +(defcustom clang-include-fixer-executable + "clang-include-fixer" + "Location of the `clang-include-fixer' executable. + +A string containing the name or the full path of the executable." + :group 'clang-include-fixer + :type 'string + :risky t) + +(defcustom clang-include-fixer-input-format + "yaml" + "clang-include-fixer input format." + :group 'clang-include-fixer + :type 'string + :risky t) + +(defcustom clang-include-fixer-init-string + "" + "clang-include-fixer input format." + :group 'clang-include-fixer + :type 'string + :risky t) + + +(defun clang-include-fixer-call-executable (callee + include-fixer-parameter-a + &optional include-fixer-parameter-b + &optional include-fixer-parameter-c + ) + "Calls clang-include-fixer with parameters INCLUDE-FIXER-PARAMETER-[ABC]. +If the call was successful the returned result is stored in a temp buffer +and the function CALLEE is called on this temp buffer." + + (let ((temp-buffer (generate-new-buffer " *clang-include-fixer-temp*")) + (temp-file (make-temp-file "clang-include-fixer"))) + (unwind-protect + (let (status stderr operations) + (if (eq include-fixer-parameter-c nil) + (setq status + (call-process-region + (point-min) (point-max) clang-include-fixer-executable + nil `(,temp-buffer ,temp-file) nil + + "-stdin" + include-fixer-parameter-a + (buffer-file-name) + )) + (setq status + (call-process-region + (point-min) (point-max) clang-include-fixer-executable + nil `(,temp-buffer ,temp-file) nil + + "-stdin" + include-fixer-parameter-a + include-fixer-parameter-b + include-fixer-parameter-c + (buffer-file-name) + ))) + + (setq stderr + (with-temp-buffer + (insert-file-contents temp-file) + (when (> (point-max) (point-min)) + (insert ": ")) + (buffer-substring-no-properties + (point-min) (line-end-position)))) + + (cond + ((stringp status) + (error "(clang-include-fixer killed by signal %s%s)" status + stderr)) + ((not (equal 0 status)) + (error "(clang-include-fixer failed with code %d%s)" status + stderr))) + (funcall callee temp-buffer)) + (delete-file temp-file) + (when (buffer-name temp-buffer) (kill-buffer temp-buffer))))) + + +(defun clang-include-fixer-replace_buffer (temp-buffer) + "Replace current buffer by content of TEMP-BUFFER" + + (with-current-buffer temp-buffer + (setq temp-start (point-min)) + (setq temp-end (point-max)) + ) + (barf-if-buffer-read-only) + (erase-buffer) + (save-excursion + (insert-buffer-substring temp-buffer temp-start temp-end))) + + +(defun clang-include-fixer-add-header (temp-buffer) + "Analyse the result of include-fixer stored in TEMP_BUFFER and add a + missing header if there is any. If there are multiple possible headers + the user can select one of them to be included." + + (with-current-buffer temp-buffer + (setq result (buffer-substring (point-min) (point-max))) + (setq include-fixer-context + (let ((json-object-type 'plist)) + (json-read-from-string result)))) + + ;; The header-infos is already sorted by include-fixer. + (setq header-infos (plist-get include-fixer-context :HeaderInfos)) + (setq symbol (plist-get include-fixer-context :SymbolIdentifier)) + (setq symbol-offset (plist-get (plist-get include-fixer-context :Range) + :Offset)) + + ;; Check the number of choices + (if (eq 0 (length header-infos)) + (progn + (if (eq "" symbol) + (message "No include is missing.") + (goto-char (1+ symbol-offset)) + (message (concat "No header found for symbol '" symbol "'.")))) + + (setq symbol-length (plist-get (plist-get include-fixer-context :Range) + :Length)) + (goto-char (1+ symbol-offset)) + (setq symbol-overlay (make-overlay (1+ symbol-offset) + (+ symbol-offset symbol-length +1))) + (overlay-put symbol-overlay 'face '(:background "green" :foreground + "black")) + + (message (number-to-string symbol-offset)) + (message (number-to-string symbol-length)) + + (if (eq 1 (length header-infos)) + (progn + (setq missing-header + (plist-get (elt header-infos 0) :Header)) + (message (concat "Only one include is missing: " + missing-header ))) + + ;; Now iterate over vector and add items to list + (setq include-list '()) + (setq index 0) + (while (< index (length header-infos)) + (setq entry (elt header-infos index)) + (add-to-list 'include-list (plist-get entry :Header)) + (setq index (1+ index)) + ) + + (setq option-message (concat "Select include for '" + symbol + "' :")) + (setq missing-header (ido-completing-read + option-message include-list))) + + ;; Now select set correct header info. + (setq header-plist '()) + (setq index 0) + (while (< index (length header-infos)) + (setq entry (elt header-infos index)) + (setq index (1+ index)) + (if (eq (plist-get entry :Header) missing-header) + (setq header-plist entry))) + (setq include-fixer-context (plist-put + include-fixer-context + ':HeaderInfos (vector header-plist))) + + (clang-include-fixer-call-executable + 'clang-include-fixer-replace_buffer + (concat "-insert-header=" (json-encode include-fixer-context))) + (delete-overlay symbol-overlay))) + + +(defun clang-include-fixer () + "Invokes the Include Fixer to insert missing C++ headers." + (interactive) + + (message (concat "Calling the include fixer. " + "This might take some seconds. Please wait.")) + + (clang-include-fixer-call-executable + 'clang-include-fixer-add-header + (concat "-db=" clang-include-fixer-input-format) + (concat "-input=" clang-include-fixer-init-string) + "-output-headers")) + +(provide 'clang-include-fixer) +;;; clang-include-fixer.el ends here