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" + ,@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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits