Download
(require 'eldoc)
(require 'cc-defs)
(require 'cl)
(eldoc-add-command 'c-electric-paren)
(unless (fboundp 'cache-make-cache)
(defun* cache-make-cache (init-fun test-fun cleanup-fun
&optional &key
(test #'eql)
(size 65)
(rehash-size 1.5)
(rehash-threshold 0.8)
(weakness nil))
"Creates a cached hash table. This is a hash table where
elements expire at some condition, as specified by init-fun and
test-fun. The three arguments do as follows:
init-fun is a function that is called when a new item is inserted
into the cache.
test-fun is a function that is called when an item in the cache
is looked up. It takes one argument, and will be passed the
result of init-fun that was generated when the item was inserted
into the cache.
cleanup-fun is called when an item is removed from the hash
table. It takes one argument, the value of the key-value pair
being deleted.
Note that values are only deleted from the cache when accessed.
This will return a list of 4 elements: a has table and the 3
arguments. All hash-table functions will work on the car of this
list, although if accessed directly the lookups will return a pair
(value, (init-fun)).
The keyword arguments are the same as for make-hash-table and are applied
to the created hash table."
(list (make-hash-table :test test
:size size
:rehash-size rehash-size
:rehash-threshold rehash-threshold
:weakness weakness) init-fun test-fun cleanup-fun))
(defun cache-gethash (key cache)
"Retrieve the value corresponding to key from cache."
(let ((keyval (gethash key (car cache) )))
(if keyval
(let ((val (car keyval))
(info (cdr keyval)))
(if (funcall (caddr cache) info)
(progn
(remhash key (car cache))
(funcall (cadddr cache) val)
nil)
val)))))
(defun cache-puthash (key val cache)
"Puts the key-val pair into cache."
(puthash key
(cons val (funcall (cadr cache)))
(car cache))))
(defvar c-eldoc-cpp-macro-arguments "-dD -w -P")
(defvar c-eldoc-cpp-normal-arguments "-w -P")
(defvar c-eldoc-cpp-command "/lib/cpp ")
(defvar c-eldoc-includes
"`pkg-config gtk+-2.0 --cflags` -I./ -I../ "
"List of commonly used packages/include directories - For
example, SDL or OpenGL. This shouldn't slow down cpp, even if
you've got a lot of them.")
(defvar c-eldoc-reserved-words
(list "if" "else" "switch" "while" "for" "sizeof")
"List of commands that eldoc will not check.")
(defvar c-eldoc-buffer-regenerate-time
120
"Time to keep a preprocessed buffer around.")
(defun c-eldoc-time-diff (t1 t2)
"Return the difference between the two times, in seconds.
T1 and T2 are time values (as returned by `current-time' for example)."
(time-to-seconds (subtract-time t1 t2)))
(defun c-eldoc-time-difference (old-time)
"Returns whether or not old-time is less than c-eldoc-buffer-regenerate-time seconds ago."
(> (c-eldoc-time-diff (current-time) old-time) c-eldoc-buffer-regenerate-time))
(defun c-eldoc-cleanup (preprocessed-buffer)
(kill-buffer preprocessed-buffer))
(defvar c-eldoc-buffers
(cache-make-cache #'current-time #'c-eldoc-time-difference #'c-eldoc-cleanup)
"Cache of buffer->preprocessed file used to speed up finding arguments")
(defun c-turn-on-eldoc-mode ()
"Enable c-eldoc-mode"
(interactive)
(set (make-local-variable 'eldoc-documentation-function)
'c-eldoc-print-current-symbol-info)
(turn-on-eldoc-mode))
(defun c-eldoc-get-buffer (function-name)
"Call the preprocessor on the current file"
(let ((output-buffer (cache-gethash (current-buffer) c-eldoc-buffers)))
(if output-buffer output-buffer
(let* ((this-name (concat "*" buffer-file-name "-preprocessed*"))
(preprocessor-command (concat c-eldoc-cpp-command " "
c-eldoc-cpp-macro-arguments " "
c-eldoc-includes " "
buffer-file-name))
(cur-buffer (current-buffer))
(output-buffer (generate-new-buffer this-name)))
(bury-buffer output-buffer)
(call-process-shell-command preprocessor-command nil output-buffer nil)
(setq preprocessor-command (concat c-eldoc-cpp-command " "
c-eldoc-cpp-normal-arguments " "
c-eldoc-includes " "
buffer-file-name))
(call-process-shell-command preprocessor-command nil output-buffer nil)
(cache-puthash cur-buffer output-buffer c-eldoc-buffers)
output-buffer))))
(defun c-eldoc-function-and-argument (&optional limit)
"Finds the current function and position in argument list."
(let* ((literal-limits (c-literal-limits))
(literal-type (c-literal-type literal-limits)))
(save-excursion
(when (eq literal-type 'string)
(goto-char (car literal-limits))
(setq literal-type nil))
(if literal-type
nil
(c-save-buffer-state ((argument-index 1))
(while (or (eq (c-forward-token-2 -1 t limit) 0)
(when (eq (char-before) ?\[)
(backward-char)
t))
(when (eq (char-after) ?,)
(setq argument-index (1+ argument-index))))
(c-backward-syntactic-ws)
(when (eq (char-before) ?\()
(backward-char)
(c-forward-token-2 -1)
(when (looking-at "[a-zA-Z_][a-zA-Z_0-9]*")
(cons (buffer-substring-no-properties
(match-beginning 0) (match-end 0))
argument-index))))))))
(defun c-eldoc-format-arguments-string (arguments index)
"Formats the argument list of a function."
(let ((paren-pos (string-match "(" arguments))
(pos 0))
(when paren-pos
(setq arguments (replace-regexp-in-string "\\\\?[[:space:]\\\n]"
" "
(substring arguments paren-pos))
arguments (replace-regexp-in-string "\\s-+" " " arguments)
arguments (replace-regexp-in-string " *, *" ", " arguments)
arguments (replace-regexp-in-string "( +" "(" arguments)
arguments (replace-regexp-in-string " +)" ")" arguments))
(while (and (> index 1)
pos
(not (string= (substring arguments (+ pos 2) (+ pos 6))
"...)")))
(setq pos (string-match "," arguments (1+ pos))
index (1- index)))
(when (and pos
(setq pos (string-match "[^ ,()]" arguments pos)))
(add-text-properties pos (string-match "[,)]" arguments pos)
'(face bold) arguments))
arguments)))
(defun c-eldoc-print-current-symbol-info ()
"Returns documentation string for the current symbol."
(let* ((current-function-cons (c-eldoc-function-and-argument (- (point) 1000)))
(current-function (car current-function-cons))
(current-function-regexp (concat "[ \t\n]+[*]*" current-function "[ \t\n]*("))
(current-macro-regexp (concat "#define[ \t\n]+[*]*" current-function "[ \t\n]*("))
(current-buffer (current-buffer))
(tag-buffer)
(function-name-point)
(arguments)
(type-face 'font-lock-type-face))
(when (and current-function
(not (member current-function c-eldoc-reserved-words)))
(when (setq tag-buffer (c-eldoc-get-buffer current-function))
(set-buffer tag-buffer)
(goto-char (point-min))
(prog1
(when (condition-case nil
(progn
(if (not (re-search-forward current-macro-regexp (point-max) t))
(re-search-forward current-function-regexp))
t)
(error (prog1 nil
(message "Function doesn't exist..."))))
(search-backward "(")
(c-skip-ws-backward)
(setq function-name-point (point))
(forward-sexp)
(setq arguments (buffer-substring-no-properties
function-name-point (point)))
(goto-char function-name-point)
(backward-char (length current-function))
(c-skip-ws-backward)
(setq function-name-point (point))
(search-backward-regexp "[};/#]" (point-min) t)
(if (= (char-after) ?#)
(let ((is-define (looking-at "#[[:space:]]*define"))
(preprocessor-point (point)))
(while (prog2 (end-of-line)
(= (char-before) ?\\)
(forward-char)))
(when (and is-define (> (point) function-name-point))
(goto-char preprocessor-point)
(setq type-face 'font-lock-preprocessor-face)))
(forward-char)
(when (looking-back "//")
(end-of-line)))
(c-skip-ws-forward)
(concat (propertize (buffer-substring-no-properties
(point)
function-name-point)
'face type-face)
" "
(propertize current-function
'face 'font-lock-function-name-face)
" "
(c-eldoc-format-arguments-string arguments
(cdr current-function-cons))))
(set-buffer current-buffer))))))
(provide 'c-eldoc)