[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [bug-cflow] another non-bug for the list: I've extended the emacs su
From: |
John Sturdy |
Subject: |
Re: [bug-cflow] another non-bug for the list: I've extended the emacs support |
Date: |
Thu, 3 Feb 2011 12:24:57 +0000 |
On Thu, Feb 3, 2011 at 9:00 AM, Sergey Poznyakoff <address@hidden> wrote:
> Hi John,
>
>> I've added some new commands to the emacs support for cflow, should I
>> post the code here?
>
> Yes, please!
>
>> It uses outline minor mode to do folding, and has the option of
>> continuous display (in another window) of the corresponding code files
>> as you go through the cflow output.
>
> That's a very useful functionality. Thanks for your efforts.
You're welcome! Here it is (and also as an attachment, in case
mailers spoil it):
;;; cflow.el --- major mode for viewing cflow output files.
;; Authors: 1994, 1995, 2005, 2007 Sergey Poznyakoff; 2010 John Sturdy
;; Version: 0.3
;; Keywords: cflow
;; $Id$
;; This file is part of GNU cflow
;; Copyright (C) 1994, 1995, 2005, 2007, 2010 Sergey Poznyakoff and
2010 John Sturdy
;; GNU cflow 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 3 of the License, or
;; (at your option) any later version.
;; GNU cflow 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 GNU cflow; if not, write to the Free Software Foundation,
;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
;; Installation:
;; You may wish to use precompiled version of the module. To create it
;; run:
;; emacs -batch -f batch-byte-compile cflow-mode.el
;; Install the file cflow-mode.elc (and, optionally, cflow-mode.el) to
;; any directory in your Emacs load-path.
;; Customization:
;; To your .emacs or site-start.el add:
;; (autoload 'cflow-mode "cflow-mode")
;; (setq auto-mode-alist (append auto-mode-alist
;; '(("\\.cflow$" . cflow-mode))))
;; The variable `cflow-display-functions' controls whether the
;; function on the current line is displayed in another window. If it
;; is set to the symbol `narrowed', which is the default, the buffer
;; in that window is narrowed to display only that function.
;; The variable `cflow-next-previous-functions-only' being non-nil
;; makes the mode's motion commands move only onto functions defined
;; in the files mentioned in the cflow file.
;; The variable `cflow-backtrack-invisible' controls whether the mode
;; offers to go back to a section that has been folded away using
;; `outline-minor-mode', on reaching the end of a subtree.
;; Furthermore, if it is set to the symbol `no-ask', it does so
;; without asking.
(eval-when-compile
;; We use functions from these modules
(mapcar 'require '(font-lock)))
(defvar cflow-display-functions 'narrowed
"Whether to display each function that we move onto.
If it is the symbol `narrowed', narrow to just that function.")
(defvar cflow-next-previous-functions-only t
"Whether the `next' and `previous' movements should go to our next function.")
(defvar cflow-backtrack-invisible 'no-ask
"*Whether to go back to folded sections on reaching the end of a subtree.
If it is the symbol `no-ask', do this without asking.")
(defvar cflow-function-regexp "\\([^ \t:]+\\):\\([0-9]+\\)"
"Pattern for a default function in cflow output.")
(defvar cflow-new-function-regexp (concat cflow-function-regexp
">\\(?: (R)\\)?:?$")
"Pattern for the first occurrence of a default function in cflow output.")
(defvar cflow-mode-syntax-table nil
"Syntax table used in cflow-mode buffers.")
(unless cflow-mode-syntax-table
(setq cflow-mode-syntax-table (make-syntax-table))
(modify-syntax-entry ?\# "<" cflow-mode-syntax-table)
(modify-syntax-entry ?\n ">" cflow-mode-syntax-table))
(defvar cflow-mode-map (make-sparse-keymap)
"Keymap used in Cflow mode.")
(define-key cflow-mode-map "s" 'cflow-find-function)
(define-key cflow-mode-map "o" 'cflow-find-function-other-window)
(define-key cflow-mode-map "d" 'cflow-display-function-other-window)
(define-key cflow-mode-map "1" 'delete-other-windows)
(define-key cflow-mode-map "b" 'cflow-backward-same-level)
(define-key cflow-mode-map "f" 'cflow-forward-same-level)
(define-key cflow-mode-map "u" 'cflow-up-level)
(define-key cflow-mode-map " " 'cflow-scroll-other-window)
(define-key cflow-mode-map "\d" 'cflow-scroll-other-window-down)
(define-key cflow-mode-map "r" 'cflow-recursion-root)
(define-key cflow-mode-map "R" 'cflow-recursion-next)
(define-key cflow-mode-map "x" 'cflow-goto-expand)
(define-key cflow-mode-map "?" 'cflow-get-stack)
(define-key cflow-mode-map "E" 'cflow-edit-out-full)
(define-key cflow-mode-map "\t" 'cflow-hide-or-show-subtree)
(define-key cflow-mode-map "n" 'cflow-next-line)
(define-key cflow-mode-map "p" 'cflow-previous-line)
(define-key cflow-mode-map [menu-bar] (make-sparse-keymap))
(define-key cflow-mode-map [menu-bar cflow]
(cons "Cflow" (make-sparse-keymap "Cflow")))
(define-key cflow-mode-map [menu-bar cflow cflow-recursion-next]
'("Recursion next" . cflow-recursion-next))
(define-key cflow-mode-map [menu-bar cflow cflow-recursion-root]
'("Recursion root" . cflow-recursion-root))
(define-key cflow-mode-map [menu-bar cflow cflow-goto-expand]
'("Find expansion" . cflow-goto-expand))
(define-key cflow-mode-map [menu-bar cflow cflow-find-function]
'("Find function" . cflow-find-function))
;; Toggle visibility of subtree
(defun cflow-hide-or-show-subtree ()
"Toggle visibility of the subtree starting on the current line.
Uses function `outline-minor-mode'."
(interactive)
(unless outline-minor-mode
(outline-minor-mode 1))
(if (get-char-property (line-end-position) 'invisible)
(show-subtree)
(hide-subtree)))
(defun cflow-backward-to-function ()
"Move backward to a function line, if not already on one."
(when cflow-next-previous-functions-only
(if (re-search-backward cflow-new-function-regexp (point-min) t)
(back-to-indentation)
(error "No previous function"))))
(defun cflow-previous-line ()
"Move to the next line.
If `cflow-display-functions' is non-nil, display the function on this line,
if there is one."
(interactive)
(cflow-backward-to-function)
(when cflow-display-functions
(cflow-display-function-other-window t)))
(defun cflow-level ()
"Return the indentation level."
(save-excursion
(beginning-of-line)
(if (re-search-forward outline-regexp (point-max) t)
(current-column)
0)))
(defun cflow-forward-to-function ()
"Move forward to the next function line, if not already on one."
(when cflow-next-previous-functions-only
(if (re-search-forward cflow-new-function-regexp (point-max) t)
(back-to-indentation)
(error "No next function"))))
(defun cflow-next-line ()
"Move to the next line.
If `cflow-display-functions' is non-nil, display the function on this line,
if there is one."
(interactive)
(let ((old-level (cflow-level)))
(next-line 1)
(cflow-forward-to-function)
(when (< (cflow-level) old-level)
(let ((previous-invisibility-change
(previous-single-char-property-change (point) 'invisible)))
(when (and previous-invisibility-change
(>= (save-excursion
(goto-char previous-invisibility-change)
(cflow-level))
old-level)
(or (eq cflow-backtrack-invisible 'no-ask)
(and cflow-backtrack-invisible
(y-or-n-p "Backtrack to folded section? "))))
(goto-char (previous-single-char-property-change
previous-invisibility-change 'invisible))
(back-to-indentation)
(save-excursion
(show-subtree))
(message "Backtracked to previously folded subtree"))))
(when cflow-display-functions
(cflow-display-function-other-window t))))
(defun cflow-backward-same-level ()
"Move to the previous line at the same level.
If `cflow-display-functions' is non-nil, display the function on this line,
if there is one."
(interactive)
(outline-backward-same-level 1)
(back-to-indentation)
(cflow-backward-to-function)
(when cflow-display-functions
(cflow-display-function-other-window t)))
(defun cflow-forward-same-level ()
"Move to the next line at the same level.
If `cflow-display-functions' is non-nil, display the function on this line,
if there is one."
(interactive)
(outline-forward-same-level 1)
(back-to-indentation)
(cflow-forward-to-function)
(when cflow-display-functions
(cflow-display-function-other-window t)))
(defun cflow-up-level ()
"Move to the previous line at the next level up.
If `cflow-display-functions' is non-nil, display the function on this line,
if there is one."
(interactive)
(outline-up-heading 1)
(back-to-indentation)
(when cflow-display-functions
(cflow-display-function-other-window t)))
(defun cflow-scroll-other-window ()
"Scroll the other window.
If it is at the end of its buffer, move to the next listed function."
(interactive)
(let ((source-window (condition-case evar
(other-window-for-scrolling)
(error nil))))
(if source-window
(let ((source-position (window-start source-window)))
(scroll-other-window)
(when (= source-position (window-start source-window))
(cflow-next-line)))
(when cflow-display-functions
(cflow-display-function-other-window t)))))
(defun cflow-scroll-other-window-down ()
"Scroll the other window down.
If it is at the start of its buffer, move to the previous listed function."
(interactive)
(let ((source-window (condition-case evar
(other-window-for-scrolling)
(error nil))))
(if source-window
(let ((source-position (window-start source-window)))
(scroll-other-window-down nil)
(when (= source-position (window-start source-window))
(cflow-previous-line)))
(when cflow-display-functions
(cflow-display-function-other-window t)))))
(defun cflow-get-stack (&optional display)
"Return the statically calculated stack.
Called interactively, or with arg DISPLAY, display it in the message buffer."
(interactive (list t))
(let ((stack nil))
(save-excursion
(while
(condition-case evar
(progn
(outline-up-heading 1)
(back-to-indentation)
(push (symbol-at-point) stack)
t)
(error nil))))
(when display
(message "Calls: %s" (mapconcat 'symbol-name stack " ==> ")))
stack))
;; Find the function under cursor.
;; Switch to the proper buffer and go to the function header
(defun cflow-find-function ()
"Find the function on the current line."
(interactive)
(let ((lst (cflow-find-default-function)))
(cond
(lst
(switch-to-buffer (find-file-noselect (car lst)))
(goto-line (car (cdr lst))))
(t
(error "No source/line information for this function")))))
(defun cflow-find-function-other-window ()
"Find the function on the current line, in the other window."
(interactive)
(let ((lst (cflow-find-default-function)))
(cond
(lst
(switch-to-buffer-other-window (find-file-noselect (car lst)))
(goto-line (car (cdr lst))))
(t
(error "No source/line information for this function")))))
(defun cflow-display-function-other-window (&optional no-error)
"Display the function on the current line, in the other window.
Optional argument NO-ERROR means not to complain if the line has no function."
(interactive)
(let ((lst (cflow-find-default-function)))
(cond
(lst
(let* ((cflow-window (selected-window))
(pop-up-windows (eq cflow-window (next-window))))
(pop-to-buffer (find-file-noselect (car lst)) t)
(widen)
(goto-line (car (cdr lst)))
(when (eq cflow-display-functions 'narrowed)
(narrow-to-defun))
(select-window cflow-window)))
(t
(unless no-error
(error "No source/line information for this function"))))))
(defun cflow-find-default-function ()
"Parse a cflow listing line.
Return (list SOURCE-NAME LINE-NUMBER)"
(save-excursion
(beginning-of-line)
(cond
((re-search-forward cflow-function-regexp
(save-excursion (end-of-line) (point))
t)
(list
(buffer-substring (match-beginning 1) (match-end 1))
(string-to-number
(buffer-substring (match-beginning 2) (match-end 2)))))
(t
nil))))
;; If the cursor stays on a recursive call, then go to the root of
;; this call
(defun cflow-recursion-root ()
(interactive)
(let ((num (cond
((save-excursion
(beginning-of-line)
(re-search-forward "(recursive: see \\([0-9]+\\))"
(save-excursion (end-of-line) (point))
t))
(string-to-number
(buffer-substring (match-beginning 1) (match-end 1))))
(t
0))))
(cond
((> num 0)
(push-mark)
(goto-line num))
(t
(error "Not a recursive call")))))
(defun cflow-recursion-next ()
"Go to next recursive call"
(interactive)
(save-excursion
(beginning-of-line)
(cond
((re-search-forward "(R)"
(save-excursion (end-of-line) (point)) t)
(setq cflow-recursion-root-line (count-lines 1 (point))))))
(cond
((null cflow-recursion-root-line)
(error "No recursive functions"))
(t
(let ((pos (save-excursion
(next-line 1)
(re-search-forward
(concat "(recursive: see "
(number-to-string cflow-recursion-root-line)
")")
(point-max)
t))))
(cond
((null pos)
(goto-line cflow-recursion-root-line)
(error "no more calls."))
(t
(push-mark)
(goto-char pos)
(beginning-of-line)))))))
(defun cflow-goto-expand ()
(interactive)
(let ((num (cond
((save-excursion
(beginning-of-line)
(re-search-forward "\\[see \\([0-9]+\\)\\]"
(save-excursion (end-of-line) (point))
t))
(string-to-number
(buffer-substring (match-beginning 1) (match-end 1))))
(t
0))))
(cond
((> num 0)
(push-mark)
(goto-line num))
(t
(error "not expandable")))))
(defvar cflow-read-only)
(defun cflow-edit-out-full ()
"Get out of Cflow mode, leaving Cflow file buffer in fundamental mode."
(interactive)
(if (yes-or-no-p "Should I let you edit the whole Cflow file? ")
(progn
(setq buffer-read-only cflow-read-only)
(fundamental-mode)
(message "Type 'M-x cflow-mode RET' once done"))))
;; Font-lock stuff
(defconst cflow-font-lock-keywords
(eval-when-compile
(list
(cons "^\\s *[0-9]+" font-lock-constant-face)
(list "\\(\\S +\\)()\\s +\\(<[^>]*>\\)"
'(1 font-lock-function-name-face)
'(2 font-lock-type-face))
(list "\\(\\S +\\)\\s +\\(<[^>]*>\\)"
'(1 font-lock-variable-name-face)
'(2 font-lock-type-face))
(cons "\\S +()$" font-lock-builtin-face)
(cons "(R):?$" font-lock-comment-face)
(cons "(recursive: see [0-9]+)" font-lock-comment-face)
(cons "^[ \\t+-|\\]+" font-lock-keyword-face))))
;;;###autoload
(defun cflow-mode ()
"Major mode for viewing cflow output files.
\\<cflow-mode-map>
`\\[cflow-next-line]' and `\\[cflow-previous-line]' move to next and
previous functions.
`\\[cflow-forward-same-level]' and `\\[cflow-backward-same-level]'
move forward and backward at the same level,
and `\\[cflow-up-level]' goes up a level;
`\\[cflow-hide-or-show-subtree]' toggles hiding the subtree at point.
If the variable `cflow-display-functions' is non-nil, the current function is
displayed in another window after each movement, and
`\\[cflow-scroll-other-window]' and
`\\[cflow-scroll-other-window-down]' scroll
the other window (and on to the next function on reaching the end of it),
and `\\[delete-other-windows]' closes the other window.
If `cflow-display-functions' is set to the symbol 'narrowed, the
window is narrowed to just that function.
Setting the variable `cflow-backtrack-invisible' non-nil will ask
to reveal, and continue from, a previous hidden section within a
level, on reaching the end of the level. If set to the symbol
'no-ask, it will do it without asking.
`\\[cflow-get-stack]' shows the call stack needed to get to the current line.
Key bindings are:
\\{cflow-mode-map}"
(interactive)
(kill-all-local-variables)
(use-local-map cflow-mode-map)
(setq major-mode 'cflow-mode
mode-name "Cflow")
(make-variable-buffer-local 'cflow-recursion-root-line)
(set (make-local-variable 'cflow-read-only) buffer-read-only)
(setq buffer-read-only t)
(set-default 'cflow-recursion-root-line nil)
(set-syntax-table cflow-mode-syntax-table)
(make-local-variable 'font-lock-defaults)
(setq font-lock-defaults
'((cflow-font-lock-keywords) nil t
(("+-*/.<>=!?$%_&~^:" . "w"))
beginning-of-line)
outline-regexp " +")
(save-excursion
;; Because " *" doesn't work as `outline-regexp', we set it to "
;; +", and so to make the top of the tree be a proper node, we'll
;; force it to match `outline-regexp'
(goto-char (point-min))
(unless (looking-at " ")
(let ((buffer-read-only nil))
(insert " ")
(put-text-property (point-min) (1+ (point-min)) 'invisible t)))))
(provide 'cflow-mode)
;;; cflow-mode ends here
cflow-mode.el
Description: Text Data