[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


From: Anton V. Belyaev
Subject: EmacsAssist
Date: 27 Mar 2006 10:15:20 -0800
User-agent: G2/0.2

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>

;; 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
;; 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.

;;; Commentary:

;; 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
in the
;;    format: return type, class, method name. You can select the
;;    moving to its line and press ENTER to jump to the method. You
also can
;;    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.

;; Usage:

;; 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.

;;; Changelog:

;; 27 mar 2006 -- Initial version 0.1 created.

;;; Code:

;; ================================== 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
                                             (lambda (i)
                                               (if (string= (car i) ext)
                                                     (if (null 
(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 ()
  (semantic-find-tags-by-class 'function
(semantic-something-to-tag-table eassist-buffer)))

(defun eassist-car-if-list (thing)
  (cond ((listp thing) (car thing))
        (t thing)))

(defun eassist-function-string-triplet (f)
   (eassist-car-if-list (semantic-tag-type f))
   (semantic-tag-function-parent f)
   (semantic-tag-name f)))

(defun eassist-format-triplets (f)
  (let ((return-width (reduce 'max (mapcar 'length (mapcar 'car f))
:initial-value 0))
        (class-width (reduce 'max (mapcar 'length (mapcar 'cadr f))
:initial-value 0))
        (name-width (reduce 'max (mapcar 'length (mapcar 'caddr f))
:initial-value 0)))
     (lambda (tri)
        ((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))
      (kill-buffer (current-buffer))
      (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)
  (apply 'string
          (lambda (ch)
            (let ((up (upcase ch)) (down (downcase ch)))
               ((= up down) (list ch))
               (t (list ?\[ up down ?\])))))
          (string-to-list reg))))

(defun eassist-search-string-updated ()
  (message eassist-search-string)
  (setq eassist-actual-methods
         '(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
eassist-search-string) 'hi-yellow)))

(defun eassist-key-pressed (key)
  (unhighlight-regexp (eassist-case-insensitive-regexp
  (setq eassist-search-string (concat eassist-search-string
(char-to-string key)))

(defun eassist-backspace-pressed ()
  (unhighlight-regexp (eassist-case-insensitive-regexp
  (setq eassist-search-string (eassist-string-without-last
eassist-search-string 1))

(defun eassist-make-key-function (key)
   `(lambda () (interactive) (eassist-key-pressed ,key)))

(defun eassist-escape ()
  (kill-buffer (current-buffer))
  (switch-to-buffer eassist-buffer))

(defvar eassist-mode-map
  (let ((map (make-sparse-keymap)))
    (suppress-keymap map)
    (do ((k (string-to-char "a") (+ 1 k))) ((> k (string-to-char "z")))
        (read-kbd-macro (char-to-string k))
        (eassist-make-key-function k)))
    (do ((k (string-to-char "A") (+ 1 k))) ((> k (string-to-char "Z")))
        (read-kbd-macro (char-to-string k))
        (eassist-make-key-function k)))
    (do ((k (string-to-char "0") (+ 1 k))) ((> k (string-to-char "9")))
        (read-kbd-macro (char-to-string k))
        (eassist-make-key-function 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
search string
  (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
'eassist-function-string-triplet method-tags))))
      (setq eassist-methods
            (mapcar* 'cons

(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

(provide 'eassist)

;;; eassist.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]