├── .gitignore ├── Makefile ├── README.org ├── auto-complete-clang-async.el ├── makefile.mk ├── screenshots ├── class_member.png ├── global_namespace_clang.png ├── global_namespace_macro.png ├── namespace.png ├── showcase.png └── syntax_check.png └── src ├── completion.h ├── completion_serv.c ├── main.c ├── msg_callback.h ├── msg_dispatcher.c ├── msg_handlers.c └── parse_results.c /.gitignore: -------------------------------------------------------------------------------- 1 | #build output 2 | src/dep/ 3 | src/obj/ 4 | clang-complete 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | INCLUDE_PATH := ./src 2 | SOURCE_PATH := ./src 3 | DEPENDENCY_PATH := ./src/dep 4 | OBJECT_PATH := ./src/obj 5 | 6 | PROGRAM_NAME := clang-complete 7 | LLVM_CONFIG := llvm-config 8 | 9 | LDLIBS := $(shell $(LLVM_CONFIG) --ldflags) -lclang 10 | CFLAGS += -std=c99 $(shell $(LLVM_CONFIG) --cflags) -Wall -Wextra -pedantic -O3 11 | 12 | 13 | include makefile.mk 14 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * Introduction 2 | 3 | emacs-clang-complete-async is an emacs extension to complete C and C++ code, 4 | it uses libclang to parse the source code on the fly and provides completion candidates to 5 | auto-complete (http://cx4a.org/software/auto-complete). 6 | 7 | [[https://github.com/Golevka/emacs-clang-complete-async/raw/master/screenshots/showcase.png]] 8 | 9 | This extension is not implemented in pure elisp, it is made up of a client part 10 | (auto-complete-clang-async.el, written in elisp) and a server part 11 | (clang-complete binary, written in C), they work cooperately in asynchonous 12 | client-server fashion. 13 | 14 | * Experimental Feature - On the fly syntax checking 15 | 16 | An experimental feature is added to this branch --- running 17 | =ac-clang-syntax-check= to highlight errornous lines in your souce code. 18 | 19 | [[https://github.com/Golevka/emacs-clang-complete-async/raw/master/screenshots/syntax_check.png]] 20 | 21 | 22 | * Setup 23 | 24 | Compile the server part (clang-complete binary) first, by executing 25 | =make=. The build process uses =llvm-config= to determine the location of 26 | libclang and the appropriate compile flags to use. If =llvm-config= is not in 27 | your path or has a different name, you can pass =make= an =LLVM_CONFIG= 28 | argument, e.g. =make LLVM_CONFIG=llvm-config-3.4=. 29 | 30 | Copy auto-complete-clang-async.el and the previously compiled 31 | clang-complete executable to ~/.emacs.d/, and add the following code to your 32 | .emacs file. 33 | 34 | #+BEGIN_SRC elisp 35 | (require 'auto-complete-clang-async) 36 | 37 | (defun ac-cc-mode-setup () 38 | (setq ac-clang-complete-executable "~/.emacs.d/clang-complete") 39 | (setq ac-sources '(ac-source-clang-async)) 40 | (ac-clang-launch-completion-process) 41 | ) 42 | 43 | (defun my-ac-config () 44 | (add-hook 'c-mode-common-hook 'ac-cc-mode-setup) 45 | (add-hook 'auto-complete-mode-hook 'ac-common-setup) 46 | (global-auto-complete-mode t)) 47 | 48 | (my-ac-config) 49 | #+END_SRC 50 | 51 | Now emacs-clang-complete-async will show completion candidates automatically 52 | when you type as usual in C or C++ mode. 53 | 54 | 55 | * Usage 56 | 57 | This extension fades in emacs C/C++ mode and provides candidates 58 | automatically while you typing code, if you want to add parameters to clang 59 | (libclang, actually), such as -Ipath, just call ac-clang-set-cflags 60 | interactively or set the value of ac-clang-flags in .emacs or .dir-locals.el, 61 | maybe you need an explicit call to ac-clang-update-cmdlineargs to make changes 62 | to cflags take effect, which is a niggling part of it T T 63 | 64 | 65 | * Note 66 | 67 | Most code of auto-complete-clang-async.el is taken from brainjcj's 68 | auto-complete-clang.el, The only difference between this one and bj's 69 | ac-clang-complete is: This one interacts with a process geared with libclang to 70 | retrieve completion candidates instead of calling clang process, which is way 71 | slower, and the asynchonous nature of our C-S based working model won't block 72 | the editor while parsing source code and resolving completion candidates, 73 | which provides a "smooth" coding experience. 74 | -------------------------------------------------------------------------------- /auto-complete-clang-async.el: -------------------------------------------------------------------------------- 1 | ;;; auto-complete-clang-async.el --- Auto Completion source for clang for GNU Emacs 2 | 3 | ;; Copyright (C) 2010 Brian Jiang 4 | ;; Copyright (C) 2012 Taylan Ulrich Bayirli/Kammer 5 | 6 | ;; Authors: Brian Jiang 7 | ;; Golevka(?) [https://github.com/Golevka] 8 | ;; Taylan Ulrich Bayirli/Kammer 9 | ;; Many others 10 | ;; Keywords: completion, convenience 11 | ;; Version: 0 12 | 13 | ;; This program is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation, either version 3 of the License, or 16 | ;; (at your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program. If not, see . 25 | 26 | 27 | ;;; Commentary: 28 | 29 | ;; Auto Completion source for clang. 30 | ;; Uses a "completion server" process to utilize libclang. 31 | ;; Also provides flymake syntax checking. 32 | 33 | ;;; Code: 34 | 35 | 36 | (provide 'auto-complete-clang-async) 37 | (eval-when-compile (require' cl)) 38 | (require 'auto-complete) 39 | (require 'flymake) 40 | 41 | 42 | (defcustom ac-clang-complete-executable 43 | (executable-find "clang-complete") 44 | "Location of clang-complete executable." 45 | :group 'auto-complete 46 | :type 'file) 47 | 48 | (defcustom ac-clang-lang-option-function nil 49 | "Function to return the lang type for option -x." 50 | :group 'auto-complete 51 | :type 'function) 52 | 53 | (defcustom ac-clang-cflags nil 54 | "Extra flags to pass to the Clang executable. 55 | This variable will typically contain include paths, e.g., (\"-I~/MyProject\" \"-I.\")." 56 | :group 'auto-complete 57 | :type '(repeat (string :tag "Argument" ""))) 58 | (make-variable-buffer-local 'ac-clang-cflags) 59 | 60 | (defun ac-clang-set-cflags () 61 | "Set `ac-clang-cflags' interactively." 62 | (interactive) 63 | (setq ac-clang-cflags (split-string (read-string "New cflags: "))) 64 | (ac-clang-update-cmdlineargs)) 65 | 66 | (defun ac-clang-set-cflags-from-shell-command () 67 | "Set `ac-clang-cflags' to a shell command's output. 68 | 69 | set new cflags for ac-clang from shell command output" 70 | (interactive) 71 | (setq ac-clang-cflags 72 | (split-string 73 | (shell-command-to-string 74 | (read-shell-command "Shell command: " nil nil 75 | (and buffer-file-name 76 | (file-relative-name buffer-file-name)))))) 77 | (ac-clang-update-cmdlineargs)) 78 | 79 | (defvar ac-clang-prefix-header nil 80 | "The prefix header to pass to the Clang executable.") 81 | (make-variable-buffer-local 'ac-clang-prefix-header) 82 | 83 | (defvar ac-clang-async-do-autocompletion-automatically t 84 | "If autocompletion is automatically triggered when you type ., -> or ::") 85 | 86 | (defun ac-clang-set-prefix-header (prefix-header) 87 | "Set `ac-clang-prefix-header' interactively." 88 | (interactive 89 | (let ((default (car (directory-files "." t "\\([^.]h\\|[^h]\\).pch\\'" t)))) 90 | (list 91 | (read-file-name (concat "Clang prefix header (currently " (or ac-clang-prefix-header "nil") "): ") 92 | (when default (file-name-directory default)) 93 | default nil (when default (file-name-nondirectory default)))))) 94 | (cond 95 | ((string-match "^[\s\t]*$" prefix-header) 96 | (setq ac-clang-prefix-header nil)) 97 | (t 98 | (setq ac-clang-prefix-header prefix-header)))) 99 | 100 | 101 | (defconst ac-clang-completion-pattern 102 | "^COMPLETION: \\(%s[^\s\n:]*\\)\\(?: : \\)*\\(.*$\\)") 103 | 104 | (defun ac-clang-parse-output (prefix) 105 | (goto-char (point-min)) 106 | (let ((pattern (format ac-clang-completion-pattern 107 | (regexp-quote prefix))) 108 | lines match detailed-info 109 | (prev-match "")) 110 | (while (re-search-forward pattern nil t) 111 | (setq match (match-string-no-properties 1)) 112 | (unless (string= "Pattern" match) 113 | (setq detailed-info (match-string-no-properties 2)) 114 | 115 | (if (string= match prev-match) 116 | (progn 117 | (when detailed-info 118 | (setq match (propertize match 119 | 'ac-clang-help 120 | (concat 121 | (get-text-property 0 'ac-clang-help (car lines)) 122 | "\n" 123 | detailed-info))) 124 | (setf (car lines) match) 125 | )) 126 | (setq prev-match match) 127 | (when detailed-info 128 | (setq match (propertize match 'ac-clang-help detailed-info))) 129 | (push match lines)))) 130 | lines)) 131 | 132 | 133 | (defconst ac-clang-error-buffer-name "*clang error*") 134 | 135 | (defun ac-clang-handle-error (res args) 136 | (goto-char (point-min)) 137 | (let* ((buf (get-buffer-create ac-clang-error-buffer-name)) 138 | (cmd (concat ac-clang-complete-executable " " (mapconcat 'identity args " "))) 139 | (pattern (format ac-clang-completion-pattern "")) 140 | (err (if (re-search-forward pattern nil t) 141 | (buffer-substring-no-properties (point-min) 142 | (1- (match-beginning 0))) 143 | ;; Warn the user more agressively if no match was found. 144 | (message "clang failed with error %d:\n%s" res cmd) 145 | (buffer-string)))) 146 | 147 | (with-current-buffer buf 148 | (let ((inhibit-read-only t)) 149 | (erase-buffer) 150 | (insert (current-time-string) 151 | (format "\nclang failed with error %d:\n" res) 152 | cmd "\n\n") 153 | (insert err) 154 | (setq buffer-read-only t) 155 | (goto-char (point-min)))))) 156 | 157 | (defun ac-clang-call-process (prefix &rest args) 158 | (let ((buf (get-buffer-create "*clang-output*")) 159 | res) 160 | (with-current-buffer buf (erase-buffer)) 161 | (setq res (apply 'call-process-region (point-min) (point-max) 162 | ac-clang-complete-executable nil buf nil args)) 163 | (with-current-buffer buf 164 | (unless (eq 0 res) 165 | (ac-clang-handle-error res args)) 166 | ;; Still try to get any useful input. 167 | (ac-clang-parse-output prefix)))) 168 | 169 | 170 | (defsubst ac-clang-create-position-string (pos) 171 | (save-excursion 172 | (goto-char pos) 173 | (format "row:%d\ncolumn:%d\n" 174 | (line-number-at-pos) 175 | (1+ (- (point) (line-beginning-position)))))) 176 | 177 | (defsubst ac-clang-lang-option () 178 | (or (and ac-clang-lang-option-function 179 | (funcall ac-clang-lang-option-function)) 180 | (cond ((eq major-mode 'c++-mode) 181 | "c++") 182 | ((eq major-mode 'c-mode) 183 | "c") 184 | ((eq major-mode 'objc-mode) 185 | (cond ((string= "m" (file-name-extension (buffer-file-name))) 186 | "objective-c") 187 | (t 188 | "objective-c++"))) 189 | (t 190 | "c++")))) 191 | 192 | (defsubst ac-clang-build-complete-args () 193 | (append '("-cc1" "-fsyntax-only") 194 | (list "-x" (ac-clang-lang-option)) 195 | ac-clang-cflags 196 | (when (stringp ac-clang-prefix-header) 197 | (list "-include-pch" (expand-file-name ac-clang-prefix-header))))) 198 | 199 | 200 | (defsubst ac-clang-clean-document (s) 201 | (when s 202 | (setq s (replace-regexp-in-string "<#\\|#>\\|\\[#" "" s)) 203 | (setq s (replace-regexp-in-string "#\\]" " " s))) 204 | s) 205 | 206 | (defun ac-clang-document (item) 207 | (if (stringp item) 208 | (let (s) 209 | (setq s (get-text-property 0 'ac-clang-help item)) 210 | (ac-clang-clean-document s))) 211 | ;; (popup-item-property item 'ac-clang-help) 212 | ) 213 | 214 | 215 | (defface ac-clang-candidate-face 216 | '((t (:background "lightgray" :foreground "navy"))) 217 | "Face for clang candidate" 218 | :group 'auto-complete) 219 | 220 | (defface ac-clang-selection-face 221 | '((t (:background "navy" :foreground "white"))) 222 | "Face for the clang selected candidate." 223 | :group 'auto-complete) 224 | 225 | (defsubst ac-clang-in-string/comment () 226 | "Return non-nil if point is in a literal (a comment or string)." 227 | (nth 8 (syntax-ppss))) 228 | 229 | 230 | (defvar ac-clang-template-start-point nil) 231 | (defvar ac-clang-template-candidates (list "ok" "no" "yes:)")) 232 | 233 | (defun ac-clang-action () 234 | (interactive) 235 | ;; (ac-last-quick-help) 236 | (let ((help (ac-clang-clean-document (get-text-property 0 'ac-clang-help (cdr ac-last-completion)))) 237 | (raw-help (get-text-property 0 'ac-clang-help (cdr ac-last-completion))) 238 | (candidates (list)) ss fn args (ret-t "") ret-f) 239 | (setq ss (split-string raw-help "\n")) 240 | (dolist (s ss) 241 | (when (string-match "\\[#\\(.*\\)#\\]" s) 242 | (setq ret-t (match-string 1 s))) 243 | (setq s (replace-regexp-in-string "\\[#.*?#\\]" "" s)) 244 | (cond ((string-match "^\\([^(<]*\\)\\(:.*\\)" s) 245 | (setq fn (match-string 1 s) 246 | args (match-string 2 s)) 247 | (push (propertize (ac-clang-clean-document args) 'ac-clang-help ret-t 248 | 'raw-args args) candidates)) 249 | ((string-match "^\\([^(]*\\)\\((.*)\\)" s) 250 | (setq fn (match-string 1 s) 251 | args (match-string 2 s)) 252 | (push (propertize (ac-clang-clean-document args) 'ac-clang-help ret-t 253 | 'raw-args args) candidates) 254 | (when (string-match "\{#" args) 255 | (setq args (replace-regexp-in-string "\{#.*#\}" "" args)) 256 | (push (propertize (ac-clang-clean-document args) 'ac-clang-help ret-t 257 | 'raw-args args) candidates)) 258 | (when (string-match ", \\.\\.\\." args) 259 | (setq args (replace-regexp-in-string ", \\.\\.\\." "" args)) 260 | (push (propertize (ac-clang-clean-document args) 'ac-clang-help ret-t 261 | 'raw-args args) candidates))) 262 | ((string-match "^\\([^(]*\\)(\\*)\\((.*)\\)" ret-t) ;; check whether it is a function ptr 263 | (setq ret-f (match-string 1 ret-t) 264 | args (match-string 2 ret-t)) 265 | (push (propertize args 'ac-clang-help ret-f 'raw-args "") candidates) 266 | (when (string-match ", \\.\\.\\." args) 267 | (setq args (replace-regexp-in-string ", \\.\\.\\." "" args)) 268 | (push (propertize args 'ac-clang-help ret-f 'raw-args "") candidates))))) 269 | (cond (candidates 270 | (setq candidates (delete-dups candidates)) 271 | (setq candidates (nreverse candidates)) 272 | (setq ac-clang-template-candidates candidates) 273 | (setq ac-clang-template-start-point (point)) 274 | (ac-complete-clang-template) 275 | 276 | (unless (cdr candidates) ;; unless length > 1 277 | (message (replace-regexp-in-string "\n" " ; " help)))) 278 | (t 279 | (message (replace-regexp-in-string "\n" " ; " help)))))) 280 | 281 | (defun ac-clang-prefix () 282 | (or (ac-prefix-symbol) 283 | (let ((c (char-before))) 284 | (when (or (eq ?\. c) 285 | ;; -> 286 | (and (eq ?> c) 287 | (eq ?- (char-before (1- (point))))) 288 | ;; :: 289 | (and (eq ?: c) 290 | (eq ?: (char-before (1- (point)))))) 291 | (point))))) 292 | 293 | (defun ac-clang-same-count-in-string (c1 c2 s) 294 | (let ((count 0) (cur 0) (end (length s)) c) 295 | (while (< cur end) 296 | (setq c (aref s cur)) 297 | (cond ((eq c1 c) 298 | (setq count (1+ count))) 299 | ((eq c2 c) 300 | (setq count (1- count)))) 301 | (setq cur (1+ cur))) 302 | (= count 0))) 303 | 304 | (defun ac-clang-split-args (s) 305 | (let ((sl (split-string s ", *"))) 306 | (cond ((string-match "<\\|(" s) 307 | (let ((res (list)) (pre "") subs) 308 | (while sl 309 | (setq subs (pop sl)) 310 | (unless (string= pre "") 311 | (setq subs (concat pre ", " subs)) 312 | (setq pre "")) 313 | (cond ((and (ac-clang-same-count-in-string ?\< ?\> subs) 314 | (ac-clang-same-count-in-string ?\( ?\) subs)) 315 | ;; (cond ((ac-clang-same-count-in-string ?\< ?\> subs) 316 | (push subs res)) 317 | (t 318 | (setq pre subs)))) 319 | (nreverse res))) 320 | (t 321 | sl)))) 322 | 323 | 324 | (defun ac-clang-template-candidate () 325 | ac-clang-template-candidates) 326 | 327 | (defun ac-clang-template-action () 328 | (interactive) 329 | (unless (null ac-clang-template-start-point) 330 | (let ((pos (point)) sl (snp "") 331 | (s (get-text-property 0 'raw-args (cdr ac-last-completion)))) 332 | (cond ((string= s "") 333 | ;; function ptr call 334 | (setq s (cdr ac-last-completion)) 335 | (setq s (replace-regexp-in-string "^(\\|)$" "" s)) 336 | (setq sl (ac-clang-split-args s)) 337 | (cond ((featurep 'yasnippet) 338 | (dolist (arg sl) 339 | (setq snp (concat snp ", ${" arg "}"))) 340 | (condition-case nil 341 | (yas/expand-snippet (concat "(" (substring snp 2) ")") 342 | ac-clang-template-start-point pos) ;; 0.6.1c 343 | (error 344 | ;; try this one: 345 | (ignore-errors (yas/expand-snippet 346 | ac-clang-template-start-point pos 347 | (concat "(" (substring snp 2) ")"))) ;; work in 0.5.7 348 | ))) 349 | ((featurep 'snippet) 350 | (delete-region ac-clang-template-start-point pos) 351 | (dolist (arg sl) 352 | (setq snp (concat snp ", $${" arg "}"))) 353 | (snippet-insert (concat "(" (substring snp 2) ")"))) 354 | (t 355 | (message "Dude! You are too out! Please install a yasnippet or a snippet script:)")))) 356 | (t 357 | (unless (string= s "()") 358 | (setq s (replace-regexp-in-string "{#" "" s)) 359 | (setq s (replace-regexp-in-string "#}" "" s)) 360 | (cond ((featurep 'yasnippet) 361 | (setq s (replace-regexp-in-string "<#" "${" s)) 362 | (setq s (replace-regexp-in-string "#>" "}" s)) 363 | (setq s (replace-regexp-in-string ", \\.\\.\\." "}, ${..." s)) 364 | (condition-case nil 365 | (yas/expand-snippet s ac-clang-template-start-point pos) ;; 0.6.1c 366 | (error 367 | ;; try this one: 368 | (ignore-errors (yas/expand-snippet ac-clang-template-start-point pos s)) ;; work in 0.5.7 369 | ))) 370 | ((featurep 'snippet) 371 | (delete-region ac-clang-template-start-point pos) 372 | (setq s (replace-regexp-in-string "<#" "$${" s)) 373 | (setq s (replace-regexp-in-string "#>" "}" s)) 374 | (setq s (replace-regexp-in-string ", \\.\\.\\." "}, $${..." s)) 375 | (snippet-insert s)) 376 | (t 377 | (message "Dude! You are too out! Please install a yasnippet or a snippet script:)"))))))))) 378 | 379 | 380 | (defun ac-clang-template-prefix () 381 | ac-clang-template-start-point) 382 | 383 | 384 | ;; This source shall only be used internally. 385 | (ac-define-source clang-template 386 | '((candidates . ac-clang-template-candidate) 387 | (prefix . ac-clang-template-prefix) 388 | (requires . 0) 389 | (action . ac-clang-template-action) 390 | (document . ac-clang-document) 391 | (cache) 392 | (symbol . "t"))) 393 | 394 | 395 | ;;; 396 | ;;; Rest of the file is related to async. 397 | ;;; 398 | 399 | (defvar ac-clang-status 'idle) 400 | (defvar ac-clang-current-candidate nil) 401 | (defvar ac-clang-completion-process nil) 402 | (defvar ac-clang-saved-prefix "") 403 | 404 | (make-variable-buffer-local 'ac-clang-status) 405 | (make-variable-buffer-local 'ac-clang-current-candidate) 406 | (make-variable-buffer-local 'ac-clang-completion-process) 407 | 408 | ;;; 409 | ;;; Functions to speak with the clang-complete process 410 | ;;; 411 | 412 | (defun ac-clang-send-source-code (proc) 413 | (save-restriction 414 | (widen) 415 | (process-send-string 416 | proc (format "source_length:%d\n" 417 | (length (string-as-unibyte ; fix non-ascii character problem 418 | (buffer-substring-no-properties (point-min) (point-max))) 419 | ))) 420 | (process-send-string proc (buffer-substring-no-properties (point-min) (point-max))) 421 | (process-send-string proc "\n\n"))) 422 | 423 | (defun ac-clang-send-reparse-request (proc) 424 | (if (eq (process-status proc) 'run) 425 | (save-restriction 426 | (widen) 427 | (process-send-string proc "SOURCEFILE\n") 428 | (ac-clang-send-source-code proc) 429 | (process-send-string proc "REPARSE\n\n")))) 430 | 431 | (defun ac-clang-send-completion-request (proc) 432 | (save-restriction 433 | (widen) 434 | (process-send-string proc "COMPLETION\n") 435 | (process-send-string proc (ac-clang-create-position-string (- (point) (length ac-prefix)))) 436 | (ac-clang-send-source-code proc))) 437 | 438 | (defun ac-clang-send-syntaxcheck-request (proc) 439 | (save-restriction 440 | (widen) 441 | (process-send-string proc "SYNTAXCHECK\n") 442 | (ac-clang-send-source-code proc))) 443 | 444 | (defun ac-clang-send-cmdline-args (proc) 445 | ;; send message head and num_args 446 | (process-send-string proc "CMDLINEARGS\n") 447 | (process-send-string 448 | proc (format "num_args:%d\n" (length (ac-clang-build-complete-args)))) 449 | 450 | ;; send arguments 451 | (mapc 452 | (lambda (arg) 453 | (process-send-string proc (format "%s " arg))) 454 | (ac-clang-build-complete-args)) 455 | (process-send-string proc "\n")) 456 | 457 | (defun ac-clang-update-cmdlineargs () 458 | (interactive) 459 | (if (listp ac-clang-cflags) 460 | (ac-clang-send-cmdline-args ac-clang-completion-process) 461 | (message "`ac-clang-cflags' should be a list of strings"))) 462 | 463 | (defun ac-clang-send-shutdown-command (proc) 464 | (if (eq (process-status proc) 'run) 465 | (process-send-string proc "SHUTDOWN\n")) 466 | ) 467 | 468 | 469 | (defun ac-clang-append-process-output-to-process-buffer (process output) 470 | "Append process output to the process buffer." 471 | (with-current-buffer (process-buffer process) 472 | (save-excursion 473 | ;; Insert the text, advancing the process marker. 474 | (goto-char (process-mark process)) 475 | (insert output) 476 | (set-marker (process-mark process) (point))) 477 | (goto-char (process-mark process)))) 478 | 479 | 480 | ;; 481 | ;; Receive server responses (completion candidates) and fire auto-complete 482 | ;; 483 | (defun ac-clang-parse-completion-results (proc) 484 | (with-current-buffer (process-buffer proc) 485 | (ac-clang-parse-output ac-clang-saved-prefix))) 486 | 487 | (defun ac-clang-filter-output (proc string) 488 | (ac-clang-append-process-output-to-process-buffer proc string) 489 | (if (string= (substring string -1 nil) "$") 490 | (case ac-clang-status 491 | (preempted 492 | (setq ac-clang-status 'idle) 493 | (ac-start) 494 | (ac-update)) 495 | 496 | (otherwise 497 | (setq ac-clang-current-candidate (ac-clang-parse-completion-results proc)) 498 | ;; (message "ac-clang results arrived") 499 | (setq ac-clang-status 'acknowledged) 500 | (ac-start :force-init t) 501 | (ac-update) 502 | (setq ac-clang-status 'idle))))) 503 | 504 | 505 | (defun ac-clang-candidate () 506 | (case ac-clang-status 507 | (idle 508 | ;; (message "ac-clang-candidate triggered - fetching candidates...") 509 | (setq ac-clang-saved-prefix ac-prefix) 510 | 511 | ;; NOTE: although auto-complete would filter the result for us, but when there's 512 | ;; a HUGE number of candidates avaliable it would cause auto-complete to 513 | ;; block. So we filter it uncompletely here, then let auto-complete filter 514 | ;; the rest later, this would ease the feeling of being "stalled" at some degree. 515 | 516 | ;; (message "saved prefix: %s" ac-clang-saved-prefix) 517 | (with-current-buffer (process-buffer ac-clang-completion-process) 518 | (erase-buffer)) 519 | (setq ac-clang-status 'wait) 520 | (setq ac-clang-current-candidate nil) 521 | 522 | ;; send completion request 523 | (ac-clang-send-completion-request ac-clang-completion-process) 524 | ac-clang-current-candidate) 525 | 526 | (wait 527 | ;; (message "ac-clang-candidate triggered - wait") 528 | ac-clang-current-candidate) 529 | 530 | (acknowledged 531 | ;; (message "ac-clang-candidate triggered - ack") 532 | (setq ac-clang-status 'idle) 533 | ac-clang-current-candidate) 534 | 535 | (preempted 536 | ;; (message "clang-async is preempted by a critical request") 537 | nil))) 538 | 539 | 540 | ;; Syntax checking with flymake 541 | 542 | (defun ac-clang-flymake-process-sentinel () 543 | (interactive) 544 | (setq flymake-err-info flymake-new-err-info) 545 | (setq flymake-new-err-info nil) 546 | (setq flymake-err-info 547 | (flymake-fix-line-numbers 548 | flymake-err-info 1 (flymake-count-lines))) 549 | (flymake-delete-own-overlays) 550 | (flymake-highlight-err-lines flymake-err-info)) 551 | 552 | (defun ac-clang-flymake-process-filter (process output) 553 | (ac-clang-append-process-output-to-process-buffer process output) 554 | (flymake-log 3 "received %d byte(s) of output from process %d" 555 | (length output) (process-id process)) 556 | (flymake-parse-output-and-residual output) 557 | (when (string= (substring output -1 nil) "$") 558 | (flymake-parse-residual) 559 | (ac-clang-flymake-process-sentinel) 560 | (setq ac-clang-status 'idle) 561 | (set-process-filter ac-clang-completion-process 'ac-clang-filter-output))) 562 | 563 | (defun ac-clang-syntax-check () 564 | (interactive) 565 | (when (eq ac-clang-status 'idle) 566 | (with-current-buffer (process-buffer ac-clang-completion-process) 567 | (erase-buffer)) 568 | (setq ac-clang-status 'wait) 569 | (set-process-filter ac-clang-completion-process 'ac-clang-flymake-process-filter) 570 | (ac-clang-send-syntaxcheck-request ac-clang-completion-process))) 571 | 572 | 573 | 574 | (defun ac-clang-shutdown-process () 575 | (if ac-clang-completion-process 576 | (ac-clang-send-shutdown-command ac-clang-completion-process))) 577 | 578 | (defun ac-clang-reparse-buffer () 579 | (if ac-clang-completion-process 580 | (ac-clang-send-reparse-request ac-clang-completion-process))) 581 | 582 | 583 | 584 | (defun ac-clang-async-autocomplete-autotrigger () 585 | (interactive) 586 | (if ac-clang-async-do-autocompletion-automatically 587 | (ac-clang-async-preemptive) 588 | (self-insert-command 1))) 589 | 590 | 591 | 592 | (defun ac-clang-async-preemptive () 593 | (interactive) 594 | (self-insert-command 1) 595 | (if (eq ac-clang-status 'idle) 596 | (ac-start) 597 | (setq ac-clang-status 'preempted))) 598 | 599 | (defun ac-clang-launch-completion-process () 600 | (let ((filename (buffer-file-name))) 601 | (if filename 602 | (ac-clang-launch-completion-process-with-file filename)))) 603 | 604 | (defun ac-clang-launch-completion-process-with-file (filename) 605 | (setq ac-clang-completion-process 606 | (let ((process-connection-type nil)) 607 | (apply 'start-process 608 | "clang-complete" "*clang-complete*" 609 | ac-clang-complete-executable 610 | (append (ac-clang-build-complete-args) 611 | (list filename))))) 612 | 613 | (set-process-filter ac-clang-completion-process 'ac-clang-filter-output) 614 | (set-process-query-on-exit-flag ac-clang-completion-process nil) 615 | ;; Pre-parse source code. 616 | (ac-clang-send-reparse-request ac-clang-completion-process) 617 | 618 | (add-hook 'kill-buffer-hook 'ac-clang-shutdown-process nil t) 619 | (add-hook 'before-revert-hook 'ac-clang-shutdown-process nil t) 620 | (add-hook 'before-save-hook 'ac-clang-reparse-buffer) 621 | 622 | (local-set-key (kbd ".") 'ac-clang-async-autocomplete-autotrigger) 623 | (local-set-key (kbd ":") 'ac-clang-async-autocomplete-autotrigger) 624 | (local-set-key (kbd ">") 'ac-clang-async-autocomplete-autotrigger)) 625 | 626 | 627 | (ac-define-source clang-async 628 | '((candidates . ac-clang-candidate) 629 | (candidate-face . ac-clang-candidate-face) 630 | (selection-face . ac-clang-selection-face) 631 | (prefix . ac-clang-prefix) 632 | (requires . 0) 633 | (document . ac-clang-document) 634 | (action . ac-clang-action) 635 | (cache) 636 | (symbol . "c"))) 637 | 638 | ;;; auto-complete-clang-async.el ends here 639 | -------------------------------------------------------------------------------- /makefile.mk: -------------------------------------------------------------------------------- 1 | # INCLUDE_PATH, SOURCE_PATH, DEPENDENCY_PATH, OBJECT_PATH, EXTERNAL_LIBS and 2 | # PROGRAM_NAME should be defined in custom makefile 3 | 4 | vpath %.h $(INCLUDE_PATH) 5 | vpath %.c $(SOURCE_PATH) 6 | vpath %.d $(DEPENDENCY_PATH) 7 | vpath %.o $(OBJECT_PATH) 8 | 9 | 10 | prefix = /usr/local 11 | exec_prefix = ${prefix} 12 | bindir = ${exec_prefix}/bin 13 | PROGRAM_EXEC = $(bindir)/$(PROGRAM_NAME) 14 | 15 | 16 | ## default .o and .dep path and program name 17 | OBJECT_PATH ?= obj 18 | DEPENDENCY_PATH ?= dep 19 | PROGRAM_NAME ?= run 20 | 21 | # source trunk 22 | source-files = $(wildcard $(addsuffix /*.c, $(SOURCE_PATH))) 23 | source-list = $(notdir $(source-files)) 24 | 25 | # binary trunk 26 | objname-list = $(subst .c,.o, $(source-list)) 27 | object-list = $(addprefix $(OBJECT_PATH)/, $(objname-list)) 28 | 29 | # dependency trunk 30 | depname-list = $(subst .c,.d, $(source-list)) 31 | dependency-list = $(addprefix $(DEPENDENCY_PATH)/, $(depname-list)) 32 | 33 | # -I option to help the compiler finding the headers 34 | CFLAGS += $(addprefix -I, $(INCLUDE_PATH)) 35 | 36 | 37 | # PROGRAM_NAME is provided in custom makefile 38 | $(PROGRAM_NAME): $(object-list) 39 | $(LINK.c) $^ $(LDLIBS) -o $@ 40 | 41 | 42 | $(OBJECT_PATH)/%.o: %.c 43 | @mkdir -p $(OBJECT_PATH) 44 | $(COMPILE.c) $(OUTPUT_OPTION) $< 45 | 46 | 47 | # Resolve [object,source] -- [header] dependency 48 | -include $(dependency-list) 49 | 50 | $(DEPENDENCY_PATH)/%.d: %.c 51 | @mkdir -p $(DEPENDENCY_PATH) 52 | @$(CC) -M $(CFLAGS) $< > $@.$$$$; \ 53 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ 54 | rm -f $@.$$$$ 55 | 56 | 57 | .PHONY: clean build install uninstall 58 | clean: 59 | rm -f $(object-list) $(dependency-list) 60 | 61 | install: $(PROGRAM_NAME) 62 | cp -f $(PROGRAM_NAME) $(bindir) 63 | 64 | uninstall: clean 65 | rm -f $(PROGRAM_EXEC) 66 | -------------------------------------------------------------------------------- /screenshots/class_member.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golevka/emacs-clang-complete-async/5d9c5cabbb6b31e0ac3637631c0c8b25184aa8b4/screenshots/class_member.png -------------------------------------------------------------------------------- /screenshots/global_namespace_clang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golevka/emacs-clang-complete-async/5d9c5cabbb6b31e0ac3637631c0c8b25184aa8b4/screenshots/global_namespace_clang.png -------------------------------------------------------------------------------- /screenshots/global_namespace_macro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golevka/emacs-clang-complete-async/5d9c5cabbb6b31e0ac3637631c0c8b25184aa8b4/screenshots/global_namespace_macro.png -------------------------------------------------------------------------------- /screenshots/namespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golevka/emacs-clang-complete-async/5d9c5cabbb6b31e0ac3637631c0c8b25184aa8b4/screenshots/namespace.png -------------------------------------------------------------------------------- /screenshots/showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golevka/emacs-clang-complete-async/5d9c5cabbb6b31e0ac3637631c0c8b25184aa8b4/screenshots/showcase.png -------------------------------------------------------------------------------- /screenshots/syntax_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golevka/emacs-clang-complete-async/5d9c5cabbb6b31e0ac3637631c0c8b25184aa8b4/screenshots/syntax_check.png -------------------------------------------------------------------------------- /src/completion.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMPLETION_SESSION_H_ 2 | #define _COMPLETION_SESSION_H_ 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | typedef struct __completion_Session_struct 10 | { 11 | /* */ 12 | const char *src_filename; /* filename of the source file */ 13 | char *src_buffer; /* buffer holding the source code */ 14 | int src_length; /* length of the source code */ 15 | int buffer_capacity; /* size of source code buffer */ 16 | 17 | /* */ 18 | int num_args; /* number of command line arguments */ 19 | char **cmdline_args; /* command line arguments to pass to the clang 20 | * driver */ 21 | 22 | /* */ 23 | CXIndex cx_index; 24 | CXTranslationUnit cx_tu; 25 | 26 | /* */ 27 | unsigned ParseOptions; 28 | unsigned CompleteAtOptions; 29 | 30 | } completion_Session; 31 | 32 | 33 | /* COMPLETION SERVER DEFAULT SETTINGS */ 34 | 35 | #define DEFAULT_PARSE_OPTIONS CXTranslationUnit_PrecompiledPreamble 36 | #define DEFAULT_COMPLETEAT_OPTIONS CXCodeComplete_IncludeMacros 37 | #define INITIAL_SRC_BUFFER_SIZE 4096 /* 4KB */ 38 | 39 | 40 | 41 | /* clang_defaultEditingTranslationUnitOptions() */ 42 | /* CXTranslationUnit_PrecompiledPreamble */ 43 | 44 | 45 | 46 | 47 | /* Initialize basic information for completion, such as source filename, initial source 48 | buffer and command line arguments to pass to clang */ 49 | void 50 | __initialize_completionSession(int argc, char *argv[], completion_Session *session); 51 | 52 | /* Initialize session object and launch the completion server, preparse the source file and 53 | build the AST for furture code completion requests */ 54 | void startup_completionSession(int argc, char *argv[], completion_Session *session); 55 | 56 | 57 | /* Print specified completion string to fp. */ 58 | void completion_printCompletionLine(CXCompletionString completion_string, FILE *fp); 59 | 60 | /* Print all completion results to fp */ 61 | void completion_printCodeCompletionResults(CXCodeCompleteResults *res, FILE *fp); 62 | 63 | 64 | /* Simple wrappers for clang parser functions */ 65 | 66 | CXTranslationUnit completion_parseTranslationUnit(completion_Session *session); 67 | int completion_reparseTranslationUnit(completion_Session *session); 68 | CXCodeCompleteResults* completion_codeCompleteAt( 69 | completion_Session *session, int line, int column); 70 | 71 | 72 | 73 | #endif /* _COMPLETION_SESSION_H_ */ 74 | -------------------------------------------------------------------------------- /src/completion_serv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "completion.h" 4 | 5 | 6 | 7 | /* Copy command line parameters (except source filename) to cmdline_args */ 8 | static void __copy_cmdlineArgs(int argc, char *argv[], completion_Session *session) 9 | { 10 | int i_arg = 0; 11 | session->num_args = argc - 2; /* argv[0] and argv[argc - 2] should be discarded */ 12 | session->cmdline_args = (char**)calloc(sizeof(char*), session->num_args); 13 | 14 | /* copy argv[1..argc-1] to cmdline_args */ 15 | for ( ; i_arg < session->num_args; i_arg++) 16 | { 17 | session->cmdline_args[i_arg] = 18 | (char*)calloc(sizeof(char), strlen(argv[i_arg + 1]) + 1); 19 | 20 | strcpy(session->cmdline_args[i_arg], argv[i_arg + 1]); 21 | } 22 | } 23 | 24 | /* Initialize basic information for completion, such as source filename, initial source 25 | buffer and command line arguments for clang */ 26 | void 27 | __initialize_completionSession(int argc, char *argv[], completion_Session *session) 28 | { 29 | /* filename shall be the last parameter */ 30 | session->src_filename = argv[argc - 1]; 31 | session->src_length = 0; /* we haven't read any source code yet. */ 32 | session->buffer_capacity = INITIAL_SRC_BUFFER_SIZE; 33 | session->src_buffer = (char*)calloc(sizeof(char), session->buffer_capacity); 34 | 35 | __copy_cmdlineArgs(argc, argv, session); 36 | } 37 | 38 | 39 | /* Initialize session object and launch the completion server, preparse the source file and 40 | build the AST for furture code completion requests 41 | */ 42 | void startup_completionSession(int argc, char *argv[], completion_Session *session) 43 | { 44 | __initialize_completionSession(argc, argv, session); 45 | 46 | /* default parameters */ 47 | session->ParseOptions = DEFAULT_PARSE_OPTIONS; 48 | session->CompleteAtOptions = DEFAULT_COMPLETEAT_OPTIONS; 49 | 50 | session->cx_index = clang_createIndex(0, 0); 51 | completion_parseTranslationUnit(session); 52 | completion_reparseTranslationUnit(session); 53 | } 54 | 55 | 56 | /* Simple wrappers for clang parser functions */ 57 | 58 | static struct CXUnsavedFile __get_CXUnsavedFile(const completion_Session *session) 59 | { 60 | struct CXUnsavedFile unsaved_files; 61 | unsaved_files.Filename = session->src_filename; 62 | unsaved_files.Contents = session->src_buffer; 63 | unsaved_files.Length = session->src_length; 64 | 65 | return unsaved_files; 66 | } 67 | 68 | CXTranslationUnit 69 | completion_parseTranslationUnit(completion_Session *session) 70 | { 71 | struct CXUnsavedFile unsaved_files = __get_CXUnsavedFile(session); 72 | session->cx_tu = 73 | clang_parseTranslationUnit( 74 | session->cx_index, session->src_filename, 75 | (const char * const *) session->cmdline_args, session->num_args, 76 | &unsaved_files, 1, 77 | session->ParseOptions); 78 | 79 | return session->cx_tu; 80 | } 81 | 82 | int completion_reparseTranslationUnit(completion_Session *session) 83 | { 84 | struct CXUnsavedFile unsaved_files = __get_CXUnsavedFile(session); 85 | return 86 | clang_reparseTranslationUnit( 87 | session->cx_tu, 1, &unsaved_files, session->ParseOptions); 88 | } 89 | 90 | CXCodeCompleteResults* 91 | completion_codeCompleteAt( 92 | completion_Session *session, int line, int column) 93 | { 94 | struct CXUnsavedFile unsaved_files = __get_CXUnsavedFile(session); 95 | return 96 | clang_codeCompleteAt( 97 | session->cx_tu, session->src_filename, line, column, 98 | &unsaved_files, 1, session->CompleteAtOptions); 99 | } 100 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "completion.h" 7 | #include "msg_callback.h" 8 | 9 | 10 | /* This function is intended to help debugging transmition issues, and should 11 | not be called in the final release version. */ 12 | void __dump_session(const completion_Session *session, FILE *fp) 13 | { 14 | int i_arg = 0; 15 | for ( ; i_arg < session->num_args; i_arg++) { 16 | fprintf(fp, "[%-2d]: %s\n", i_arg, session->cmdline_args[i_arg]); 17 | } 18 | 19 | fprintf(fp, "filename: %s\n" 20 | "code:\n%s\n", 21 | session->src_filename, session->src_buffer); fflush(fp); 22 | } 23 | 24 | 25 | int main(int argc, char *argv[]) 26 | { 27 | completion_Session session; 28 | 29 | if (argc < 2) { 30 | printf("Source file name must be specified as the last commandline argument\n"); 31 | exit(-1); 32 | } 33 | 34 | startup_completionSession(argc, argv, &session); 35 | 36 | for ( ; ; ) { 37 | completion_AcceptRequest(&session, stdin); 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/msg_callback.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMPLETION_PROTOCOL_H_ 2 | #define _COMPLETION_PROTOCOL_H_ 3 | 4 | 5 | 6 | #include "completion.h" 7 | 8 | 9 | /* Dispatch different messages to their corresponding message handlers */ 10 | void completion_AcceptRequest(completion_Session *session, FILE *fp); 11 | 12 | 13 | /* 14 | MESSAGE HANDLERS: callback to handle recieved requests 15 | 16 | COMPLETION: Do code completion at a specified point. 17 | Message format: 18 | row:[#row#] 19 | column:[#column#] 20 | source_length:[#src_length#] 21 | <# SOURCE CODE #> 22 | 23 | SOURCEFILE: Update the source code in the source buffer (session->src_buffer) 24 | Message format: 25 | source_length:[#src_length#] 26 | <# SOURCE CODE #> 27 | 28 | CMDLINEARGS: Specify command line arguments passing to clang parser. 29 | Message format: 30 | num_args:[#n_args#] 31 | arg1 arg2 ...... (there should be n_args items here) 32 | 33 | REPARSE: Reparse the source code 34 | [no message body] 35 | 36 | SYNTAXCHECK: Retrieve diagnostic messages 37 | Message format: 38 | source_length:[#src_length#] 39 | <# SOURCE CODE #> 40 | 41 | SHUTDOWN: Shut down the completion server (this program) 42 | [no message body] 43 | */ 44 | 45 | /* message handlers */ 46 | void completion_doCompletion(completion_Session *session, FILE *fp); /* COMPLETION */ 47 | void completion_doSourcefile(completion_Session *session, FILE *fp); /* SOURCEFILE */ 48 | void completion_doCmdlineArgs(completion_Session *session, FILE *fp); /* CMDLINEARGS */ 49 | void completion_doReparse(completion_Session *session, FILE *fp); /* REPARSE */ 50 | void completion_doSyntaxCheck(completion_Session *session, FILE *fp); /* SYNTAXCHECK */ 51 | void completion_doShutdown(completion_Session *session, FILE *fp); /* SHUTDOWN */ 52 | 53 | 54 | #endif /* _COMPLETION_PROTOCOL_H_ */ 55 | -------------------------------------------------------------------------------- /src/msg_dispatcher.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "msg_callback.h" 5 | 6 | 7 | /* command - message tuple for commander pattern */ 8 | struct __command_dispatch_entry { 9 | const char *command; 10 | void (*command_handler)(completion_Session*, FILE*); 11 | }; 12 | 13 | /* message dispatch table */ 14 | struct __command_dispatch_entry 15 | __command_dispatch_table[] = 16 | { 17 | {"COMPLETION", completion_doCompletion}, 18 | {"SOURCEFILE", completion_doSourcefile}, 19 | {"CMDLINEARGS", completion_doCmdlineArgs}, 20 | {"SYNTAXCHECK", completion_doSyntaxCheck}, 21 | {"REPARSE", completion_doReparse}, 22 | {"SHUTDOWN", completion_doShutdown} 23 | }; 24 | 25 | 26 | /* Dispatch different messages to their corresponding message handlers */ 27 | void completion_AcceptRequest(completion_Session *session, FILE *fp) 28 | { 29 | unsigned int i_entry = 0; 30 | char msg_head[LINE_MAX]; 31 | fscanf(fp, "%s", msg_head); 32 | 33 | /* find corresponded message handler to dispatch message to */ 34 | for ( ; i_entry < 35 | sizeof(__command_dispatch_table)/ 36 | sizeof(__command_dispatch_table[0]); i_entry++) 37 | { 38 | if (strcmp(msg_head, 39 | __command_dispatch_table[i_entry].command) == 0) 40 | { 41 | fgets(msg_head, sizeof(msg_head), fp); /* skip trailing '\n' */ 42 | __command_dispatch_table[i_entry].command_handler(session, fp); 43 | return; 44 | } 45 | } 46 | 47 | /* no message handler was found */ 48 | printf("ERROR: UNKNOWN COMMAND: %s", msg_head); fflush(stdout); 49 | } 50 | -------------------------------------------------------------------------------- /src/msg_handlers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "msg_callback.h" 6 | 7 | 8 | /* discard all remaining contents on this line, jump to the beginning of the 9 | next line */ 10 | static void __skip_the_rest(FILE *fp) 11 | { 12 | char crlf[LINE_MAX]; 13 | fgets(crlf, LINE_MAX, fp); 14 | } 15 | 16 | /* read len bytes from fp to buffer */ 17 | static void __read_n_bytes(FILE *fp, char *buffer, int len) 18 | { 19 | while (len-- != 0) { 20 | *buffer++ = (char)fgetc(fp); 21 | } 22 | } 23 | 24 | 25 | /* Read the source file portion of the message to the source code buffer in 26 | specified session object. 27 | 28 | Sourcefile segment always starts with source_length: [#len#], followed by a 29 | newline character, then the actual source code. Length of the source code is 30 | indicated by [#len#] so we know how much bytes we should read from fp. 31 | */ 32 | static void completion_readSourcefile(completion_Session *session, FILE *fp) 33 | { 34 | int source_length; 35 | fscanf(fp, "source_length:%d", &source_length); 36 | __skip_the_rest(fp); 37 | 38 | if (source_length >= session->buffer_capacity) /* we're running out of space */ 39 | { 40 | /* expand the buffer two-fold of source size */ 41 | session->buffer_capacity = source_length * 2; 42 | session->src_buffer = 43 | (char*)realloc(session->src_buffer, session->buffer_capacity); 44 | } 45 | 46 | /* read source code from fp to buffer */ 47 | session->src_length = source_length; 48 | __read_n_bytes(fp, session->src_buffer, source_length); 49 | } 50 | 51 | 52 | /* Read completion request (where to complete at and current source code) from message 53 | header and calculate completion candidates. 54 | 55 | Message format: 56 | row: [#row_number#] 57 | column: [#column_number#] 58 | source_length: [#src_length#] 59 | <# SOURCE CODE #> 60 | */ 61 | void completion_doCompletion(completion_Session *session, FILE *fp) 62 | { 63 | CXCodeCompleteResults *res; 64 | 65 | /* get where to complete at */ 66 | int row, column; 67 | fscanf(fp, "row:%d", &row); __skip_the_rest(fp); 68 | fscanf(fp, "column:%d", &column); __skip_the_rest(fp); 69 | 70 | /* get a copy of fresh source file */ 71 | completion_readSourcefile(session, fp); 72 | 73 | /* calculate and show code completions results */ 74 | res = completion_codeCompleteAt(session, row, column); 75 | if (res != NULL) 76 | { 77 | /* code completion completed successfully, so we sort and dump these 78 | * completion candidates back to client */ 79 | clang_sortCodeCompletionResults(res->Results, res->NumResults); 80 | completion_printCodeCompletionResults(res, stdout); 81 | clang_disposeCodeCompleteResults(res); 82 | } 83 | 84 | fprintf(stdout, "$"); fflush(stdout); /* we need to inform emacs that all 85 | candidates has already been sent */ 86 | } 87 | 88 | 89 | /* Reparse the source code to refresh the translation unit */ 90 | void completion_doReparse(completion_Session *session, FILE *fp) 91 | { 92 | (void) fp; /* get rid of unused parameter warning */ 93 | completion_reparseTranslationUnit(session); 94 | } 95 | 96 | /* Update source code in src_buffer */ 97 | void completion_doSourcefile(completion_Session *session, FILE *fp) 98 | { 99 | (void) fp; /* get rid of unused parameter warning */ 100 | completion_readSourcefile(session, fp); 101 | } 102 | 103 | 104 | /* dispose command line arguments of session */ 105 | static void completion_freeCmdlineArgs(completion_Session *session) 106 | { 107 | /* free each command line arguments */ 108 | int i_arg = 0; 109 | for ( ; i_arg < session->num_args; i_arg++) { 110 | free(session->cmdline_args[i_arg]); 111 | } 112 | 113 | /* and dispose the arg vector */ 114 | free(session->cmdline_args); 115 | } 116 | 117 | /* Update command line arguments passing to clang translation unit. Format 118 | of the coming CMDLINEARGS message is as follows: 119 | 120 | num_args: [#n_args#] 121 | arg1 arg2 ... (there should be n_args items here) 122 | */ 123 | void completion_doCmdlineArgs(completion_Session *session, FILE *fp) 124 | { 125 | int i_arg = 0; 126 | char arg[LINE_MAX]; 127 | 128 | /* destroy command line args, and we will rebuild it later */ 129 | completion_freeCmdlineArgs(session); 130 | 131 | /* get number of arguments */ 132 | fscanf(fp, "num_args:%d", &(session->num_args)); __skip_the_rest(fp); 133 | session->cmdline_args = (char**)calloc(sizeof(char*), session->num_args); 134 | 135 | /* rebuild command line arguments vector according to the message */ 136 | for ( ; i_arg < session->num_args; i_arg++) 137 | { 138 | /* fetch an argument from message */ 139 | fscanf(fp, "%s", arg); 140 | 141 | /* and add it to cmdline_args */ 142 | session->cmdline_args[i_arg] = (char*)calloc(sizeof(char), strlen(arg) + 1); 143 | strcpy(session->cmdline_args[i_arg], arg); 144 | } 145 | 146 | /* we have to rebuild our translation units to make these cmdline args changes 147 | take place */ 148 | clang_disposeTranslationUnit(session->cx_tu); 149 | completion_parseTranslationUnit(session); 150 | completion_reparseTranslationUnit(session); /* dump PCH for acceleration */ 151 | } 152 | 153 | /* Handle syntax checking request, message format: 154 | source_length: [#src_length#] 155 | <# SOURCE CODE #> 156 | */ 157 | void completion_doSyntaxCheck(completion_Session *session, FILE *fp) 158 | { 159 | unsigned int i_diag = 0, n_diag; 160 | CXDiagnostic diag; 161 | CXString dmsg; 162 | 163 | /* get a copy of fresh source file */ 164 | completion_readSourcefile(session, fp); 165 | 166 | /* reparse the source to retrieve diagnostic message */ 167 | completion_reparseTranslationUnit(session); 168 | 169 | /* dump all diagnostic messages to fp */ 170 | n_diag = clang_getNumDiagnostics(session->cx_tu); 171 | for ( ; i_diag < n_diag; i_diag++) 172 | { 173 | diag = clang_getDiagnostic(session->cx_tu, i_diag); 174 | dmsg = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); 175 | fprintf(stdout, "%s\n", clang_getCString(dmsg)); 176 | clang_disposeString(dmsg); 177 | clang_disposeDiagnostic(diag); 178 | } 179 | 180 | fprintf(stdout, "$"); fflush(stdout); /* end of output */ 181 | } 182 | 183 | /* When emacs buffer is killed, a SHUTDOWN message is sent automatically by a hook 184 | function to inform the completion server (this program) to terminate. */ 185 | void completion_doShutdown(completion_Session *session, FILE *fp) 186 | { 187 | (void) fp; /* this parameter is unused, the server will shutdown 188 | * directly without sending any messages to its client */ 189 | 190 | /* free clang parser infrastructures */ 191 | clang_disposeTranslationUnit(session->cx_tu); 192 | clang_disposeIndex(session->cx_index); 193 | 194 | /* free session properties */ 195 | completion_freeCmdlineArgs(session); 196 | free(session->src_buffer); 197 | 198 | exit(0); /* terminate completion process */ 199 | } 200 | -------------------------------------------------------------------------------- /src/parse_results.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "completion.h" 7 | 8 | 9 | 10 | /* Print "COMPLETION: " followed by the TypedText chunk of the completion 11 | * string to fp, that's the text that a user would be expected to type to get 12 | * this code-completion result. TypedText is the keyword for the client program 13 | * (emacs script in this case) to filter completion results. 14 | * 15 | * This function returns the number of completion chunks on success, or it 16 | * would return an -1 if no TypedText chunk was found. 17 | */ 18 | static int completion_printCompletionHeadTerm( 19 | CXCompletionString completion_string, FILE *fp) 20 | { 21 | int i_chunk = 0; 22 | int n_chunks = clang_getNumCompletionChunks(completion_string); 23 | CXString ac_string; 24 | 25 | /* inspect all chunks only to find the TypedText chunk */ 26 | for ( ; i_chunk < n_chunks; i_chunk++) 27 | { 28 | if (clang_getCompletionChunkKind(completion_string, i_chunk) 29 | == CXCompletionChunk_TypedText) 30 | { 31 | /* We got it, just dump it to fp */ 32 | ac_string = clang_getCompletionChunkText(completion_string, i_chunk); 33 | fprintf(fp, "COMPLETION: %s", clang_getCString(ac_string)); 34 | clang_disposeString(ac_string); 35 | return n_chunks; /* care package on the way */ 36 | } 37 | } 38 | 39 | return -1; /* We haven't found TypedText chunk in completion_string */ 40 | } 41 | 42 | 43 | /* Print the completion line except the header term (COMPLETION: TypedText), 44 | * the output format should be identical with the result of clang -cc1 45 | * -code-completion-at. Here are some sample outputs from the clang code 46 | * completion process: 47 | 48 | COMPLETION: short 49 | COMPLETION: signed 50 | COMPLETION: static 51 | COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>) 52 | COMPLETION: struct 53 | 54 | * However, here we don't handle Pattern explicitly because the emacs 55 | * script would simply drop those pattern lines with an regexp T T 56 | */ 57 | static void completion_printAllCompletionTerms( 58 | CXCompletionString completion_string, FILE *fp) 59 | { 60 | int i_chunk = 0; 61 | int n_chunks = clang_getNumCompletionChunks(completion_string); 62 | 63 | CXString chk_text; 64 | enum CXCompletionChunkKind chk_kind; 65 | 66 | for ( ; i_chunk < n_chunks; i_chunk++) 67 | { 68 | /* get the type and completion text of this chunk */ 69 | chk_kind = clang_getCompletionChunkKind(completion_string, i_chunk); 70 | chk_text = clang_getCompletionChunkText(completion_string, i_chunk); 71 | 72 | /* differenct kinds of chunks has various output formats */ 73 | switch (chk_kind) 74 | { 75 | case CXCompletionChunk_Placeholder: 76 | fprintf(fp, "<#%s#>", clang_getCString(chk_text)); 77 | break; 78 | 79 | case CXCompletionChunk_ResultType: 80 | fprintf(fp, "[#%s#]", clang_getCString(chk_text)); 81 | break; 82 | 83 | case CXCompletionChunk_Optional: 84 | /* print optional term in a recursive way */ 85 | fprintf(fp, "{#"); 86 | completion_printAllCompletionTerms( 87 | clang_getCompletionChunkCompletionString(completion_string, i_chunk), 88 | fp); 89 | fprintf(fp, "#}"); 90 | break; 91 | 92 | default: 93 | fprintf(fp, "%s", clang_getCString(chk_text)); 94 | } 95 | 96 | clang_disposeString(chk_text); 97 | } 98 | } 99 | 100 | 101 | /* Print specified completion string to fp. */ 102 | void completion_printCompletionLine( 103 | CXCompletionString completion_string, FILE *fp) 104 | { 105 | /* print completion item head: COMPLETION: typed_string */ 106 | if (completion_printCompletionHeadTerm(completion_string, fp) > 1) 107 | { 108 | /* If there's not only one TypedText chunk in this completion string, 109 | * we still have a lot of info to dump: 110 | * 111 | * COMPLETION: typed_text : ##infos## 112 | */ 113 | fprintf(fp, " : "); 114 | completion_printAllCompletionTerms(completion_string, fp); 115 | } 116 | 117 | printf("\n"); 118 | } 119 | 120 | /* Print all completion results to fp */ 121 | void completion_printCodeCompletionResults(CXCodeCompleteResults *res, FILE *fp) 122 | { 123 | unsigned int i = 0; 124 | for ( ; i < res->NumResults; i++) { 125 | completion_printCompletionLine(res->Results[i].CompletionString, fp); 126 | } 127 | } 128 | --------------------------------------------------------------------------------