1 ;;; cogre-uml.el --- UML support for COGRE
3 ;;; Copyright (C) 2001, 2002, 2003, 2004, 2007 Eric M. Ludlam
5 ;; Author: Eric M. Ludlam <zappo@gnu.org>
7 ;; X-RCS: $Id: uml-create.el,v 1.1 2007-11-26 15:04:26 michaels Exp $
9 ;; This file is not part of GNU Emacs.
11 ;; This is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This software is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
28 ;; Routines used to create UML diagrams from Semantic generated reverse
29 ;; engineered token databases.
34 (require 'semanticdb-find)
37 (defclass cogre-semantic-uml-graph (cogre-graph)
39 "This graph is for semantic oriented UML diagrams.")
41 (defmethod cogre-insert-class-list ((graph cogre-semantic-uml-graph))
42 "Return a list of classes GRAPH will accept."
43 (append (eieio-build-class-alist 'cogre-link)
44 (eieio-build-class-alist 'cogre-semantic-class)
45 (eieio-build-class-alist 'cogre-package)))
47 (defclass cogre-semantic-class (cogre-class)
49 "A Class node linked to semantic parsed buffers.
50 Inherits from the default UML class node type, and adds user
51 interfacing which links working with this node directly to source
54 (defmethod cogre-save ((graph cogre-semantic-uml-graph))
55 "Save the current GRAPH."
56 ;; Autogenerated graphcs have semantic tags in them which are often
57 ;; linked via overlay into a buffer. We need to write something
58 ;; special to unlink (clone?) those tags so they are saveable.
59 ;;(error "You cannot save autogenerated graphs")
61 ;; Doing this allows the graph to be saved. Some bugs in saving
62 ;; these graphs have been made. Try it out for a while.
66 (defmethod initialize-instance ((this cogre-semantic-class) &optional fields)
67 "When interactively creating a class node THIS, query for the class name.
68 Optional argument FIELDS are not used."
70 (if (string-match "^Class[0-9]*" (oref this object-name))
71 ;; In this case, we have a default class object-name, so try and query
72 ;; for the real class (from sources) which we want to use.
73 (let* ((class (or (oref this class) (cogre-read-class-name)))
74 (tag (if (semantic-tag-p class)
77 (semanticdb-strip-find-results
78 (semanticdb-brute-deep-find-tags-by-name class)
82 ;; We need to clone the tag to unlink our storage from any
83 ;; buffer it may be associated with.
84 (setq tag (semantic-tag-copy tag nil t)))
86 (if (semantic-tag-p class) (setq class (semantic-tag-name class)))
87 (if (and tag (eq (semantic-tag-class tag) 'type)
88 (or (string= (semantic-tag-type tag) "class")
89 (string= (semantic-tag-type tag) "struct")))
90 (let ((slots (semantic-tag-type-members tag))
91 (extmeth (semantic-tag-external-member-children tag t))
96 ;; A plain string, a simple language, just do attributes.
97 ((stringp (car slots))
98 (setq attrib (cons (list (car slots) 'variable nil)
101 ;; Variable decl is an attribute
102 ((eq (semantic-tag-class (car slots)) 'variable)
103 (setq attrib (cons (car slots) attrib)))
104 ;; A function decle is a method.
105 ((eq (semantic-tag-class (car slots)) 'function)
106 (setq method (cons (car slots) method)))
108 (setq slots (cdr slots)))
109 ;; Add in all those extra methods
111 (let ((sl (cdr (car extmeth))))
113 (if (eq (semantic-tag-class (car sl)) 'function)
114 (setq method (cons (car sl) method)))
116 (setq extmeth (cdr extmeth)))
117 ;; Put them into the class.
118 (oset this object-name class)
119 (oset this class tag)
120 (oset this attributes (nreverse attrib))
121 (oset this methods (nreverse method))
124 ;; We couldn't find a semantic tag for this class, so just
125 ;; put the name in there.
126 (cond ((stringp class)
127 (oset this object-name class))
129 (stringp (car class)))
130 (oset this object-name (car class)))
132 (oset this class nil)
133 (oset this attributes nil)
134 (oset this methods nil)
138 ;; Saving such graphs is not good! We can't reliably restore the overlays
139 ;; since we should switch to the originating buffer for every one! Yuck!
141 ;; (defmethod cogre-element-pre-serialize ((node cogre-semantic-class))
142 ;; "Prepare the current NODE to be serialized.
143 ;; Deoverlay all semantic tokens referenced."
144 ;; (call-next-method)
145 ;; (semantic-deoverlay-list (oref node class))
146 ;; (semantic-deoverlay-list (oref node attributes))
147 ;; (semantic-deoverlay-list (oref node methods))
150 ;; (defmethod cogre-element-post-serialize ((node cogre-semantic-class))
151 ;; "Restore overlays in NODE after being loaded from disk.
152 ;; Also called after a graph was saved to restore all objects.
153 ;; Reverses `cogre-graph-pre-serialize'."
154 ;; (call-next-method)
155 ;; (semantic-overlay-list (oref node class))
156 ;; (semantic-overlay-list (oref node attributes))
157 ;; (semantic-overlay-list (oref node methods))
160 (defcustom cogre-token->uml-function 'semantic-uml-abbreviate-nonterminal
161 "Function to use to create strings for tokens in CLASS nodes."
163 :type semantic-format-tag-functions)
166 (defmethod cogre-uml-stoken->uml ((class cogre-semantic-class) stoken &optional text)
167 "For CLASS convert a Semantic token STOKEN into a uml definition.
168 Optional TEXT property is passed down."
169 ;; We need to disable images because our diagram is still
171 (let ((semantic-format-use-images-flag nil))
172 (call-next-method class stoken
174 (let ((tb (or (semantic-tag-buffer stoken)
175 (semantic-tag-buffer (oref class class)))))
176 (if tb (set-buffer tb))
177 (funcall cogre-token->uml-function
183 (defmethod cogre-entered ((class cogre-semantic-class) start end)
184 "Method called when the cursor enters CLASS.
185 START and END cover the region with the property."
186 (cogre-uml-source-display class (point))
189 (defmethod cogre-left ((class cogre-semantic-class) start end)
190 "Method called when the cursor exits CLASS.
191 START and END cover the region with the property."
196 ;; Manage the display of the source buffer somewhere near the class diagram
198 (defcustom cogre-uml-source-display-method
199 'cogre-uml-source-display-bottom
200 "A Function called to display a source buffer associated with a Graph.
201 This function can be anything, or nil, though the following options
203 `cogre-uml-source-display-bottom' - in a window on the bottom of the frame.
204 `cogre-uml-source-display-top' - in a window on the top of the frame.
205 The function specified must take a `point-marker' to specify the
206 location that is to be displayed."
208 :type '(choice (const 'cogre-uml-source-display-bottom)
209 (const 'cogre-uml-source-display-top)
212 (defcustom cogre-uml-browse-token-hook nil
213 "*Hooks run when a token is browsed by the COGRE graph.
214 Each hook takes one argument, and one optional argument, the token
215 being browsed too, and a containing parent token, if available.
216 This is run when the token is first found, not during the actual
217 browse. The token will be under point when this hook is called.
218 Changing window configurations is not recommended."
223 (defun cogre-uml-browse-token-highlight-hook-fn (tok &optional parent)
224 "Momentarilly highlight TOK. Ignore PARENT.
225 Function useable by `cogre-uml-browse-token-hook'."
226 (semantic-momentary-highlight-tag tok))
228 (defmethod cogre-uml-source-marker ((class cogre-semantic-class) token)
229 "Return a marker position for a CLASS containing TOKEN.
230 This returned marker will be in the source file of the attribute,
231 method, or class definition. nil if there is not match."
232 (let ((semc (oref class class))
234 (cond ((and token (semantic-tag-with-position-p token))
235 (setq p (save-excursion
236 (semantic-go-to-tag token)
238 'cogre-uml-browse-token-hook
242 ((and token (semantic-tag-with-position-p semc))
243 (setq p (save-excursion
244 (semantic-go-to-tag token semc)
246 'cogre-uml-browse-token-hook
250 ((and semc (semantic-tag-with-position-p semc))
251 (setq p (save-excursion
252 (semantic-go-to-tag semc)
254 'cogre-uml-browse-token-hook
261 (defmethod cogre-uml-source-display ((class cogre-semantic-class) point)
262 "Display source code associated with CLASS based on text at POINT.
263 The text must be handled by an overlay of some sort which has the
264 semantic token we need as a property. If not, then nothing happens.
265 Uses `cogre-uml-source-display-method'."
266 (let* ((sem (get-text-property point 'semantic))
267 (p (cogre-uml-source-marker class sem)))
270 (funcall cogre-uml-source-display-method p))
274 (defmethod cogre-activate ((class cogre-semantic-class))
276 This could be as simple as displaying the current state,
277 customizing the object, or performing some complex task."
278 (let* ((sem (get-text-property (point) 'semantic))
279 (p (cogre-uml-source-marker class sem))
282 (error "No source to jump to")
283 ;; Activating is the reverse of just showing the sorce
284 (switch-to-buffer (marker-buffer p))
285 (funcall cogre-uml-source-display-method cp)
289 (defcustom cogre-uml-source-display-window-size 5
290 "Size of same-frame window displaying source code."
294 (defun cogre-uml-source-display-bottom (m)
295 "Display point M in a small buffer on the bottom of the current frame."
296 (if (not (eq (next-window) (selected-window)))
297 (cogre-uml-source-display-other-window m)
298 (split-window-vertically (- (window-height)
299 cogre-uml-source-display-window-size
302 (switch-to-buffer (marker-buffer m) t)
308 (defun cogre-uml-source-display-other-window (m)
309 "Display point M in other window."
311 (switch-to-buffer (marker-buffer m) t)
317 ;;; Auto-Graph generation
319 ;; Functions for creating a graph from semantic parts.
320 (defvar cogre-class-history nil
321 "History for inputting class names.")
323 (defun cogre-read-class-name ()
324 "Read in a class name to be used by a cogre node."
325 (let ((finddefaultlist (semantic-find-tag-by-overlay))
328 ;; Assume the top most item is the all encompassing class.
330 (setq class (car finddefaultlist)))
331 ;; Make sure our class is really a class
334 (eq (semantic-tag-class class) 'type)
335 (string= (semantic-tag-type class) "class")))
337 (setq class (semantic-tag-name class)))
339 (setq prompt (if class (concat "Class (default " class "): ") "Class: "))
340 ;; Get the stream used for completion.
341 (let ((types (semanticdb-strip-find-results
342 (semanticdb-brute-find-tags-by-class 'type)
343 ;; Don't find-file-match. Just need names.
345 (setq stream (semantic-find-tags-by-type "class" types)))
347 (completing-read prompt stream
348 nil nil nil 'cogre-class-history
353 (defun cogre-uml-quick-class (class)
354 "Create a new UML diagram based on CLASS showing only immediate lineage.
355 The parent to CLASS, CLASS, and all of CLASSes children will be shown."
356 (interactive (list (cogre-read-class-name)))
357 (let* ((class-tok (car (semanticdb-strip-find-results
358 (semanticdb-brute-deep-find-tags-by-name class) t)))
360 (parent (semantic-tag-type-superclasses class-tok))
362 (children (semanticdb-find-nonterminal-by-function
363 (lambda (stream sp si)
364 (semantic-brute-find-tag-by-function
366 (and (eq (semantic-tag-class tok) 'type)
368 (semantic-tag-type-superclasses tok))
370 (semantic-tag-type-interfaces tok)))))
378 ;; Create a new graph
379 (cogre class 'cogre-semantic-uml-graph)
380 (goto-char (point-min))
381 ;; Create all the parent nodes in the graph, and align them.
384 (cons (make-instance cogre-semantic-class
385 :position (vector x-accum y-accum)
388 (cogre-node-rebuild (car parent-nodes))
389 (setq x-accum (+ x-accum
390 (length (car (oref (car parent-nodes) rectangle)))
391 cogre-horizontal-margins))
392 (setq ymax (max ymax (length (oref (car parent-nodes) rectangle))))
393 (setq parent (cdr parent)))
394 (setq xmax (- x-accum cogre-horizontal-margins))
397 (setq y-accum (+ y-accum ymax cogre-vertical-margins))
399 (make-instance 'cogre-semantic-class
400 :position (vector x-accum y-accum)
402 (cogre-node-rebuild class-node)
403 (setq ymax (length (oref class-node rectangle)))
404 ;; Creawte all the children nodes, and align them.
406 (setq y-accum (+ y-accum ymax cogre-vertical-margins))
408 (let ((c (cdr (car children))))
411 (cons (make-instance 'cogre-semantic-class
412 :position (vector x-accum y-accum)
415 (cogre-node-rebuild (car children-nodes))
416 (setq x-accum (+ x-accum
417 (length (car (oref (car children-nodes) rectangle)))
418 cogre-horizontal-margins))
420 (setq children (cdr children)))
421 (setq xmax (max xmax (- x-accum cogre-horizontal-margins)))
422 ;; Center all the nodes to eachother.
425 (lines (list parent-nodes
431 (setq maxn (car (car lines)))
433 ;;(cogre-node-rebuild maxn)
434 (setq delta (- xmax (aref (oref maxn position) 0)
435 (length (car (oref maxn rectangle)))))
437 (setq shift (/ delta 2))
438 (mapcar (lambda (n) (cogre-move-delta n shift 0))
440 (setq lines (cdr lines)))
442 ;; Link everyone together
443 (let ((n parent-nodes))
445 (make-instance 'cogre-inherit :start class-node :end (car n))
447 (setq n children-nodes)
449 (make-instance 'cogre-inherit :start (car n) :end class-node)
456 (defun cogre-uml-create (class)
457 "Create a new UML diagram, with CLASS as the root node.
458 CLASS must be a type in the current project."
459 (interactive (list (cogre-read-class-name)))
460 (let ((root (semanticdb-strip-find-results
461 (semanticdb-find-tags-by-name class) t))
463 ;; Implement this some day.
466 (provide 'uml-create)
468 ;;; uml-create.el ends here