Download
(require 'cl)
(require 'etags)
(defvar cppsense-filename-hash (make-hash-table :test 'equal))
(defvar cppsense-debug nil "*Debug")
(defvar cppsense-last-cmd-time (current-time))
(setq cppsense-popup-threshold 0.3)
(defconst cppsense-symbol-regexp "\\(\\<\\(\\sw\\|\\s_\\)+\\>\\)")
(defvar cppsense-markers nil)
(defvar cppsense-completion-frame nil)
(defvar cppsense-selection-overlay nil)
(defvar cppsense-completions-buffer "*cpssense-completions*")
(defvar cppsense-enable-automatic-completion nil)
(defvar cppsense-completion-symbol-beginning-position nil)
(defvar cppsense-completion-editing-commands
'(self-insert-command
c-electric-backspace))
(defvar cppsense-header-extensions '("h" "hh"))
(defvar cppsense-source-extensions '("cc" "cpp" "icc"))
(defvar cppsense-include-regexp "#include \\(<\\|\"\\)\\(.*\\)\\(>\\|\"\\)")
(defvar cppsense-include-regexp-paren 2)
(defvar cppsense-symbol-cache (make-hash-table :test 'equal))
(defun cppsense-initialize ()
(interactive)
(if tags-file-name
(find-file-noselect tags-file-name)
(call-interactively 'visit-tags-table))
(message "Collecting file names from TAGS file...")
(clrhash cppsense-filename-hash)
(with-current-buffer (get-file-buffer tags-file-name)
(goto-char (point-min))
(while (search-forward "\f\n" nil t)
(looking-at "\\(.*\\),[0-9]+")
(let* ((file (match-string 1))
(name (file-name-nondirectory file))
(dir (file-name-directory file))
(directories (gethash name cppsense-filename-hash)))
(setq dir (substring dir 0 -1))
(puthash name
(if directories
(append directories (list dir))
(list dir))
cppsense-filename-hash))))
(message "Done."))
(add-hook 'c++-mode-hook 'cppsense-setup)
(add-hook 'c-mode-hook 'cppsense-setup)
(defun cppsense-setup ()
(if (eq (hash-table-count cppsense-filename-hash) 0)
(cppsense-initialize))
(if cppsense-enable-automatic-completion
(add-hook 'post-command-hook 'cppsense-try-automatic-completion nil t))
(local-set-key (kbd "<f1>") 'cppsense-do-something-clever)
(local-set-key (kbd "C-<f1>") 'cppsense-show-popup-help-for-symbol))
(defun cppsense-do-something-clever ()
(interactive)
(if (eq last-command 'cppsense-do-something-clever)
(pop-tag-mark)
(let ((origin (cppsense-create-point-marker)))
(unwind-protect
(progn
(setq cppsense-markers nil)
(some (lambda (func)
(funcall func))
'(cppsense-include-handler
cppsense-symbol-handler
cppsense-completion-handler
cppsense-nothing-handler)))
(cppsense-destroy-markers))
(require 'etags)
(ring-insert find-tag-marker-ring origin))))
(defun cppsense-show-popup-help-for-symbol ()
(interactive)
(cppsense-do-something-clever))
(defun cppsense-include-handler ()
(if (save-excursion
(beginning-of-line)
(looking-at cppsense-include-regexp))
(let* ((file (match-string cppsense-include-regexp-paren))
(path (cppsense-get-include-file-path file t)))
(if (not path)
(error "File %s not found." file)
(find-file path)
(message "File %s found." file)))))
(defun cppsense-get-include-file-path (file &optional check-current-dir)
(let* ((dirname (file-name-directory file))
(name (file-name-nondirectory file)))
(if (and check-current-dir
(file-readable-p name))
(expand-file-name file)
(let ((dirs (gethash name cppsense-filename-hash))
dir)
(when dirs
(setq dir
(if dirname
(progn
(setq dirname (substring dirname 0 -1))
(some (lambda (d)
(if (equal dirname
(file-name-nondirectory d))
d))
dirs))
(if (not (cdr dirs))
(car dirs)
(car dirs))))
(if dir
(concat dir "/" name)))))))
(defun cppsense-symbol-handler ()
(let* ((symbol-info (cppsense-get-symbol-info-at-point))
(marker (cdr (assoc 'declaration symbol-info))))
(when symbol-info
(if (not marker)
(error "Can't find declaration of symbol: %s"
(cdr (assoc 'symbol symbol-info)))
(if (eq this-command 'cppsense-show-popup-help-for-symbol)
(cppsense-show-popup-help (cppsense-get-code-context-from-marker marker))
(switch-to-buffer (marker-buffer marker))
(goto-char marker)))
(move-marker marker nil)
t)))
(defun cppsense-get-symbol-info-at-point ()
(when (and (not (eobp))
(let ((syntax (char-syntax (char-after))))
(or (eq syntax ?w)
(eq syntax ?_))))
(let* ((symbol-begin (cppsense-get-symbol-begin-at-point))
(symbol-end (cppsense-get-symbol-end-at-point))
(symbol (buffer-substring-no-properties symbol-begin symbol-end))
(marker (some (lambda (func)
(funcall func symbol symbol-begin symbol-end))
'(cppsense-find-symbol-with-heuristics
cppsense-lookup-symbol-in-tag-file))))
(cppsense-debug (concat "cppsense-get-symbol-info-at-point: "
"symbol:%s begin: %s end: %s marker: %s")
symbol symbol-begin symbol-end marker)
(list (cons 'symbol symbol)
(cons 'begin symbol-begin)
(cons 'end symbol-end)
(cons 'declaration marker)))))
(defun cppsense-get-declaration-marker-of-symbol-at-point ()
(assoc-default 'declaration (cppsense-get-symbol-info-at-point)))
(defun cppsense-find-symbol-with-heuristics (symbol symbol-begin symbol-end)
(if (cppsense-within-function)
(cppsense-find-symbol-within-function symbol symbol-begin symbol-end)
(if (cppsense-within-class)
(save-excursion
(c-beginning-of-defun)
(when (looking-at (concat "class\\s-*" cppsense-symbol-regexp))
(goto-char (match-beginning 1))
(let* ((members (cppsense-get-members-of-class
(cppsense-create-point-marker)))
(marker (if members
(some (lambda (member)
(if (equal (plist-get member 'name) symbol)
(plist-get member 'declaration)))
members))))
(if marker
(cppsense-get-member-function-definition symbol marker)))))
(cppsense-try-class symbol))))
(defun cppsense-find-symbol-within-function (symbol symbol-begin symbol-end)
(save-excursion
(if (save-excursion
(goto-char symbol-end)
(looking-at "("))
(if (cppsense-member-p symbol-begin)
(cppsense-get-qualified-member-function symbol)
(goto-char (point-min))
(when (re-search-forward (concat "::\\<\\(" symbol "\\)\\>(") nil t)
(goto-char (match-beginning 1))
(cppsense-debug (concat "cppsense-find-symbol-with-heuristics: "
"member function in same file found: %s")
symbol)
(cppsense-create-point-marker)))
(if (cppsense-member-p symbol-begin)
(cppsense-get-qualified-member-info-at-point symbol)
(c-beginning-of-defun)
(if (or (re-search-forward (concat "\\s-\\(" symbol "\\)[,)]")
(save-excursion
(re-search-forward "^{"))
t)
(re-search-forward (concat "^\\s-*\\S-.*\\s-\\<\\(" symbol "\\)\\>\\s-*\\(;\\|[,=\[].*;\\)")
(save-excursion
(c-end-of-defun)
(point))
t))
(progn (goto-char (or (match-beginning 1)
(match-beginning 0)))
(cppsense-debug (concat "cppsense-find-symbol-with-heuristics: "
"function arg or local variable found: %s")
symbol)
(cppsense-create-point-marker))
(or (cppsense-try-class symbol)
(progn
(cppsense-debug (concat "cppsense-find-symbol-with-heuristics: "
"assume symbol is an unqualified member variable: %s")
symbol)
(when (re-search-forward (concat cppsense-symbol-regexp "::")
(save-excursion
(search-forward "{" nil t)
(forward-char -1)
(forward-sexp)
(point))
t)
(goto-char (match-beginning 0))
(let ((decl (cppsense-get-declaration-marker-of-symbol-at-point)))
(if decl
(cppsense-get-member-of-class decl symbol)))))))))))
(defun cppsense-member-p (symbol-begin)
(or (and (eq (char-before symbol-begin) ?>)
(eq (char-before (1- symbol-begin)) ?-))
(and (eq (char-before symbol-begin) ?:)
(eq (char-before (1- symbol-begin)) ?:))
(eq (char-before symbol-begin) ?.)))
(defun cppsense-get-qualified-member-function (function)
(let ((marker (cppsense-get-qualified-member-info-at-point function)))
(if marker
(cppsense-get-member-function-definition function marker))))
(defun cppsense-get-member-function-definition (function marker)
(cppsense-debug "cppsense-get-member-function-definition: %s %s"
function marker)
(with-current-buffer (marker-buffer marker)
(save-excursion
(goto-char marker)
(c-beginning-of-defun)
(let ((class (cppsense-get-class-on-current-line)))
(if (not class)
(progn (message "Can't find class containing function %s" function)
marker)
(let* ((regexp (concat "\\<" class "::\\(" function "\\)\\s-*\\(\n\\s-*\\)?("))
(inline (save-excursion
(goto-char (point-min))
(if (re-search-forward regexp nil t)
(match-beginning 1)))))
(if inline
(progn
(goto-char inline)
(cppsense-create-point-marker))
(let ((source-file (cppsense-get-other-file)))
(if (not source-file)
(progn (message "Can't find source file for class %s" class)
marker)
(with-current-buffer (find-file-noselect
source-file)
(save-excursion
(goto-char (point-min))
(if (not (let (found)
(while (and (not found)
(re-search-forward regexp nil t))
(unless (save-match-data
(cppsense-within-function))
(setq found t)))
found))
(progn (cppsense-debug "Can't find function %s in source file of class %s"
function class)
marker)
(goto-char (match-beginning 1))
(cppsense-debug "Definition of function %s is found."
function)
(cppsense-create-point-marker)))))))))))))
(defun cppsense-get-qualified-member-info-at-point (membername)
(let ((parent-decl (cppsense-get-parent-class)))
(if parent-decl
(cppsense-get-member-of-class parent-decl membername))))
(defun cppsense-get-parent-class ()
(save-excursion
(cppsense-go-to-beginning-of-symbol)
(skip-syntax-backward ".")
(forward-char -1)
(let ((parent-decl (cppsense-get-declaration-marker-of-symbol-at-point)))
(when parent-decl
(with-current-buffer (marker-buffer parent-decl)
(save-excursion
(goto-char parent-decl)
(unless (cppsense-get-class-on-current-line)
(backward-word)
(setq parent-decl (cppsense-get-declaration-marker-of-symbol-at-point)))
parent-decl))))))
(defun cppsense-get-member-of-class (class-marker membername)
(let ((members (cppsense-get-members-of-class class-marker)))
(if members
(or (some (lambda (member)
(if (equal (plist-get member 'name) membername)
(plist-get member 'declaration)))
members)
(progn
(cppsense-debug (concat "cppsense-get-member-of-class: "
"member not found: %s")
membername)
nil)))))
(defun cppsense-get-members-of-class (marker &optional dont-garbage-collect-markers)
(with-current-buffer (marker-buffer marker)
(save-excursion
(goto-char marker)
(let ((class (cppsense-get-class-on-current-line)))
(if (not class)
(progn (message "Not a class or a struct: %s"
(cppsense-get-symbol-name marker))
nil)
(let* ((end (save-excursion
(search-forward "{" nil t)
(forward-char -1)
(forward-sexp)
(point)))
(member-regexp (concat cppsense-symbol-regexp
"\\(;"
"\\|"
"\\s-*("
"\\)"))
members)
(while (re-search-forward member-regexp end t)
(unless (or
(eq (get-text-property (point) 'face)
'font-lock-comment-face)
(equal (match-string 1) class)
(save-match-data
(save-excursion
(beginning-of-line)
(looking-at "\\s-*friend class"))))
(let* ((name (match-string-no-properties 1))
(declaration (save-excursion
(save-match-data
(goto-char (match-beginning 1))
(if dont-garbage-collect-markers
(point-marker)
(cppsense-create-point-marker)))))
(info (list 'name name 'declaration declaration)))
(when (save-excursion
(goto-char (match-end 1))
(looking-at "\\s-*("))
(let ((definition (cppsense-get-member-function-definition
name declaration)))
(if dont-garbage-collect-markers
(setq definition (copy-marker definition)))
(setq info (append info (list 'definition definition)))))
(push info members)))
(forward-line 1))
(cppsense-debug (concat "cppsense-get-members-of-class: "
"members found: %s") members)
members))))))
(defun cppsense-get-class-on-current-line ()
(save-excursion (beginning-of-line)
(if (looking-at (concat "\\s-*\\(class\\|struct\\)\\s-+"
cppsense-symbol-regexp))
(match-string 2))))
(defun cppsense-try-class (symbol)
(let* ((file (concat symbol ".hh"))
(dirs (gethash file cppsense-filename-hash)))
(if dirs
(with-current-buffer (find-file-noselect (concat (car dirs) "/" file))
(save-excursion
(goto-char (point-min))
(when (re-search-forward (concat "^\\(class\\|struct\\)\\s-+\\<\\("
symbol
"\\)\\>") nil t)
(goto-char (match-beginning 2))
(cppsense-debug (concat "cppsense-try-class: "
"class found: %s") symbol)
(cppsense-create-point-marker)))))))
(defun cppsense-lookup-symbol-in-tag-file (symbol symbol-begin symbol-end)
(let (file text line pos)
(with-current-buffer (get-file-buffer tags-file-name)
(save-excursion
(goto-char (point-min))
(when (let ((case-fold-search nil))
(search-forward (concat "\177" symbol "\001") nil t))
(setq text (buffer-substring-no-properties
(line-beginning-position) (match-beginning 0)))
(goto-char (match-end 0))
(looking-at "\\([0-9]+\\),\\([0-9]+\\)")
(setq line (string-to-int (match-string 1)))
(setq pos (string-to-int (match-string 2)))
(re-search-backward "\f\n\\([^\n]+\\),[0-9]*\n")
(setq file (match-string 1)))))
(when file
(save-excursion
(with-current-buffer (find-file-noselect file)
(etags-goto-tag-location (cons text (cons line pos)))
(cppsense-debug "Found symbol %s in tag file." symbol)
(cppsense-create-point-marker))))))
(defun cppsense-completion-handler ()
(if (not (bobp))
(let ((syntax (char-syntax (char-before))))
(when (or (eq syntax ?w)
(eq syntax ?_))
(save-excursion
(cppsense-go-to-beginning-of-symbol)
(setq cppsense-completion-symbol-beginning-position (point)))
(setq cppsense-completion-candidates nil)
(if (cppsense-member-p
cppsense-completion-symbol-beginning-position)
(let* ((parent-decl (cppsense-get-parent-class)))
(if parent-decl
(setq cppsense-completion-candidates
(list
(list 'header "Class Members"
'candidates
(cppsense-get-members-of-class parent-decl t))))))
(let ((class (cppsense-get-class-marker-for-containing-function)))
(setq cppsense-completion-candidates
(append (if class
(list (list 'header "Class Members"
'candidates
(cppsense-get-members-of-class
(assoc-default 'declaration class) t))))
(list (list 'header "Global Symbols"
'candidates (cppsense-get-include-data)))))))
(if (not cppsense-completion-candidates)
(message "No completions for symbol before point.")
(setq cppsense-completion-candidates
(mapcar (lambda (candidates-info)
(let ((sorted-candidates
(sort (copy-list (plist-get candidates-info 'candidates))
(lambda (first second)
(string< (downcase (plist-get first 'name))
(downcase (plist-get second 'name)))))))
(plist-put candidates-info 'candidates sorted-candidates)))
cppsense-completion-candidates))
(cppsense-show-completion-for-point))
t))))
(defun cppsense-show-completion-for-point ()
(let* ((maxlinelen
(apply
'max
(mapcar (lambda (candidates-info)
(apply 'max (mapcar (lambda (candidate)
(length (plist-get candidate 'name)))
(plist-get candidates-info 'candidates))))
cppsense-completion-candidates)))
(numofcandidates
(apply '+ (mapcar (lambda (candidates-info)
(length (plist-get candidates-info 'candidates)))
cppsense-completion-candidates)))
(width (max (+ maxlinelen 2)
15))
(height (max (min numofcandidates 20) 5))
(pixel-width (* width (frame-char-width)))
(pixel-height (*
(1+ height)
(frame-char-height)))
(position (cppsense-calculate-popup-position pixel-height
pixel-width))
(frame-params (list (cons 'top (cdr position))
(cons 'left (car position))
(cons 'width width)
(cons 'height height)
(cons 'minibuffer nil)
(cons 'menu-bar-lines 0)
(cons 'tool-bar-lines 0)
(cons 'title "Completions")))
(orig-frame (selected-frame)))
(if (not cppsense-completion-frame)
(setq cppsense-completion-frame (make-frame frame-params))
(modify-frame-parameters cppsense-completion-frame frame-params)
(make-frame-visible cppsense-completion-frame))
(select-frame cppsense-completion-frame)
(switch-to-buffer cppsense-completions-buffer)
(setq cursor-type nil)
(setq mode-line-format nil)
(set-window-fringes nil 0 0)
(if cppsense-selection-overlay
(move-overlay cppsense-selection-overlay (point-min) (point-min))
(setq cppsense-selection-overlay
(make-overlay (point-min) (point-min)))
(overlay-put cppsense-selection-overlay 'face 'highlight))
(redirect-frame-focus cppsense-completion-frame orig-frame)
(select-frame orig-frame)
(add-hook 'pre-command-hook 'cppsense-pre-command)
(add-hook 'post-command-hook 'cppsense-post-command))
(setq cppsense-saved-keys nil)
(dolist (binding `((,(kbd "<down>") . cppsense-next-line)
(,(kbd "<up>") . cppsense-previous-line)
(,(kbd "<next>") . cppsense-next-page)
(,(kbd "<prior>") . cppsense-previous-page)
(,(kbd "<ESC>") . cppsense-completion-cancel)
(,(kbd "<RET>") . cppsense-completion-insert-selection)))
(let ((key (car binding))
(command (cdr binding)))
(push (cons key (lookup-key (current-local-map) key))
cppsense-saved-keys)
(define-key (current-local-map) key command)))
(cppsense-update-completion-list)
(setq cppsense-completion-just-started t))
(defun cppsense-pre-command ()
(unless (or (eq this-command 'handle-switch-frame)
(memq this-command cppsense-completion-editing-commands)
(get this-command 'cppsense-allowed-during-completion))
(cppsense-completion-cleanup)))
(defun cppsense-post-command ()
(if cppsense-completion-just-started
(setq cppsense-completion-just-started nil)
(if (or (< (point) cppsense-completion-symbol-beginning-position)
(and (eq this-command 'self-insert-command)
(let ((syntax (char-syntax (char-before))))
(not (or (eq syntax ?w)
(eq syntax ?_))))))
(cppsense-completion-cleanup)
(if (memq this-command cppsense-completion-editing-commands)
(cppsense-update-completion-list)))))
(defun cppsense-update-completion-list ()
(let* ((orig-frame (selected-frame))
(filter (if (eq (selected-frame) cppsense-completion-frame)
""
(downcase (buffer-substring
cppsense-completion-symbol-beginning-position
(point)))))
(len (length filter)))
(unwind-protect
(progn
(select-frame cppsense-completion-frame)
(switch-to-buffer cppsense-completions-buffer)
(erase-buffer)
(dolist (candidates-info cppsense-completion-candidates)
(let ((header-inserted nil))
(dolist (candidate (plist-get candidates-info 'candidates))
(when (or (equal "" filter)
(string-match filter (plist-get candidate 'name)))
(unless header-inserted
(let ((start (point)))
(insert (plist-get candidates-info 'header) "\n")
(put-text-property start (point)
'face 'header-line))
(setq header-inserted t))
(let ((start (point)))
(insert (plist-get candidate 'name) "\n")
(put-text-property start (1+ start)
'cppsense-completion-candidate candidate))))))
(goto-char (point-min))
(cppsense-previous-line))
(select-frame orig-frame))))
(defun cppsense-completion-cleanup ()
(remove-hook 'pre-command-hook 'cppsense-pre-command)
(remove-hook 'post-command-hook 'cppsense-post-command)
(make-frame-invisible cppsense-completion-frame)
(dolist (binding cppsense-saved-keys)
(define-key (current-local-map) (car binding) (cdr binding)))
(dolist (candidates-info cppsense-completion-candidates)
(dolist (candidate (plist-get candidates-info 'candidates))
(dolist (elem candidate)
(if (markerp elem)
(move-marker elem nil)))))
(tooltip-hide)
(with-current-buffer cppsense-completions-buffer
(setq cursor-type t)
(kill-local-variable 'mode-line-format)))
(defun cppsense-mark-current-line ()
(let ((orig-frame (selected-frame)))
(unwind-protect
(progn
(select-frame cppsense-completion-frame)
(move-overlay cppsense-selection-overlay
(line-beginning-position)
(1+ (line-end-position))))
(select-frame orig-frame))))
(defun cppsense-next-line ()
(interactive)
(cppsense-move-selection (lambda () (forward-line 1))))
(put 'cppsense-next-line 'cppsense-allowed-during-completion t)
(defun cppsense-previous-line ()
(interactive)
(cppsense-move-selection (lambda () (forward-line -1))))
(put 'cppsense-previous-line 'cppsense-allowed-during-completion t)
(defun cppsense-next-page ()
(interactive)
(cppsense-move-selection (lambda ()
(condition-case nil
(scroll-up)
(end-of-buffer (goto-char (point-max)))))))
(put 'cppsense-next-page 'cppsense-allowed-during-completion t)
(defun cppsense-previous-page ()
(interactive)
(cppsense-move-selection (lambda ()
(condition-case nil
(scroll-down)
(beginning-of-buffer (goto-char (point-min)))))))
(put 'cppsense-previous-page 'cppsense-allowed-during-completion t)
(defun cppsense-move-selection (func)
(interactive)
(tooltip-hide)
(let ((orig-frame (selected-frame)))
(unwind-protect
(progn
(select-frame cppsense-completion-frame)
(let ((start (point)))
(funcall func)
(if (eobp)
(forward-line -1)
(if (bobp)
(forward-line 1)
(unless (get-text-property (line-beginning-position)
'cppsense-completion-candidate)
(if (> start (point))
(forward-line -1)
(forward-line 1))))))
(cppsense-mark-current-line))
(select-frame orig-frame)))
(with-current-buffer cppsense-completions-buffer
(unless (eq (overlay-start cppsense-selection-overlay)
(overlay-end cppsense-selection-overlay))
(let* ((candidate (get-text-property (line-beginning-position)
'cppsense-completion-candidate))
(marker (or (plist-get candidate 'definition)
(plist-get candidate 'declaration))))
(if (and marker
(sit-for 0.5))
(let ((orig-frame (selected-frame)))
(unwind-protect
(progn
(unless (markerp marker)
(setq marker
(with-current-buffer (find-file-noselect
(plist-get marker 'file))
(save-excursion
(goto-char (plist-get marker 'pos))
(search-forward (plist-get candidate 'name) nil t)
(cppsense-create-point-marker)))))
(select-frame cppsense-completion-frame)
(save-excursion
(end-of-line)
(cppsense-show-popup-help
(cppsense-get-code-context-from-marker marker))))
(select-frame orig-frame))))))))
(defun cppsense-completion-insert-selection ()
(interactive)
(let ((candidate (with-current-buffer cppsense-completions-buffer
(get-text-property (line-beginning-position)
'cppsense-completion-candidate))))
(when candidate
(let ((p (point)))
(cppsense-go-to-beginning-of-symbol)
(delete-region (point) p))
(insert (plist-get candidate 'name)))))
(defun cppsense-completion-cancel ()
(interactive))
(defun cppsense-try-automatic-completion ()
(if (and (or (eq this-command 'self-insert-command)
(eq this-command 'c-electric-backspace))
cppsense-completion-frame
(not (frame-visible-p cppsense-completion-frame))
(sit-for 0.5))
(cppsense-completion-handler)))
(defun cppsense-nothing-handler ()
(let ((other-file (cppsense-get-other-file)))
(if other-file
(find-file other-file)
(error "Can't find other file."))))
(defun cppsense-get-other-file ()
(let* ((file (file-name-nondirectory (buffer-file-name)))
(name (file-name-sans-extension file))
(ext (file-name-extension file))
(other-extensions (cond ((member ext cppsense-header-extensions)
cppsense-source-extensions)
((member ext cppsense-source-extensions)
cppsense-header-extensions)
(t (error "Unknown extension %s." ext)))))
(some (lambda (ext)
(let ((other-file (concat name "." ext)))
(if (file-readable-p other-file)
other-file
(let ((directories (gethash other-file
cppsense-filename-hash)))
(when directories
(if (second directories)
(message (concat "There are more than one possible candidates. "
"Choosing the first one.")))
(concat (car directories) "/" other-file))))))
other-extensions)))
(defun cppsense-completing-read (&rest args)
"Same as completing-read, but completes list of candidates immediately."
(let ((unread-command-events (cons ?\t unread-command-events)))
(apply 'completing-read args)))
(defun cppsense-debug (string &rest args)
(if cppsense-debug
(apply 'message string args)))
(defun cppsense-color-string-background (oldstr color)
(interactive)
(let ((prevpos 0)
pos
(str (copy-sequence oldstr))
(continue t))
(while continue
(setq pos (next-single-property-change prevpos 'face str))
(unless pos
(setq pos (length str))
(setq continue nil))
(let ((face (get-text-property prevpos 'face str)))
(put-text-property prevpos pos 'face
(list (cons 'background-color color)
(cons 'foreground-color (if face
(face-foreground face))))
str))
(setq prevpos pos))
str))
(defun cppsense-show-popup-help (message)
(let* ((old-propertize (symbol-function 'propertize))
(x-max-tooltip-size '(120 . 40))
(lines (split-string message "\n"))
(tooltip-width (* (frame-char-width)
(apply 'max (mapcar 'length lines))))
(tooltip-height (* (frame-char-height) (min (length lines)
(cdr x-max-tooltip-size))))
(xy (cppsense-calculate-popup-position tooltip-height tooltip-width))
(tooltip-frame-parameters (append `((left . ,(car xy))
(top . ,(cdr xy)))
tooltip-frame-parameters)))
(set-mouse-position (selected-frame) 0 100)
(fset 'propertize (lambda (string &rest properties)
string))
(unwind-protect
(tooltip-show message)
(fset 'propertize old-propertize))))
(defun cppsense-calculate-popup-position (height width)
(let* ((point-pos (posn-at-point))
(point-xy (posn-x-y point-pos))
(x (let ((x (+ (car point-xy) (frame-parameter nil 'left))))
(if (> (+ x width) (x-display-pixel-width))
(- (x-display-pixel-width) width 10)
x)))
(y (let* ((point-y (+ (cdr point-xy) (frame-parameter nil 'top)))
(y (- point-y height)))
(if (< y 0)
(+ point-y (* 4 (frame-char-height)))
y))))
(cons x y)))
(defun cppsense-truncate-path (path &optional length)
"If PATH is too long truncate some components from the beginning."
(let ((maxlength (if length
length
70)))
(if (<= (length path) maxlength)
path
(let* ((components (reverse (split-string path "/")))
(tmppath (car components)))
(setq components (cdr components))
(while (and components
(< (length tmppath) maxlength))
(setq path tmppath)
(setq tmppath (concat (car components)
"/"
tmppath))
(setq components (cdr components)))
(concat ".../" path)))))
(defun cppsense-within-function-or-class ()
(save-excursion
(while (and (not (bobp))
(not (eq (char-after (line-beginning-position)) ?{))
(not (eq (char-after (line-beginning-position)) ?})))
(forward-line -1))
(eq (char-after (line-beginning-position)) ?{)))
(defun cppsense-within-function ()
(and (cppsense-within-function-or-class)
(save-excursion
(c-beginning-of-defun)
(not (looking-at "class")))))
(defun cppsense-within-class ()
(and (cppsense-within-function-or-class)
(save-excursion
(c-beginning-of-defun)
(looking-at "class"))))
(defun cppsense-get-symbol-name (marker)
(with-current-buffer (marker-buffer marker)
(save-excursion
(goto-char marker)
(if (looking-at cppsense-symbol-regexp)
(match-string 1)
(cppsense-debug "cppsense-get-symbol-name: no match: %s"
marker)))))
(defun cppsense-get-symbol-begin (marker)
(with-current-buffer (marker-buffer marker)
(save-excursion
(goto-char marker)
(cppsense-get-symbol-begin-at-point))))
(defun cppsense-get-symbol-end (marker)
(with-current-buffer (marker-buffer marker)
(save-excursion
(goto-char marker)
(cppsense-get-symbol-end-at-point))))
(defun cppsense-get-symbol-begin-at-point ()
(save-excursion
(cppsense-go-to-beginning-of-symbol)
(point)))
(defun cppsense-go-to-beginning-of-symbol ()
(skip-syntax-backward "w_"))
(defun cppsense-get-symbol-end-at-point ()
(save-excursion
(cppsense-go-to-end-of-symbol)
(point)))
(defun cppsense-go-to-end-of-symbol ()
(skip-syntax-forward "w_"))
(defun cppsense-create-point-marker ()
(let ((marker (point-marker)))
(push marker cppsense-markers)
marker))
(defun cppsense-destroy-markers ()
(dolist (marker cppsense-markers)
(move-marker marker nil)))
(defun cppsense-get-code-context-from-marker (marker)
(with-current-buffer (marker-buffer marker)
(save-excursion
(goto-char marker)
(concat (cppsense-color-string-background
(concat
(cppsense-truncate-path (buffer-file-name))
":\n")
"moccasin")
"\n"
(buffer-substring (save-excursion
(forward-line -5)
(point))
(line-beginning-position))
(cppsense-color-string-background
(buffer-substring (line-beginning-position)
(1+ (line-end-position)))
"honeydew2")
(buffer-substring (1+ (line-end-position))
(save-excursion
(forward-line +5)
(point)))))))
(defun cppsense-get-class-marker-for-containing-function ()
(and (cppsense-within-function)
(save-excursion
(c-beginning-of-defun)
(when (re-search-forward (concat cppsense-symbol-regexp "::")
(save-excursion
(search-forward "{" nil t)
(forward-char -1)
(forward-sexp)
(point))
t)
(goto-char (match-beginning 0))
(cppsense-get-symbol-info-at-point)))))
(defun cppsense-get-symbols-for-file (file)
(let ((local-scopes (cppsense-get-local-scope-areas)))
(with-current-buffer (get-file-buffer tags-file-name)
(goto-char (point-min))
(let ((file-regexp (concat "\f\n\\(.*" file "\\),[0-9]*\n"))
symbols)
(if (re-search-forward file-regexp nil t)
(while (not (looking-at "\f"))
(if (looking-at ".*\177\\(.*\\)\001\\([0-9]+\\),\\([0-9]+\\)")
(let ((name (match-string-no-properties 1))
(pos (string-to-int (match-string 3))))
(unless (or (member-if (lambda (symbol)
(equal name (plist-get symbol 'name)))
symbols)
(some (lambda (scope)
(and (>= pos (car scope))
(<= pos (cdr scope))))
local-scopes))
(push (list 'name name
'declaration (list 'pos pos 'file file))
symbols))))
(forward-line 1))
(message "File not found in TAG file: %s" file))
symbols))))
(defun cppsense-get-local-scope-areas ()
(let (areas)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^{" nil t)
(push (cons (save-excursion
(let ((start (point)))
(c-beginning-of-defun)
(if (cppsense-get-class-on-current-line)
start
(point))))
(save-excursion
(c-end-of-defun)
(point)))
areas)))
areas))
(defun cppsense-get-include-data-internal ()
(let* ((expanded-path (expand-file-name (buffer-file-name)))
(symbols (gethash expanded-path cppsense-symbol-cache)))
(unless symbols
(setq symbols (cppsense-get-symbols-for-file expanded-path))
(save-excursion
(goto-char (point-min))
(while (re-search-forward cppsense-include-regexp nil t)
(let ((include (match-string-no-properties cppsense-include-regexp-paren)))
(unless (member include skip-includes)
(push include skip-includes)
(let ((path (cppsense-get-include-file-path include)))
(if path
(if (file-readable-p path)
(with-current-buffer (find-file-noselect path)
(setq symbols
(append symbols
(cppsense-get-include-data-internal))))
(message "Include file %s not readable." path))
(message "Include file %s not found." include)))))))
(puthash expanded-path symbols cppsense-symbol-cache))
symbols))
(defun cppsense-get-include-data ()
(let (skip-includes)
(cppsense-get-include-data-internal)))
(provide 'cppsense)