[clang-tools-extra] r283306 - Overhaul clang-include-fixer.el
Haojian Wu via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 5 03:04:13 PDT 2016
Author: hokein
Date: Wed Oct 5 05:04:13 2016
New Revision: 283306
URL: http://llvm.org/viewvc/llvm-project?rev=283306&view=rev
Log:
Overhaul clang-include-fixer.el
General overhaul to fix many coding bugs, simplify the code, and improve readability.
* Clarify documentation strings of user options.
* Say that clang-include-fixer-executable is a file to have auto completion.
* Allow user to select available options for clang-include-fixer-input-format. Turn it into a symbol as it's not a free-form string.
* Remove clang-include-fixer-query-mode. This option was apparently used to select between two different operation modes, which is not a typical use case for user options. Provide two separate commands instead.
* Add a face for the overlay highlighting so that users can customize it.
Move user commands to the front so that readers of the code aren't buried in internal functions.
* Make process calls asynchronous. This is possible here because clang-include-fixer doesn't change files in place. This means input is no longer blocked while clang-include-fixer is running.
* Factor out logic in helper functions to keep functions short.
* Add comments where appropriate.
* Provide an alternative buffer replacement strategy for the case that a single line was inserted (the normal case in the case of clang-include-fixer). This keeps point, markers, and other buffer information intact.
* Use let-alist and association lists instead of property lists to shorten the code.
* Instead of highlighting only the first occurrence of a symbol, highlight all occurrences and move point to the closest one.
* Detect qualified names at point.
* Use filepos-to-bufferpos if available.
* Formatting.
Patch by Philipp Stephani!
Added:
clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer-test.el
Modified:
clang-tools-extra/trunk/docs/include-fixer.rst
clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer.el
Modified: clang-tools-extra/trunk/docs/include-fixer.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/include-fixer.rst?rev=283306&r1=283305&r2=283306&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/include-fixer.rst (original)
+++ clang-tools-extra/trunk/docs/include-fixer.rst Wed Oct 5 05:04:13 2016
@@ -121,26 +121,19 @@ in your ``.emacs``:
(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``.
+Within Emacs the tool can be invoked with the command
+``M-x clang-include-fixer``. This will insert the header that defines the
+first undefined symbol; if there is more than one header that would define the
+symbol, the user is prompted to select one.
-Make sure Emacs can find :program:`clang-include-fixer`:
-
-- Add the path to :program:`clang-include-fixer` to the PATH environment variable.
-
-Customized settings in `.emacs`:
-
-- ``(custom-set-variables '(clang-include-fixer-executable "/path/to/include-fixer"))``
+To include the header that defines the symbol at point, run
+``M-x clang-include-fixer-at-point``.
- Set clang-include-fixer binary file path.
-
-- ``(custom-set-variables '(clang-include-fixer-query-mode t))``
-
- Set to `t` if you want to insert ``#include`` for the symbol under the cursor.
- Default is `nil`. Compared to normal mode, this mode won't parse the source
- file and only search the sysmbol from database, which is faster than normal
- mode.
+Make sure Emacs can find :program:`clang-include-fixer`:
-See ``clang-include-fixer.el`` for more details.
+- Either add the parent directory of :program:`clang-include-fixer` to the PATH
+ environment variable, or customize the Emacs user option
+ ``clang-include-fixer-executable`` to point to the file name of the program.
How it Works
============
Added: clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer-test.el
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer-test.el?rev=283306&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer-test.el (added)
+++ clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer-test.el Wed Oct 5 05:04:13 2016
@@ -0,0 +1,40 @@
+;;; clang-include-fixer-test.el --- unit tests for clang-include-fixer.el -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Unit tests for clang-include-fixer.el.
+
+;;; Code:
+
+(require 'cc-mode)
+(require 'ert)
+
+(ert-deftest clang-include-fixer--insert-line ()
+ "Unit test for `clang-include-fixer--insert-line'."
+ (with-temp-buffer
+ (insert "aa\nab\nac\nad\n")
+ (let ((from (current-buffer)))
+ (with-temp-buffer
+ (insert "aa\nac\nad\n")
+ (let ((to (current-buffer)))
+ (should (clang-include-fixer--insert-line from to))
+ (should (equal (buffer-string) "aa\nab\nac\nad\n")))))
+ (should (equal (buffer-string) "aa\nab\nac\nad\n"))))
+
+(ert-deftest clang-include-fixer--symbol-at-point ()
+ "Unit test for `clang-include-fixer--symbol-at-point'."
+ (with-temp-buffer
+ (insert "a+bbb::cc")
+ (c++-mode)
+ (goto-char (point-min))
+ (should (equal (clang-include-fixer--symbol-at-point) "a"))
+ (forward-char)
+ ;; Emacs treats the character immediately following a symbol as part of the
+ ;; symbol.
+ (should (equal (clang-include-fixer--symbol-at-point) "a"))
+ (forward-char)
+ (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))
+ (goto-char (point-max))
+ (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))))
+
+;;; clang-include-fixer-test.el ends here
Modified: clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer.el
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer.el?rev=283306&r1=283305&r2=283306&view=diff
==============================================================================
--- clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer.el (original)
+++ clang-tools-extra/trunk/include-fixer/tool/clang-include-fixer.el Wed Oct 5 05:04:13 2016
@@ -1,7 +1,7 @@
-;;; clang-include-fxier.el --- Emacs integration of the clang include fixer
+;;; clang-include-fixer.el --- Emacs integration of the clang include fixer -*- lexical-binding: t; -*-
;; Keywords: tools, c
-;; Package-Requires: ((json "1.2"))
+;; Package-Requires: ((cl-lib "0.5") (json "1.2") (let-alist "1.0.4"))
;;; Commentary:
@@ -12,219 +12,395 @@
;;; Code:
+(require 'cl-lib)
(require 'json)
+(require 'let-alist)
(defgroup clang-include-fixer nil
- "Include fixer."
+ "Clang-based include fixer."
:group 'tools)
(defcustom clang-include-fixer-executable
"clang-include-fixer"
- "Location of the `clang-include-fixer' executable.
+ "Location of the clang-include-fixer executable.
- A string containing the name or the full path of the executable."
+A string containing the name or the full path of the executable."
:group 'clang-include-fixer
- :type 'string
+ :type '(file :must-match t)
:risky t)
(defcustom clang-include-fixer-input-format
- "yaml"
- "clang-include-fixer input format."
+ 'yaml
+ "Input format for clang-include-fixer.
+This string is passed as -db argument to
+`clang-include-fixer-executable'."
:group 'clang-include-fixer
- :type 'string
+ :type '(radio
+ (const :tag "Hard-coded mapping" :fixed)
+ (const :tag "YAML" yaml)
+ (symbol :tag "Other"))
:risky t)
(defcustom clang-include-fixer-init-string
""
- "clang-include-fixer init string."
+ "Database initialization string for clang-include-fixer.
+This string is passed as -input argument to
+`clang-include-fixer-executable'."
:group 'clang-include-fixer
:type 'string
:risky t)
-(defcustom clang-include-fixer-query-mode
- nil
- "clang-include-fixer query mode."
- :group 'clang-include-fixer
- :type 'boolean
- :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 query-symbol-infos (plist-get include-fixer-context :QuerySymbolInfos))
-
- (if (eq 0 (length query-symbol-infos))
- (message "The file is fine, no need to add a header.")
-
- (setq symbol-info (elt query-symbol-infos 0))
- (setq symbol (plist-get symbol-info :RawIdentifier))
- (setq symbol-offset (plist-get (plist-get symbol-info :Range)
- :Offset))
-
- ;; Check the number of choices
- (if (eq 0 (length header-infos))
- (progn
- (goto-char (1+ symbol-offset))
- (message (concat "Couldn't find header for '" symbol "'.")))
-
- (setq symbol-length (plist-get (plist-get symbol-info :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))))
-
+(defface clang-include-fixer-highlight '((t :background "green"))
+ "Used for highlighting the symbol for which a header file is being added.")
(defun clang-include-fixer ()
- "Invokes the Include Fixer to insert missing C++ headers."
+ "Invoke the Include Fixer to insert missing C++ headers."
(interactive)
-
(message (concat "Calling the include fixer. "
- "This might take some seconds. Please wait."))
+ "This might take some seconds. Please wait."))
+ (clang-include-fixer--start #'clang-include-fixer--add-header
+ "-output-headers"))
- (if clang-include-fixer-query-mode
- (let (p1 p2)
- (save-excursion
- (skip-chars-backward "-a-zA-Z0-9_:")
- (setq p1 (point))
- (skip-chars-forward "-a-zA-Z0-9_:")
- (setq p2 (point))
- (setq query-symbol (buffer-substring-no-properties p1 p2))
- (if (string= "" query-symbol)
- (message "Skip querying empty symbol.")
- (clang-include-fixer-call-executable
- 'clang-include-fixer-add-header
- (concat "-db=" clang-include-fixer-input-format)
- (concat "-input=" clang-include-fixer-init-string)
- (concat "-query-symbol=" (thing-at-point 'symbol))
- ))))
- (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"))
- )
+(defun clang-include-fixer-at-point ()
+ "Invoke the Clang include fixer for the symbol at point."
+ (interactive)
+ (let ((symbol (clang-include-fixer--symbol-at-point)))
+ (unless symbol
+ (user-error "No symbol at current location"))
+ (clang-include-fixer--start #'clang-include-fixer--add-header
+ (format "-query-symbol=%s" symbol))))
+
+(defun clang-include-fixer--start (callback &rest args)
+ "Asynchronously start clang-include-fixer with parameters ARGS.
+The current file name is passed after ARGS as last argument. If
+the call was successful the returned result is stored in a
+temporary buffer, and CALLBACK is called with the temporary
+buffer as only argument."
+ (let ((process (if (fboundp 'make-process)
+ ;; Prefer using âmake-processâ if available, because
+ ;; âstart-processâ doesnât allow us to separate the
+ ;; standard error from the output.
+ (clang-include-fixer--make-process callback args)
+ (clang-include-fixer--start-process callback args))))
+ (save-restriction
+ (widen)
+ (process-send-region process (point-min) (point-max)))
+ (process-send-eof process))
+ nil)
+
+(defun clang-include-fixer--make-process (callback args)
+ "Start a new clang-incude-fixer process using `make-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted. ARGS is a list of additional command line
+arguments. Return the new process object."
+ (let* ((stdin (current-buffer))
+ (stdout (generate-new-buffer "*clang-include-fixer output*"))
+ (stderr (generate-new-buffer "*clang-include-fixer errors*")))
+ (make-process :name "clang-include-fixer"
+ :buffer stdout
+ :command (clang-include-fixer--command args)
+ :coding 'utf-8-unix
+ :connection-type 'pipe
+ :sentinel (clang-include-fixer--sentinel stdin stdout stderr
+ callback)
+ :stderr stderr)))
+
+(defun clang-include-fixer--start-process (callback args)
+ "Start a new clang-incude-fixer process using `start-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted. ARGS is a list of additional command line
+arguments. Return the new process object."
+ (let* ((stdin (current-buffer))
+ (stdout (generate-new-buffer "*clang-include-fixer output*"))
+ (process-connection-type nil)
+ (process (apply #'start-process "clang-include-fixer" stdout
+ (clang-include-fixer--command args))))
+ (set-process-coding-system process 'utf-8-unix 'utf-8-unix)
+ (set-process-sentinel process
+ (clang-include-fixer--sentinel stdin stdout nil
+ callback))
+ process))
+
+(defun clang-include-fixer--command (args)
+ "Return the clang-include-fixer command line.
+Returns a list; the first element is the binary to
+execute (`clang-include-fixer-executable'), and the remaining
+elements are the command line arguments. Adds proper arguments
+for `clang-include-fixer-input-format' and
+`clang-include-fixer-init-string'. Appends the current buffer's
+file name; prepends ARGS directly in front of it."
+ (cl-check-type args list)
+ `(,clang-include-fixer-executable
+ ,(format "-db=%s" clang-include-fixer-input-format)
+ ,(format "-input=%s" clang-include-fixer-init-string)
+ "-stdin"
+ , at args
+ ,(buffer-file-name)))
+
+(defun clang-include-fixer--sentinel (stdin stdout stderr callback)
+ "Return a process sentinel for clang-include-fixer processes.
+STDIN, STDOUT, and STDERR are buffers for the standard streams;
+only STDERR may be nil. CALLBACK is called in the case of
+success; it is called with a single argument, STDOUT. On
+failure, a buffer containing the error output is displayed."
+ (cl-check-type stdin buffer-live)
+ (cl-check-type stdout buffer-live)
+ (cl-check-type stderr (or null buffer-live))
+ (cl-check-type callback function)
+ (lambda (process event)
+ (cl-check-type process process)
+ (cl-check-type event string)
+ (unwind-protect
+ (if (string-equal event "finished\n")
+ (progn
+ (when stderr (kill-buffer stderr))
+ (with-current-buffer stdin
+ (funcall callback stdout))
+ (kill-buffer stdout))
+ (when stderr (kill-buffer stdout))
+ (message "clang-include-fixer failed")
+ (with-current-buffer (or stderr stdout)
+ (insert "\nProcess " (process-name process)
+ ?\s event))
+ (display-buffer (or stderr stdout))))
+ nil))
+
+(defun clang-include-fixer--replace-buffer (stdout)
+ "Replace current buffer by content of STDOUT."
+ (cl-check-type stdout buffer-live)
+ (barf-if-buffer-read-only)
+ (unless (clang-include-fixer--insert-line stdout (current-buffer))
+ (erase-buffer)
+ (insert-buffer-substring stdout))
+ (message "Fix applied")
+ nil)
+
+(defun clang-include-fixer--insert-line (from to)
+ "Insert a single missing line from the buffer FROM into TO.
+FROM and TO must be buffers. If the contents of FROM and TO are
+equal, do nothing and return non-nil. If FROM contains a single
+line missing from TO, insert that line into TO so that the buffer
+contents are equal and return non-nil. Otherwise, do nothing and
+return nil. Buffer restrictions are ignored."
+ (cl-check-type from buffer-live)
+ (cl-check-type to buffer-live)
+ (with-current-buffer from
+ (save-excursion
+ (save-restriction
+ (widen)
+ (with-current-buffer to
+ (save-excursion
+ (save-restriction
+ (widen)
+ ;; Search for the first buffer difference.
+ (let ((chars (abs (compare-buffer-substrings to nil nil from nil nil))))
+ (if (zerop chars)
+ ;; Buffer contents are equal, nothing to do.
+ t
+ (goto-char (point-min))
+ (forward-char chars)
+ ;; We might have ended up in the middle of a line if the
+ ;; current line partially matches. In this case we would
+ ;; have to insert more than a line. Move to the beginning of
+ ;; the line to avoid this situation.
+ (beginning-of-line)
+ (with-current-buffer from
+ (goto-char (point-min))
+ (forward-char chars)
+ (beginning-of-line)
+ (let ((from-begin (point))
+ (from-end (progn (forward-line) (point)))
+ (to-point (with-current-buffer to (point))))
+ ;; Search for another buffer difference after the line in
+ ;; question. If there is none, we can proceed.
+ (when (zerop (compare-buffer-substrings from from-end nil
+ to to-point nil))
+ (with-current-buffer to
+ (insert-buffer-substring from from-begin from-end))
+ t))))))))))))
+
+(defun clang-include-fixer--add-header (stdout)
+ "Analyse the result of include-fixer stored in STDOUT.
+Add a missing header if there is any. If there are multiple
+possible headers the user can select one of them to be included.
+Temporarily highlight the affected symbols. Asynchronously call
+clang-include-fixer to insert the selected header."
+ (cl-check-type stdout buffer-live)
+ (let ((context (clang-include-fixer--parse-json stdout)))
+ (let-alist context
+ (cond
+ ((null .QuerySymbolInfos)
+ (message "The file is fine, no need to add a header."))
+ ((null .HeaderInfos)
+ (message "Couldn't find header for '%s'"
+ (let-alist (car .QuerySymbolInfos) .RawIdentifier)))
+ (t
+ ;; Replace the HeaderInfos list by a single header selected by
+ ;; the user.
+ (clang-include-fixer--select-header context)
+ ;; Call clang-include-fixer again to insert the selected header.
+ (clang-include-fixer--start
+ #'clang-include-fixer--replace-buffer
+ (format "-insert-header=%s"
+ (clang-include-fixer--encode-json context)))))))
+ nil)
+
+(defun clang-include-fixer--select-header (context)
+ "Prompt the user for a header if necessary.
+CONTEXT must be a clang-include-fixer context object in
+association list format. If it contains more than one HeaderInfo
+element, prompt the user to select one of the headers. CONTEXT
+is modified to include only the selected element."
+ (cl-check-type context cons)
+ (let-alist context
+ (if (cdr .HeaderInfos)
+ (clang-include-fixer--prompt-for-header context)
+ (message "Only one include is missing: %s"
+ (let-alist (car .HeaderInfos) .Header))))
+ nil)
+
+(defvar clang-include-fixer--history nil
+ "History for `clang-include-fixer--prompt-for-header'.")
+
+(defun clang-include-fixer--prompt-for-header (context)
+ "Prompt the user for a single header.
+The choices are taken from the HeaderInfo elements in CONTEXT.
+They are replaced by the single element selected by the user."
+ (let-alist context
+ (let ((symbol (clang-include-fixer--symbol-name .QuerySymbolInfos))
+ ;; Add temporary highlighting so that the user knows which
+ ;; symbols the current session is about.
+ (overlays (mapcar #'clang-include-fixer--highlight .QuerySymbolInfos)))
+ (unwind-protect
+ (save-excursion
+ ;; While prompting, go to the closest overlay so that the user sees
+ ;; some context.
+ (goto-char (clang-include-fixer--closest-overlay overlays))
+ (cl-flet ((header (info) (let-alist info .Header)))
+ ;; The header-infos is already sorted by include-fixer.
+ (let* ((header (ido-completing-read
+ (format-message "Select include for '%s': "
+ symbol)
+ (mapcar #'header .HeaderInfos)
+ nil :require-match nil
+ 'clang-include-fixer--history))
+ (info (cl-find header .HeaderInfos :key #'header)))
+ (cl-assert info)
+ (setcar .HeaderInfos info)
+ (setcdr .HeaderInfos nil))))
+ (mapc #'delete-overlay overlays)))))
+
+(defun clang-include-fixer--symbol-name (symbol-infos)
+ "Return the unique symbol name in SYMBOL-INFOS.
+Raise a signal if the symbol name is not unique."
+ (let ((symbols (delete-dups (mapcar (lambda (info)
+ (let-alist info .RawIdentifier))
+ symbol-infos))))
+ (when (cdr symbols)
+ (error "Multiple symbols %s returned" symbols))
+ (car symbols)))
+
+(defun clang-include-fixer--highlight (symbol-info)
+ "Add an overlay to highlight SYMBOL-INFO.
+Return the overlay object."
+ (let ((overlay (let-alist symbol-info
+ (make-overlay
+ (clang-include-fixer--filepos-to-bufferpos
+ .Range.Offset 'approximate)
+ (clang-include-fixer--filepos-to-bufferpos
+ (+ .Range.Offset .Range.Length) 'approximate)))))
+ (overlay-put overlay 'face 'clang-include-fixer-highlight)
+ overlay))
+
+(defun clang-include-fixer--closest-overlay (overlays)
+ "Return the start of the overlay in OVERLAYS that is closest to point."
+ (cl-check-type overlays cons)
+ (let ((point (point))
+ acc)
+ (dolist (overlay overlays acc)
+ (let ((start (overlay-start overlay)))
+ (when (or (null acc) (< (abs (- point start)) (abs (- point acc))))
+ (setq acc start))))))
+
+(defun clang-include-fixer--parse-json (buffer)
+ "Parse a JSON response from clang-include-fixer in BUFFER.
+Return the JSON object as an association list."
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char (point-min))
+ (let ((json-object-type 'alist)
+ (json-array-type 'list)
+ (json-key-type 'symbol)
+ (json-false :json-false)
+ (json-null nil)
+ (json-pre-element-read-function nil)
+ (json-post-element-read-function nil))
+ (json-read)))))
+
+(defun clang-include-fixer--encode-json (object)
+ "Return the JSON representation of OBJECT as a string."
+ (let ((json-encoding-separator ",")
+ (json-encoding-default-indentation " ")
+ (json-encoding-pretty-print nil)
+ (json-encoding-lisp-style-closings nil)
+ (json-encoding-object-sort-predicate nil))
+ (json-encode object)))
+
+(defun clang-include-fixer--symbol-at-point ()
+ "Return the qualified symbol at point.
+If there is no symbol at point, return nil."
+ ;; Let âbounds-of-thing-at-pointâ to do the hard work and deal with edge
+ ;; cases.
+ (let ((bounds (bounds-of-thing-at-point 'symbol)))
+ (when bounds
+ (let ((beg (car bounds))
+ (end (cdr bounds)))
+ (save-excursion
+ ;; Extend the symbol range to the left. Skip over namespace
+ ;; delimiters and parent namespace names.
+ (goto-char beg)
+ (while (and (clang-include-fixer--skip-double-colon-backward)
+ (skip-syntax-backward "w_")))
+ ;; Skip over one more namespace delimiter, for absolute names.
+ (clang-include-fixer--skip-double-colon-backward)
+ (setq beg (point))
+ ;; Extend the symbol range to the right. Skip over namespace
+ ;; delimiters and child namespace names.
+ (goto-char end)
+ (while (and (clang-include-fixer--skip-double-colon-forward)
+ (skip-syntax-forward "w_")))
+ (setq end (point)))
+ (buffer-substring-no-properties beg end)))))
+
+(defun clang-include-fixer--skip-double-colon-forward ()
+ "Skip a double colon.
+When the next two characters are '::', skip them and return
+non-nil. Otherwise return nil."
+ (let ((end (+ (point) 2)))
+ (when (and (<= end (point-max))
+ (string-equal (buffer-substring-no-properties (point) end) "::"))
+ (goto-char end)
+ t)))
+
+(defun clang-include-fixer--skip-double-colon-backward ()
+ "Skip a double colon.
+When the previous two characters are '::', skip them and return
+non-nil. Otherwise return nil."
+ (let ((beg (- (point) 2)))
+ (when (and (>= beg (point-min))
+ (string-equal (buffer-substring-no-properties beg (point)) "::"))
+ (goto-char beg)
+ t)))
+
+;; âfilepos-to-bufferposâ is new in Emacs 25.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--filepos-to-bufferpos
+ (if (fboundp 'filepos-to-bufferpos)
+ 'filepos-to-bufferpos
+ (lambda (byte &optional _quality _coding-system)
+ (byte-to-position (1+ byte)))))
(provide 'clang-include-fixer)
;;; clang-include-fixer.el ends here
More information about the cfe-commits
mailing list