[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Anton V. Belyaev
27 Mar 2006 10:15:20 -0800
Remember VisualAssist for VisualStudio? If you lack its convinient M-o,
M-m features in Emacs, EmacsAssist is for you. EmacsAssist is a C/C++
code navigator, allowing rapid navigation between class methods and
switch between header and body files (.h and .cpp).
Comments, critics, feature requests are welcome.
I am beginner in eLisp, so I would highly appreciate comments related
to Elisp usage and Emacs API usage (for example if something is written
ugly from point of view of experienced Elisp programmer).
;;; eassist.el --- EmacsAssist, C/C++ code navigator.
;; Copyright (C) 2006 Anton V. Belyaev
;; Author: Anton V. Belyaev <anton.belyaev at the gmail.com>
;; This file is *NOT* part of GNU Emacs.
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2 of
;; the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be
;; useful, but WITHOUT ANY WARRANTY; without even the implied
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE. See the GNU General Public License for more details.
;; You should have received a copy of the GNU General Public
;; License along with this program; if not, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA
;; Version: 0.1
;; Compatibility: Emacs 22, CEDET 1.0pre3.
;; Contains some useful functions features for C/C++ developers similar
;; those from VisualAssist. Remember that convinient M-o, M-g and M-m?
;; 1) Method navigaton.
;; When eassist-list-methods called when c/c++ body file buffer is
;; a new buffer is shown, containing list of methods and functions
;; format: return type, class, method name. You can select the
;; moving to its line and press ENTER to jump to the method. You
;; type a string in the buffer and method list will be reduced to
;; which contain the string as a substring. Nice highlight is
;; This function is recommended to be bound to M-m in c-mode.
;; 2) Header <-> Body file switch.
;; You can easily switch between body (c, cpp, cc...) and its
;; header file (h, hpp...) using eassist-switch-h-cpp. The file is
;; in the same directory. You can adjust body to header
;; customizing eassist-header-switches variable.
;; This function is recommended to be bound to M-o in c-mode.
;; EmacsAssist uses Semantic
;; EmacsAssist is defeloped for Semantics from CEDET 1.0pre3 package.
;; EmacsAssist works with current development (22) version of Emacs and
;; does not work with version 21.
;; 1) Install CEDET 1.0pre3 package for Emacs (if you dont have CEDET
;; 2) Copy eassist.el to your emacs/lisp folder.
;; 3) Add to your .emacs following line to load EmacsAssist:
;; (require 'eassist)
;; 4) Add convinient keymaps for fast EmacsAssist calls in c-mode:
;; (defun my-c-mode-common-hook ()
;; (define-key c-mode-base-map (kbd "M-o") 'eassist-switch-h-cpp)
;; (define-key c-mode-base-map (kbd "M-m") 'eassist-list-methods))
;; (add-hook 'c-mode-common-hook 'my-c-mode-common-hook)
;; 5) Open any C++ file with class definithion, press M-m. Try to type
;; any method name.
;; 6) Open any .cpp file. Press M-o. If there is .h or .hpp file in the
;; same folder, it will be opened.
;; 27 mar 2006 -- Initial version 0.1 created.
;; ================================== My STRING utils
(defun eassist-string-last (string n)
(substring string (- (length string) n)))
(defun eassist-string-without-last (string n)
(substring string 0 (max 0(- (length string) n))))
(defun eassist-string-ends-with (string end)
(string= end (eassist-string-last string (length end))))
;; ================================== My STRING utils end
;; ================================== CPP-H switch
;; Funcalls action until it is not nil.
;; Returns result of the last action.
(defun eassist-do-for-first-suitable (lst action)
(if (null lst)
(let ((res (funcall action (car lst))))
(if (null res)
(eassist-do-for-first-suitable (cdr lst) action)
(setq eassist-header-switches '(
("h" . ("cpp" "cc" "c"))
("hpp" . ("cpp"))
("cpp" . ("h" "hpp"))
("c" . ("h"))
("cc" . ("h" "hpp"))
(defun eassist-switch-h-cpp ()
(let ((ext (file-name-extension (buffer-file-name))))
(if (null (eassist-do-for-first-suitable eassist-header-switches
(if (string= (car i) ext)
(eassist-do-for-first-suitable (cdr i)
(message "There is no
corresponding pair (header or body)
(message "It is not a header or body file! See eassist-header-switches
(defun eassist-try-h-cpp (ext)
(concat (eassist-string-without-last (buffer-file-name) (length
(file-name-extension (buffer-file-name)))) ext)))
(defun eassist-find-if-exist (file)
(if (file-exists-p file)
(progn (find-file file) file)
;; ================================== CPP-H switch end
;; ================================== Method navigator
(defun eassist-functions ()
(defun eassist-car-if-list (thing)
(cond ((listp thing) (car thing))
(defun eassist-function-string-triplet (f)
(eassist-car-if-list (semantic-tag-type f))
(defun eassist-format-triplets (f)
(let ((return-width (reduce 'max (mapcar 'length (mapcar 'car f))
(class-width (reduce 'max (mapcar 'length (mapcar 'cadr f))
(name-width (reduce 'max (mapcar 'length (mapcar 'caddr f))
((cadr tri) (format (format "%%%ds %%%ds :: %%s\n" return-width
class-width) (car tri) (cadr tri) (caddr tri)))
( t (format (format "%%%ds %%%ds %%s\n" return-width
class-width) (car tri) "" (caddr tri)))))
(defun eassist-list-methods ()
(setq eassist-buffer (current-buffer))
(switch-to-buffer (get-buffer-create (concat (buffer-name
(current-buffer)) " method list")) t)
(defun eassist-jump-to-method ()
(let ((tag (cdr (nth (1- (line-number-at-pos))
(switch-to-buffer (semantic-tag-buffer tag) t)
(goto-char (semantic-tag-start tag))
(t (message "The line does not contain method description!")))))
(defun eassist-case-insensitive-regexp (reg)
(let ((up (upcase ch)) (down (downcase ch)))
((= up down) (list ch))
(t (list ?\[ up down ?\])))))
(defun eassist-search-string-updated ()
'(lambda (elt) (string-match eassist-search-string (car elt)))
(mapcar '(lambda (elt) (insert (car elt))) eassist-actual-methods)
(goto-line (/ (count-lines (point-min) (point-max)) 2))
((= 0 (length eassist-search-string)) nil)
(t (highlight-regexp (eassist-case-insensitive-regexp
(defun eassist-key-pressed (key)
(setq eassist-search-string (concat eassist-search-string
(defun eassist-backspace-pressed ()
(setq eassist-search-string (eassist-string-without-last
(defun eassist-make-key-function (key)
`(lambda () (interactive) (eassist-key-pressed ,key)))
(defun eassist-escape ()
(let ((map (make-sparse-keymap)))
(do ((k (string-to-char "a") (+ 1 k))) ((> k (string-to-char "z")))
(read-kbd-macro (char-to-string k))
(do ((k (string-to-char "A") (+ 1 k))) ((> k (string-to-char "Z")))
(read-kbd-macro (char-to-string k))
(do ((k (string-to-char "0") (+ 1 k))) ((> k (string-to-char "9")))
(read-kbd-macro (char-to-string k))
(define-key map (kbd "<RET>") 'eassist-jump-to-method)
(define-key map (kbd "<backspace>") 'eassist-backspace-pressed)
(define-key map (kbd "<ESC>") 'eassist-escape)
"Keymap for `eassist-mode'.")
(defun eassist-mode-init ()
(make-local-variable 'eassist-search-string) ;; current method
(make-local-variable 'eassist-methods) ;; (<formatted method
string> . <semantic method tag>)
(make-local-variable 'eassist-actual-methods) ;; subset of
eassist-methods that contain eassist-search string in the method string
(setq eassist-search-string "")
(let ((method-tags (eassist-functions)))
(let ((method-strings (eassist-format-triplets (mapcar
(define-derived-mode eassist-mode nil "Eassist methods"
"EmacsAssist method selection mode.
Turning on Text mode runs the normal hook `eassist-mode-hook'."
;; ================================== Method navigator end
;;; eassist.el ends here
Anton V. Belyaev <=