eieio / tree.el

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
;;; tree.el --- Draw a tree with text characters an manipulate it.

;;; Copyright (C) 1996, 1998, 1999, 2001 Eric M. Ludlam
;;
;; Author: <zappo@gnu.org>
;; Version: 0.3
;; RCS: $Id$
;; Keywords: OO, tree
;;                                                                          
;; 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, 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, you can either send email to this
;; program's author (see below) or write to:
;;
;;              The Free Software Foundation, Inc.
;;              675 Mass Ave.
;;              Cambridge, MA 02139, USA.
;;
;; Please send bug reports, etc. to zappo@gnu.org

;;; Commentary:
;;   Many new IDEs provide a tree of some structure-or-other to express the
;; structural organization of data.  This is a feature lacking in Emacs,
;; and this is some code to provide that functionality.
;;
;;  The interactive command `tree-test-it-all' will display a demo tree,
;; and `directory-tree-thing' will display a directory hierarchy from
;; the default directory of the current buffer.
;;
;; You can access general tree commands to play with this mode using
;; the functions:
;; tree-test-it-all  - bogus data that lets you see how tree-mode works
;; eieio-class-tree  - show all currently defined eieio classes and
;;                     their current inheritance organization
;; directory-tree-thing - Draw a tree corresponding to the current
;;                     directory.
;; 
;; In all tree modes:
;; RET & mouse-1
;;       will select a node: usually printing something simple in the
;;       minibuffer.
;; e & mouse-2
;;       will edit a node: bring up a file/directory/editor for that
;;       object.
;; x & mouse-3
;;       expand or deflate a node and it's children.  Shrunken nodes
;;       will have elipses `...' in them to indicate that more data
;;       could be expanded.
;;
;; Creating a new tree mode:
;; 1) Look at an example
;; 2) Create a new tree-node class and add whatever slots you need
;; 3) Create a place to make a tree with `tree-new-buffer'
;; 4) Set root of tree ith `tree-set-root'
;; 5) Add nodes ass you see fit with `tree-add-child'
;; 6) Draw the tree with `tree-refresh-tree'
;; 7) Modify select, edit, and change-scope to do whatever you need.
;;
;; REQUIRES: Emacs 19.30 or better and eieio

;; requires emacs 19 with arbitrary text-properties

(require 'eieio)

;;;
;; Variable definitions

;;; Code:
(defvar tree-map nil "Keymap used in tree mode.")
(if tree-map
    ()
  (setq tree-map (make-sparse-keymap))
  (define-key tree-map "\r" 'tree-select-node)
  (define-key tree-map "\n" 'tree-select-node)
  (define-key tree-map [mouse-1] 'tree-select-node-mouse)
  (define-key tree-map "e" 'tree-edit-node)
  (define-key tree-map [mouse-2] 'tree-edit-node-mouse)
  (define-key tree-map "x" 'tree-expand-or-contract-node)
  (define-key tree-map [mouse-3] 'tree-expand-or-contract-node-mouse)
  )

(defconst tree-ul-char "+")
(defconst tree-ur-char "+")
(defconst tree-ll-char "+")
(defconst tree-lr-char "+")
(defconst tree-vertical-char "|")
(defconst tree-horizontal-char "-")

(defvar tree-root-node nil
  "The root node of a tree in a given tree buffer.")
(make-variable-buffer-local 'tree-root-node)

(defvar tree-buffer-mode 'tree-center-box-1
  "Current mode of a tree buffer.

Valid values are:
'tree-center-box-1 - nodes are boxed w/ 1 line of text in center of region
                     this is default if this value is unknown
'tree-top-box-1    - nodes are boxed w/ 1 line of text @ top of region
'tree-bottom-box-1 - nodes are boxed w/ 1 line of text @ bottom of region")
(make-variable-buffer-local 'tree-buffer-mode)

(defvar tree-face 'bold
  "Face used inside tree-boxes.")

;;;
;; Mode management
;;
(defun tree-mode ()
  "Takes the current buffer, and initialize tree mode upon it."
  (kill-all-local-variables)
  (use-local-map tree-map)
  (setq major-mode 'tree-mode
	mode-name "TREE")
  (setq truncate-lines t)	; no line-wrapping
  (run-hooks 'tree-mode-hook)
;  (setq mode-line-format
;	(list
;	 'mode-line-modified
;	 "--TREE: %15b %[(" 'mode-name 'minor-mode-alist "%n)%]--"
;	 '(line-number-mode "L%l--%-")))
	 
)

(defun tree-new-buffer (name)
  "Create a buffer called NAME to display some tree type things.
Return the newly created buffer."
  (save-excursion
    (set-buffer (get-buffer-create name))
    (tree-mode)
    (current-buffer)))

(defun tree-new-frame (name)
  "Create a new frame NAME and set it up to use graphic characters.
Returns the newly created frame"
  (let ((nf (make-frame (list
			 (cons 'name name)
			 '(height . 30)
			 '(width . 80)
			 ;; I'd like to use a nicer font, but...
			 ;; '(font . "-dec-terminal-*-*-*-*-*-*-*-*-*-*-*-*")
			 '(minibuffer . nil))))
	(nb (tree-new-buffer name))
	(cf (selected-frame)))
    ;; set the buffer we are about to play with here...
    (select-frame nf)
    (switch-to-buffer nb)
    (select-frame cf)
    nf))

;;;
;; display management
;;
(defun tree-refresh-tree ()
  "Refresh the tree structure which is currently active in this buffer."
  (message "Refreshing tree...")
  ;; first-things first.  Cache the height of every node in the tree
  (tree-level-height tree-root-node)
  ;; Now fill the buffer with stuff
  (insert (make-string (oref tree-root-node height) ?\n))
  ;; Now loop over every node building the tree
  (tree-draw-node tree-root-node t t
		  (+ (tree-node-width tree-root-node) 2) 1 0)
  (message "Refreshing tree...Done")
  )

(defun tree-draw-node (node first last width toprow leftmargin)
  "Draw the single NODE and it's children at a correct estimated position.
Really calls a function based upon `tree-buffer-mode'.
FIRST LAST WIDTH TOPROW and LEFTMARGIN are passed along."
  (funcall tree-buffer-mode node first last width toprow leftmargin))

(defun tree-center-box-1 (node first last width toprow leftmargin)
  "As `tree-draw-node' except that we draw 1-line text w/ a box around it.
NODE FIRST LAST WIDTH TOPROW and LEFTMARGIN are passed along."
  (tree-box-1 node first last width toprow leftmargin 'center))

(defun tree-top-box-1 (node first last width toprow leftmargin)
  "As `tree-draw-node' except that we draw 1-line text w/ a box around it.
NODE FIRST LAST WIDTH TOPROW and LEFTMARGIN are passed along."
  (tree-box-1 node first last width toprow leftmargin 'top))

(defun tree-bottom-box-1 (node first last width toprow leftmargin)
  "As `tree-draw-node' except that we draw 1-line text w/ a box around it.
NODE FIRST LAST WIDTH TOPROW and LEFTMARGIN are passed along."
  (tree-box-1 node first last width toprow leftmargin 'bottom))

(defun tree-box-1 (node first last width toprow leftmargin &optional pos)
  "Draw a single NODE and it's children at a correct estimated position.
FIRST and LAST are not used.  WIDTH specifies how much space this row
will take.  TOPROW specifies what row this node starts at, and
LEFTMARGIN specifies how far out on the left this node can draw
itself.
Optional argument POS is a postion."
  (with-slots ((h height) (kids children) (p parent) (nm name) (ex expand))
      node
    (let ((tpos
	    (cond ((eq pos 'center) (- (/ h 2) 1))
		  ((eq pos 'top) 0)
		  ((eq pos 'bottom) (- h 3))
		  (t (error "Illegal call to tree-box-1"))))
	   (l 0))
      ;;(message "Refreshing tree...[%s]" nm)
      ;; draw the box
      (tree-goto-xy leftmargin (+ tpos toprow))
      (insert (if p " " "") tree-ul-char)
      (insert (make-string (- (tree-node-width node) 2)
			   (aref tree-horizontal-char 0)))
      (insert tree-ur-char)
      (tree-goto-xy leftmargin (+ tpos toprow 1))
      (insert (if p "-" "") tree-vertical-char)
      (let ((p1 (point)))
	(insert nm)
	(put-text-property p1 (point) 'face tree-face)
	(put-text-property p1 (point) 'node-object node)
	(put-text-property p1 (point) 'mouse-face 'highlight)
	)
      (insert (if (oref node expand) "" "..."))
      (insert tree-vertical-char)
      (if (and kids ex)
	  (let* ((mn (tree-node-width node))
		 (nd (- width mn (if p 2 1)))
		 (l 0))
	    (while (< l nd)
	      (insert "-")
	      (setq l (1+ l)))))
      (tree-goto-xy leftmargin (+ tpos toprow 2))
      (insert (if p " " "") tree-ll-char)
      (insert (make-string (- (tree-node-width node) 2)
			   (aref tree-horizontal-char 0)))
      (insert tree-lr-char)
      ;; draw all the kids
      (while (and kids ex)
	(tree-draw-node (car kids) (= l 0) (cdr kids)
			(+ (tree-level-width node) 3)
			(+ toprow l) (+ leftmargin width))
	(setq l (+ l (oref (car kids) height)))
	(setq kids (cdr kids)))
      ;; draw the connecting lines
      (setq kids (oref node children))
      (if (and kids (oref node expand))
	  (let ((i 1) (ok nil) (h (1- h)))
	    (while (and (< i h) kids)
	      (tree-goto-xy (+ leftmargin width -1) (+ toprow i))
	      (insert
	       (cond ((looking-at " -") (setq ok t kids (cdr kids)) "+")
		     (ok "|")
		     (t " ")))
	      (delete-char 1)
	      (setq i (1+ i))
	      )))
      (tree-goto-xy (+ leftmargin 2) (+ tpos toprow 1))
      (oset node currentpos (point))
      )))

(defun tree-goto-xy (x y)
  "Move cursor to position X Y in buffer, and add spaces and CRs if needed."
  (let ((indent-tabs-mode nil)
	(num (goto-line y)))
    (if (and (= 0 num) (/= 0 (current-column))) (newline 1))
    (if (eobp) (newline num))
    ;; Now, a quicky column moveto/forceto method.
    (or (= (move-to-column x) x) (indent-to x))))


;;;
;; Tree data-structure management
;;
(defclass tree-node ()
  ((name :initarg :name
	 :initform nil)
   (children :initarg :children
	     :initform nil)
   (expand :initarg :expand
	   :initform t)
   (parent :initarg :parent
	   :initform nil)
   (height :initarg :height
	   :initform 3)
   (currentpos :initform 0)
   )
  "Base class for a tree node")

(defmethod select ((tn tree-node))
  "Action to take when first mouse is clicked."
  (message "Clicked on node %s" (object-name tn))
  )
(defmethod edit ((tn tree-node))
  "Action to take when middle mouse button is clicked."
  (let ((nn (read-string "New name: ")))
    (oset tn name nn))
  (erase-buffer)
  (tree-refresh-tree)
  (goto-char (oref tn currentpos))
  )
(defmethod change-scope ((tn tree-node))
  "Action to take when last mouse is clicked on this node"
  (if (oref tn children)
      (progn
	(oset tn expand (not (oref tn expand)))
	(erase-buffer)
	(tree-refresh-tree)
	(goto-char (oref tn currentpos))
	)))

(defun tree-set-root (node)
  "Create a new tree NODE with the specified name, and make it the root."
  (setq tree-root-node node)
  )

(defun tree-new-node (name)
  "Create a new tree node with specified text NAME."
  (tree-node name :name name))

(defun tree-add-child (parent child)
  "Add to PARENT variable `tree-node' the variable `tree-node' CHILD.
Returns child to aid in building quick trees."
  (oset child parent parent)
  (oset parent children (append (oref parent children) (list child)))
  child)

(defun tree-sort-elements (node)
  "Sort all children of NODE, recurse."
  (let ((k (oref node children)))
    (setq k (sort k '(lambda (a b) (string< (oref a name) (oref b name)))))
    (oset node children k)
    (while k
      (tree-sort-elements (car k))
      (setq k (cdr k)))))

(defun tree-trim-below (node depth)
  "Set the expand field for NODE to nil for all nodes below DEPTH."
  (let ((k (oref node children)))
    (if (and k (<= depth 1)) (oset node expand nil))
    (while k
      (tree-trim-below (car k) (1- depth))
      (setq k (cdr k)))))


;;;
;; Tree node statistics
;;

(defun tree-node-width (node)
  "Return the width of NODE."
  (+ (length (oref node name)) 2 (if (not (oref node expand)) 3 0)))

(defun tree-level-width (node)
  "Return the widest box to appear under NODE."
  (let ((kids (oref node children))
	(w 0))
    (while kids
      (let ((tl (tree-node-width (car kids))))
	(if (and (< w tl) (oref (car kids) children) (oref (car kids) expand))
	    (setq w tl)))
      (setq kids (cdr kids)))
    w))

(defun tree-level-height (node)
  "Return the total height in chars of all nodes under NODE.
Cache the height into each node for later use"
  (let ((kids (oref node children))
	(h 0))
    (if (or (not kids) (not (oref node expand)))
	(setq h 3)
      (while kids
	(setq h (+ h (tree-level-height (car kids))))
	(setq kids (cdr kids))))
    (oset node height h)
    h))


;;;
;; Tree keyboard commands
;;

(defun tree-select-node ()
  "Activate the node currently under (point), or bell if none.
Requires text-properties"
  (interactive)
  (let ((node (get-text-property (point) 'node-object)))
    (if node
	(select node)
      (error "There is no tree-node under point"))))

(defun tree-select-node-mouse ()
  "Activate the node currently under (point), or bell if none.
Requires text-properties"
  (interactive)
  (call-interactively 'mouse-set-point)
  (let ((node (get-text-property (point) 'node-object)))
    (if node
	(select node)
      (error "There is no tree-node under point"))))

(defun tree-edit-node ()
  "Activate the node currently under (point), or bell if none.
Requires text-properties"
  (interactive)
  (let ((node (get-text-property (point) 'node-object)))
    (if node
	(edit node)
      (error "There is no tree-node under point"))))

(defun tree-edit-node-mouse ()
  "Activate the node currently under (point), or bell if none.
Requires text-properties"
  (interactive)
  (call-interactively 'mouse-set-point)
  (let ((node (get-text-property (point) 'node-object)))
    (if node
	(edit node)
      (error "There is no tree-node under point"))))

(defun tree-expand-or-contract-node ()
  "Activate the node currently under (point), or bell if none.
Requires text-properties"
  (interactive)
  (let ((node (get-text-property (point) 'node-object)))
    (if node
	(change-scope node)
      (error "There is no tree-node under point"))))

(defun tree-expand-or-contract-node-mouse ()
  "Activate the node currently under (point), or bell if none.
Requires text-properties"
  (interactive)
  (call-interactively 'mouse-set-point)
  (let ((node (get-text-property (point) 'node-object)))
    (if node
	(change-scope node)
      (error "There is no tree-node under point"))))


;;;
;; Tree test case
;;

;;;###autoload
(defun tree-test-it-all ()
  "Try using various features of tree mode in a demo of it's display."
  (interactive)
  ;; create a new buffer
  (switch-to-buffer (tree-new-buffer "*TREE DEMO*"))
  (erase-buffer)
  ;; set up the root node and some children
  (let* ((ntn (tree-set-root (tree-new-node "root")))
	 (stn1 (tree-add-child ntn (tree-new-node "Bob")))
	 (stn2 (tree-add-child ntn (tree-new-node "Stan")))
	 (stn3 (tree-add-child ntn (tree-new-node "Valarie")))
	 (sstn1 (tree-add-child stn1 (tree-new-node "Bob1")))
	 (sstn2 (tree-add-child stn1 (tree-new-node "Bob2")))
	 (sstn3 (tree-add-child stn1 (tree-new-node "Bob3")))
	 (sstn4 (tree-add-child stn1 (tree-new-node "Bob4")))
	 (sstn5 (tree-add-child stn3 (tree-new-node "Valarie2")))
	 (sstn6 (tree-add-child stn3 (tree-new-node "Valarie3")))
	 (ssstn1 (tree-add-child sstn4 (tree-new-node "Bobby1")))
	 (ssstn2 (tree-add-child sstn4 (tree-new-node "Bobby2")))
	 ;(ssstn2 (tree-add-child sstn4 (tree-new-node "Bobby3")))
	 ))
  (tree-refresh-tree)
  )


;;;
;; Tree demo using eieio class structures
;;
(defclass eieio-tree-node (tree-node)
  ((class :initarg :class
	  :initform nil))
  "Tree node used to represent eieio classes")

(defmethod edit ((etn eieio-tree-node))
  "Don't really edit, but pull up details about the given widget using
`eieio-describe-class'"
  (eieio-describe-class (oref etn class)))

(defmethod select ((etn eieio-tree-node))
  "Display a tiny bit of info about this object which might be useful"
  (message "%s" (class-name (oref etn class)))
  )

(defun eieio-new-node (class)
  "Create a new widget tree node with the specified WIDGET slot.
Argument CLASS is the class we are displaying."
  (eieio-tree-node (symbol-name class)
		   :name (symbol-name class)
		   :class class)
  )

;;;###autoload
(defun eieio-class-tree (&optional root-class)
  "Displays a class tree using the TREE package in another buffer.
Optional argument ROOT-CLASS is the starting point."
  (interactive)
  (if (not root-class) (setq root-class 'eieio-default-superclass))
  (switch-to-buffer (tree-new-buffer "*EIEIO CLASS TREE*"))
  (erase-buffer)
  (let ((np (tree-set-root (eieio-new-node root-class))))
    (eieio-tree-grow np))
  (tree-refresh-tree))

(defun eieio-tree-grow (node)
  "Add to NODE all children."
  (let* ((wk (aref (class-v (oref node class)) class-children))
	 nn)
    (while wk
      (setq nn (eieio-new-node (car wk)))
      (tree-add-child node nn)
      (eieio-tree-grow nn)
      (setq wk (cdr wk))))
  )


;;;
;; Tree demos using directories
;;
(defclass dirtree-node (tree-node)
  ((pathname :initarg :path
	     :initform nil)
   (haschildren :initarg :haschildren
		:initform unknown)
   )
  "A tree-node child class for displaying a directory.")

(defmethod edit ((dtn dirtree-node))
  "Action to take when this node is clicked."
  (find-file (format "%s%s" (oref dtn pathname) (oref dtn name)))
)
(defmethod select ((dtn dirtree-node))
  "Action to take when this node is clicked."
  (shell-command (format "ls -ld %s%s" (oref dtn pathname)
			 (oref dtn name)))
)
(defmethod change-scope ((dtn dirtree-node))
  "Action to take when last mouse is clicked on this node"
  ;; check for new nodes...
  (if (equal (oref dtn haschildren) 'unknown)
      (let ((path-path (oref dtn pathname)))
	(directory-tree-more-nodes dtn 1)))
  (if (oref dtn children)
      (progn
	(oset dtn expand (not (oref dtn expand)))
	(erase-buffer)
	(tree-refresh-tree)
	(goto-char (oref dtn currentpos))
	)
    ))

(defun dirtree-new (name path)
  "Create a new directory tree node.
Argument NAME is the name of the tree node.
Argument PATH is the path to that file."
  (dirtree-node name :name name :path path))

;;;###autoload
(defun directory-tree-thing (ppath)
  "Start at the current directory, and build a giant tree of files.
Argument PPATH is the path to the directory we are going to analyze."
  (interactive "fDirectory to graph: ")
  (let ((toppath (if (string-match "/$" ppath)
		     (substring ppath 0 (1- (length ppath)))
		   ppath)))
    (switch-to-buffer
     (tree-new-buffer (format "TREE: %s" (file-name-nondirectory toppath))))
    (erase-buffer)
    (let ((node (tree-set-root (dirtree-new
				(file-name-nondirectory toppath)
				(file-name-directory toppath)
				)))
	  (path-path (file-name-directory toppath)))
      (directory-tree-more-nodes node 2))
    (setq tree-buffer-mode 'tree-top-box-1)
    (message "Refreshing tree...")
    (tree-refresh-tree)
    ))

(defun directory-tree-more-nodes (node dokids)
  "Find more parts of this directory.  Do not expand kids if dokids = 0.
Argument NODE is the node to display.  DOKIDS is a flag to display children."
  (message "Tracing directory... [%s]" (oref node name))
  ;; mark that we checked this guy
  (oset node haschildren 'known)
  (let* ((nm (oref node name))
	 ;;                            path-path is letted in previous call
	 (files (directory-files (concat path-path nm) nil nil t)))
    (while files
      (if (or (string= "." (car files))
	      (string= ".." (car files)))
	  ()
	(if (file-accessible-directory-p (concat path-path nm "/"
						 (car files)))
	    (let ((path-path (concat path-path nm "/"))
		  (newnode (tree-add-child node (dirtree-new
						 (car files)
						 (concat path-path nm "/")
						 ))))
	      ;; These directories never have subdirectories, but
	      ;; often contain many many files!
	      (if (and (not (member (car files)
				    '(".xvpics" "SCCS" "RCS" "CVS")))
		       (< 0 dokids))
		  (progn
		    (directory-tree-more-nodes newnode (1- dokids))
		    (oset newnode children
			  (sort (oref newnode children)
				'(lambda (a b)
				   (string< (oref a name) (oref b name)))
				))
		    )))))
      (setq files (cdr files)))
    ;; mark not to expand..
    (if (and (= dokids 0) (oref node children))
	(oset node expand nil))
    )
  )

(provide 'tree)

;;; tree.el ends here
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.