forked from ChasManRors/ecb
-
Notifications
You must be signed in to change notification settings - Fork 20
/
ecb.el
1851 lines (1652 loc) · 75.1 KB
/
ecb.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
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; ecb.el --- a code browser
;; Copyright (C) 2000, 2001 Jesper Nordenberg
;; Author: Jesper Nordenberg <mayhem@home.se>
;; Maintainer: Jesper Nordenberg <mayhem@home.se>
;; Keywords: java, class, browser
;; Created: Jul 2000
(defvar ecb-version "1.32"
"Current ECB version.")
;; 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
;; GNU Emacs; see the file COPYING. If not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
;;; Commentary:
;;
;; The Emacs code browser (ECB) creates four buffers: *ECB Directories*, *ECB
;; Sources*, *ECB Methods* and *ECB History*. These buffers can be used to
;; navigate through source code with the mouse.
;;
;; To use the Emacs code browser add the ECB files to your load path and add the
;; following line to your .emacs file:
;;
;; (require 'ecb)
;;
;; Optional: You can byte-compile ECB after the ECB-package is loaded with
;; `ecb-byte-compile'.
;;
;; ECB requires version 1.3.3 of Eric's semantic bovinator
;; (http://www.ultranet.com/~zappo/semantic.shtml).
;; If you are working with Java, ECB works best when the JDE package
;; (http://sunsite.auc.dk/jde) is installed.
;;
;; ECB is activated by calling:
;;
;; (ecb-activate)
;;
;; After activating ECB you should call `ecb-show-help' to get a detailed
;; description of what ECB offers to you and how to use ECB.
;;
;; The latest version of the ECB is available at
;; http://home.swipnet.se/mayhem/ecb.html
;; $Id$
;;; Code:
;; TODO: After semantic 1.4 is stable (means no beta stadium) we throw away
;; the compatibility with semantic 1.3.X!
(eval-and-compile
(if (locate-library "semantic-load")
(progn
(message "ECB uses semantic 1.4")
(require 'semantic)
(setq semantic-load-turn-everything-on nil)
(require 'semantic-load)
(defun semantic-toplevel-get-cache ()
semantic-toplevel-bovine-cache))
;; semantic 1.3.X
(message "ECB uses semantic 1.3.X")
(defconst semantic-version "1.3.X")
(require 'semantic)
(require 'semantic-el)
(require 'semantic-c)
(require 'semantic-make)
(defun semantic-active-p ()
(or semantic-toplevel-bovine-table
semantic-toplevel-bovinate-override))
(defun semantic-toplevel-get-cache ()
(car semantic-toplevel-bovine-cache))
)
)
(require 'tree-buffer)
(require 'ecb-layout)
(require 'ecb-mode-line)
(require 'ecb-util)
(require 'ecb-help)
(require 'assoc);; Semantic fix
(eval-when-compile
;; to avoid compiler grips
(require 'cl))
;;====================================================
;; Variables
;;====================================================
(defvar ecb-selected-method-start 0
"The currently selected method.")
(defvar ecb-path-selected-directory nil
"Path to currently selected directory.")
(defvar ecb-path-selected-source nil
"Path to currently selected source.")
(defvar ecb-selected-token nil
"The currently selected Semantic token.")
(defvar ecb-methods-root-node nil
"Path to currently selected source.")
(defvar ecb-activated nil
"Do not set this variable directly. Use `ecb-activate' and
`ecb-deactivate'!")
;;====================================================
;; Customization
;;====================================================
(defgroup ecb nil
"Emacs code browser."
:group 'tools
:prefix "ecb-")
(defgroup ecb-general nil
"General settings for the Emacs code browser."
:group 'ecb
:prefix "ecb-")
(defgroup ecb-directories nil
"Settings for the directories buffer in the Emacs code browser."
:group 'ecb
:prefix "ecb-")
(defgroup ecb-sources nil
"Settings for the source buffers in the Emacs code browser."
:group 'ecb
:prefix "ecb-")
(defgroup ecb-methods nil
"Settings for the methods buffer in the Emacs code browser."
:group 'ecb
:prefix "ecb-")
(defgroup ecb-history nil
"Settings for the history buffer in the Emacs code browser."
:group 'ecb
:prefix "ecb-")
(defcustom ecb-use-recursive-edit nil
"*Tell ECB to use a recursive edit so that it can easily be deactivated
by \(keyboard-escape-quit)."
:group 'ecb-general
:type 'boolean)
(defcustom ecb-source-path nil
"*Path where to find code sources."
:group 'ecb-directories
:set (function (lambda(symbol value)
(set symbol value)
(if (and ecb-activated
(functionp 'ecb-update-directories-buffer))
(ecb-update-directories-buffer))))
:type '(repeat (directory :tag "Path")))
(defvar ecb-source-path-functions nil
"List of functions to call for finding sources. Each time the function
`ecb-update-directories-buffer' is called, the functions in this variable will
be evaluated. Such a function must return either nil or a list of strings
where each string is a path.")
(defcustom ecb-show-sources-in-directories-buffer nil
"*Show source files in directories buffer."
:group 'ecb-directories
:type 'boolean)
(defcustom ecb-directories-buffer-name "*ECB Directories*"
"*Name of the ECB directory buffer. Because it is not a normal buffer for
editing you should enclose the name with stars, e.g. \"*ECB Directories*\".
If it is necessary for you you can get emacs-lisp access to the buffer-object of
the ECB-directory-buffer by this name, e.g. by a call of `set-buffer'.
Changes for this option at runtime will take affect only after deactivating and
then activating ECB again!"
:group 'ecb-directories
:type 'string)
(defface ecb-sources-face
'((((class color) (background light)) (:foreground "medium blue"))
(((class color) (background dark)) (:foreground "LightBlue1"))
(t (:background "gray")))
"*Define a face for displaying sources in the directories buffer."
:group 'faces
:group 'ecb-directories)
(defcustom ecb-source-in-directories-buffer-face
'ecb-sources-face
"*Face for source files in the directories buffer."
:group 'ecb-directories
:type 'face)
(defcustom ecb-excluded-directories-regexp "^\\(CVS\\|\\..*\\)$"
"*Specifies directories that should not be included in the directories
list. The value of this variable should be a regular expression."
:group 'ecb-directories
:type 'regexp)
(defcustom ecb-auto-expand-directory-tree t
"*Automatically expand the directory tree to the current source file."
:group 'ecb-directories
:type 'boolean)
(defcustom ecb-sources-buffer-name "*ECB Sources*"
"*Name of the ECB sources buffer. Because it is not a normal buffer for
editing you should enclose the name with stars, e.g. \"*ECB Sources*\".
If it is necessary for you you can get emacs-lisp access to the buffer-object of
the ECB-sources-buffer by this name, e.g. by a call of `set-buffer'.
Changes for this option at runtime will take affect only after deactivating and
then activating ECB again!"
:group 'ecb-sources
:type 'string)
(defcustom ecb-source-file-regexps
'("\\(^\\(\\.\\|#\\)\\|\\(~$\\|\\.\\(elc\\|obj\\|o\\|class\\|lib\\|dll\\|a\\|so\\|cache\\)$\\)\\)"
"^\\.\\(emacs\\|gnus\\)$")
"*Specifies which files are shown as source files. Consists of one exclude
regexp and one include regexp. A file is displayed in the source-buffer of ECB
iff: The file does not match the exclude regexp OR the file matches the
include regexp. There are three predefined and useful combinations of an
exclude and include regexp:
- All files
- All, but no backup, object, lib or ini-files \(except .emacs and .gnus). This
means all files except those starting with \".\", \"#\" or ending with
\"~\", \".elc\", \".obj\", \".o\", \".lib\", \".dll\", \".a\", \".so\".
(but including .emacs and .gnus)
- Common source file types (.c, .java etc.)
In addition to these predefined values a custom exclude and include
combination can be defined."
:group 'ecb-sources
:type '(radio (const :tag "All files"
:value ("" ""))
(const :tag "All, but no backup, object, lib or ini-files \(except .emacs and .gnus)"
:value ("\\(^\\(\\.\\|#\\)\\|\\(~$\\|\\.\\(elc\\|obj\\|o\\|class\\|lib\\|dll\\|a\\|so\\|cache\\)$\\)\\)" "^\\.\\(emacs\\|gnus\\)$"))
(const :tag "Common source file types (.c, .java etc.)"
:value ("" "\\(\\(M\\|m\\)akefile\\|.*\\.\\(java\\|el\\|c\\|cc\\|h\\|hh\\|txt\\|html\\|texi\\|info\\|bnf\\)\\)$"))
(list :tag "Custom (tips: \"$^\" matches no files, \"\" mathes all files)"
(regexp :tag "Exclude regexp"
:value "")
(regexp :tag "Include regexp"
:value ""))))
(defcustom ecb-show-source-file-extension t
"*Show the file extension of source files."
:group 'ecb-sources
:type 'boolean)
(defcustom ecb-sources-sort-method 'name
"*Defines how the source files are sorted."
:group 'ecb-sources
:type '(radio (const :tag "By name"
:value name)
(const :tag "By extension"
:value extension)
(const :tag "No sorting"
:value nil)))
(defcustom ecb-history-buffer-name "*ECB History*"
"*Name of the ECB history buffer. Because it is not a normal buffer for
editing you should enclose the name with stars, e.g. \"*ECB History*\".
If it is necessary for you you can get emacs-lisp access to the buffer-object of
the ECB-history-buffer by this name, e.g. by a call of `set-buffer'.
Changes for this option at runtime will take affect only after deactivating and
then activating ECB again!"
:group 'ecb-history
:type 'string)
(defcustom ecb-sort-history-items nil
"*Sorts the items in the history buffer."
:group 'ecb-history
:type 'boolean)
(defcustom ecb-clear-history-behavior 'not-existing-buffers
"*Defines which entries of the history buffer should be deleted if
`ecb-clear-history' is called. Three options are available:
- not-existing-buffers: All entries which represent a buffername not existing
anymore in the bufferlist will be cleared. Probably the most senseful value.
- existing-buffers: The opposite of 'not-existing-buffers.
- all: The whole history will be cleared."
:group 'ecb-history
:type '(radio (const :tag "Not existing buffers"
:value not-existing-buffers)
(const :tag "Existing buffers"
:value existing-buffers)
(const :tag "All entries"
:value all)))
(defcustom ecb-methods-buffer-name "*ECB Methods*"
"*Name of the ECB methods buffer. Because it is not a normal buffer for
editing you should enclose the name with stars, e.g. \"*ECB Methods*\".
If it is necessary for you you can get emacs-lisp access to the buffer-object of
the ECB-methods-buffer by this name, e.g. by a call of `set-buffer'.
Changes for this option at runtime will take affect only after deactivating and
then activating ECB again!"
:group 'ecb-methods
:type 'string)
(defcustom ecb-auto-update-methods-after-save t
"*Automatically updating the ECB method buffer after saving
the current source-buffer."
:group 'ecb-methods
:type 'boolean)
(defcustom ecb-show-method-arguments 'only-type
"*Show method argument types and/or names. You have the following
choices:
- only-type: Show only the type of the argument
- type-and-name: Show both type and name
- nil: Do not show arguments.
In an untyped language like emacs-lisp show always only the argumentnames
instead."
:group 'ecb-methods
:type '(radio (const :tag "Show only type"
:value only-type)
(const :tag "Show type and name"
:value type-and-name)
(const :tag "Do not show arguments"
:value nil)))
(defcustom ecb-show-method-return-type 'after
"*Show method return type. You can specify where the return type
is displayed:
- after: <method-name> \(<arguments>) : <return type> \(= UML notation)
- before: <return type> <method-name> \(<arguments>)"
:group 'ecb-methods
:type '(radio (const :tag "Display after method \(UML\)"
:value after)
(const :tag "Display before method"
:value before)
(const :tag "Do not show return type"
:value nil)))
(defcustom ecb-font-lock-methods t
"*Adds font-locking \(means highlighting) to the ECB-method buffer."
:group 'ecb-methods
:type 'boolean)
(defcustom ecb-show-classes 'before
"*How to show classes in the methods buffer."
:group 'ecb-methods
:type '(radio (const :tag "Display before methods"
:value before)
(const :tag "Display after methods"
:value after)
(const :tag "Do not show classes"
:value nil)))
(defcustom ecb-font-lock-method-faces '(font-lock-function-name-face
font-lock-type-face
font-lock-variable-name-face
font-lock-type-face
bold
font-lock-variable-name-face
font-lock-type-face)
"*Specify how to highlight the parts of a method in the method buffer.
The value must be a list of exactly seven elements each of them either nil
\(not highlighting this part) or a face for this part. The sequence within the
list must be \(methodename argumenttype argumentname returntype classtype
variablename variabletype).
This option takes only effect if `ecb-font-lock-methods' is on."
:group 'ecb-methods
:type '(list (radio :tag "Method name"
(const :tag "Do not highlight" :value nil)
(face))
(radio :tag "Argument type"
(const :tag "Do not highlight" :value nil)
(face))
(radio :tag "Argument name"
(const :tag "Do not highlight" :value nil)
(face))
(radio :tag "Return type"
(const :tag "Do not highlight" :value nil)
(face))
(radio :tag "Class type"
(const :tag "Do not highlight" :value nil)
(face))
(radio :tag "Variable name"
(const :tag "Do not highlight" :value nil)
(face))
(radio :tag "Variable type"
(const :tag "Do not highlight" :value nil)
(face))))
(defcustom ecb-sort-methods t
"*Sort the methods in the methods buffer."
:group 'ecb-methods
:type 'boolean)
(defcustom ecb-method-jump-sets-mark t
"*Jumping to a method from the ECB-method buffer now sets the mark
so the user can easily jump back."
:group 'ecb-methods
:type 'boolean)
(defcustom ecb-sort-variables t
"*Sort the variables in the methods buffer."
:group 'ecb-methods
:type 'boolean)
(defcustom ecb-show-variables 'collapsed
"*How to show variables in the methods buffer."
:group 'ecb-methods
:type '(radio (const :tag "Show variables expanded"
:value expanded)
(const :tag "Show variables collapsed"
:value collapsed)
(const :tag "Do not show variables"
:value nil)))
(defcustom ecb-show-parents 'expanded
"*How to show parents (extends and implements) of a class in the
methods buffer \(see also `ecb-exclude-parents-regexp')."
:group 'ecb-methods
:type '(radio (const :tag "Show parents expanded"
:value expanded)
(const :tag "Show parents collapsed"
:value collapsed)
(const :tag "Do not show parents"
:value nil)))
(defcustom ecb-exclude-parents-regexp nil
"*Regexp which parent classes should not be shown in the methods buffer
\(see also `ecb-show-parents'). If nil then all parents will be shown if
`ecb-show-parents' is not nil."
:group 'ecb-methods
:type '(radio (const :tag "Do not exclude any parents"
:value nil)
(regexp :tag "Parents-regexp to exclude")))
(defcustom ecb-highlight-token-with-point 'highlight-scroll
"*How to highlight the method or variable under the cursor.
- highlight-scroll: Always scroll the method buffer, so the current method of the
edit-window is highlighted in the method-window.
- highlight: Only highlight the current method of the edit window in the
method window if the method is visible in the method-window.
- nil: No highlighting is done.
See also `ecb-highlight-token-with-point-delay'."
:group 'ecb-methods
:type '(radio (const :tag "Highlight and scroll window"
:value highlight-scroll)
(const :tag "Just highlight"
:value highlight)
(const :tag "Do not highlight"
:value nil)))
(defcustom ecb-highlight-token-with-point-delay 0.25
"*Time Emacs must be idle before current token is highlighted.
If nil then there is no delay, means current token is highlighted immediately.
If your computer is slow then it could be reasonable to set a value of about
0.25 for example."
:group 'ecb-methods
:type '(radio (const :tag "No highlighting delay"
:value nil)
(number :tag "Idle time before highlighting"
:value 0.25))
:set (function (lambda (symbol value)
(set symbol value)
(if ecb-activated
(ecb-activate-ecb-token-sync value))))
:initialize 'custom-initialize-default)
(defcustom ecb-tree-indent 2
"*Indent size for tree buffer. If you change this during ECB is activated
you must deactivate and activate ECB again to take effect."
:group 'ecb-general
:type 'integer)
(defcustom ecb-tree-expand-symbol-before nil
"*Show the expand symbol before the items in a tree."
:group 'ecb-general
:type 'boolean)
(defcustom ecb-truncate-lines t
"*Truncate lines in ECB buffers. If you change this during ECB is activated
you must deactivate and activate ECB again to take effect."
:group 'ecb-general
:type 'boolean)
(defcustom ecb-window-sync t
"*Synchronize ECB with edit window."
:group 'ecb-general
:type 'boolean)
(defcustom ecb-tree-incremental-search 'prefix
"*Enable incremental search in the ECB-tree-buffers. For a detailed
explanation see the online help section \"Working with the keyboard in the ECB
buffers\". If you change this during ECB is activated you must deactivate and
activate ECB again to take effect."
:group 'ecb-general
:type '(radio (const :tag "Match only prefix"
:value prefix)
(const :tag "Match every substring"
:value substring)
(const :tag "No incremental search"
:value nil)))
(defcustom ecb-show-node-name-in-minibuffer 'if-too-long
"*Show the name of the ECB-buffer item under mouse in minibuffer.
If set to 'always of 'if-too-long then this works always only by moving the
mouse over a node regardless if the ECB-window is the active window or not."
:group 'ecb-general
:set (function (lambda (symbol value)
(set symbol value)
(if (and (boundp 'ecb-activated)
ecb-activated)
(if (or (equal value 'always)
(equal value 'if-too-long))
(tree-buffer-activate-follow-mouse)
(tree-buffer-deactivate-follow-mouse)
(tree-buffer-deactivate-mouse-tracking)))))
:type '(radio (const :tag "Always"
:value always)
(const :tag "If longer than window-width"
:value if-too-long)
(const :tag "After SHIFT-primary-mouse-button-click"
:value shift-click)
(const :tag "Never"
:value nil)))
(defcustom ecb-show-file-info-in-minibuffer t
"*Show file information about the file under mouse in minibuffer."
:group 'ecb-general
:type 'boolean)
(defcustom ecb-show-complete-file-name-in-minibuffer nil
"*Show the complete file name including directories for the file under mouse
in minibuffer."
:group 'ecb-general
:type 'boolean)
(defcustom ecb-primary-secondary-mouse-buttons 'mouse-2--C-mouse-2
"*Primary- and secondary mouse button for using the ECB-buffers.
A click with the primary button causes the main effect in each ECB-buffer:
- ECB Directories: Expanding/collapsing nodes and displaying files in the ECB
Sources buffer.
- ECB sources/history: Opening the file in that edit-window specified by the
option `ecb-primary-mouse-jump-destination'.
- ECB Methods: Jumping to the method in that edit-window specified by the
option `ecb-primary-mouse-jump-destination'.
A click with the primary mouse-button while the SHIFT-key is pressed only
displays the complete clicked node in the minibuffer. This is useful if the
node is longer as the window-width of the ECB-window and `ecb-truncate-lines'
is not nil.
The secondary mouse-button is for opening \(jumping to) the file in the other
window \(see the documentation `ecb-primary-mouse-jump-destination').
The following combinations are possible:
- primary: mouse-2, secondary: C-mouse-2 \(means mouse-2 while CTRL-key is
pressed). This is the default setting.
- primary: mouse-1, secondary: C-mouse-1
- primary: mouse-1, secondary: mouse-2
If you change this during ECB is activated you must deactivate and activate
ECB again to take effect"
:group 'ecb-general
:type '(radio (const :tag "Primary: mouse-2, secondary: Ctrl-mouse-2"
:value mouse-2--C-mouse-2)
(const :tag "Primary: mouse-1, secondary: Ctrl-mouse-1"
:value mouse-1--C-mouse-1)
(const :tag "Primary: mouse-1, secondary: mouse-2"
:value mouse-1--mouse-2)))
;; Thanks to David Hay for the suggestion <David.Hay@requisite.com>
(defcustom ecb-primary-mouse-jump-destination 'left-top
"*Jump-destination of a primary mouse-button click \(see
`ecb-primary-secondary-mouse-buttons') in an ECB-window, if you click onto a
source or method or variable. Defines in which edit-window \(if splitted) ECB
does the \"right\" action \(opening the source, jumping to a method/variable).
There are two possible choices:
- left-top: Does the \"right\" action always in the left/topmost edit-window.
- last-point: Does the \"right\" action always in that edit-window which had
the point before.
If the edit-window is not splitted this setting doesn´t matter.
Note: A click with the secondary mouse-button \(see again
`ecb-primary-secondary-mouse-buttons' does the \"right\" action always in the
\"other\" window related to the setting in this option."
:group 'ecb-general
:type '(radio (const :tag "Left/topmost edit-window"
:value left-top)
(const :tag "Last edit-window with point"
:value last-point)))
(defcustom ecb-activate-before-layout-draw-hook nil
"*Normal hook run at the end of activating the ecb-package by running
`ecb-activate'. This hooks are run after all the internal setup process
but directly before(!) drawing the layout specified in `ecb-layout' \(means
before dividing the frame into several windows).
A senseful using of this hook can be maximizing the Emacs-frame for example,
because this should be done before the layout is drawn because ECB computes
the size of the ECB-windows with the current frame size!
If you need a hook-option for the real end of the activating process (i.e.
after the layout-drawing) look at `ecb-activate-hook'."
:group 'ecb-general
:type 'hook)
(defcustom ecb-activate-hook nil
"*Normal hook run at the end of activating the ecb-package by running
`ecb-activate'. This hooks are run at the real end of the activating
process, means after the layout has been drawn!. If you need hooks which are
run direct before the layout-drawing look at
`ecb-activate-before-layout-draw-hook'."
:group 'ecb-general
:type 'hook)
(defcustom ecb-deactivate-hook nil
"*Normal hook run at the end of deactivating \(but before the ecb-layout is
cleared!) ECB by running `ecb-deactivate'."
:group 'ecb-general
:type 'hook)
;;====================================================
;; Methods
;;====================================================
(defconst ecb-language-modes-args-separated-with-space
'(emacs-lisp-mode scheme-mode lisp-mode))
(defconst ecb-methodname 0)
(defconst ecb-argumenttype 1)
(defconst ecb-argumentname 2)
(defconst ecb-returntype 3)
(defconst ecb-classtype 4)
(defconst ecb-variablename 5)
(defconst ecb-variabletype 6)
(defmacro ecb-exec-in-directories-window (&rest body)
`(unwind-protect
(when (ecb-window-select ecb-directories-buffer-name)
,@body)
))
(defmacro ecb-exec-in-sources-window (&rest body)
`(unwind-protect
(when (ecb-window-select ecb-sources-buffer-name)
,@body)
))
(defmacro ecb-exec-in-methods-window (&rest body)
`(unwind-protect
(when (ecb-window-select ecb-methods-buffer-name)
,@body)
))
(defmacro ecb-exec-in-history-window (&rest body)
`(unwind-protect
(when (ecb-window-select ecb-history-buffer-name)
,@body)
))
(defun ecb-window-select (name)
(let ((window (get-buffer-window name)))
(if window
(select-window window)
nil)))
(defun ecb-buffer-select (name)
(set-buffer (get-buffer name)))
(defun ecb-highlight-text (orig-text type)
"If `ecb-font-lock-methods' is not nil then dependend to TYPE the face
specified in `ecb-font-lock-method-faces' is added to TEXT, otherwise TEXT
will get the face 'default. Returns TEXT."
(let ((text (copy-sequence orig-text)))
(if (stringp text)
(if ecb-font-lock-methods
(let* ((face-option-val (nth type ecb-font-lock-method-faces))
(face (or (and face-option-val
(if running-xemacs
;; facep seems not to work correct with
;; XEmacs and boundp returns nil for
;; faces like bold, italic, underline.
(or (boundp face-option-val)
(equal face-option-val 'bold)
(equal face-option-val 'italic)
(equal face-option-val 'underline))
(facep face-option-val))
face-option-val)
'default)))
(put-text-property 0 (length text) 'face face text)
;; some special heuristic for better handling of the lisp-dialects
(when (and (memq major-mode
ecb-language-modes-args-separated-with-space)
(eq type ecb-argumentname)
(not (eq face 'default))
;; lets look if some special keywords like &optional or :key
;; are in the text.
(or (string-match "^\\(&[^& \t]+\\)" text)
(string-match "^\\(:[^: \t]+\\)" text)))
(put-text-property (match-beginning 1) (match-end 1)
'face (if (boundp 'font-lock-type-face)
'font-lock-type-face 'default) text)))
(put-text-property 0 (length text) 'face 'default text)))
text))
(defun ecb-get-method-sig (method-token)
"Returns the complete method-signature as a string and does also the
highlighting of the methods if `ecb-font-lock-methods' is not nil."
;; all strings i this method must be build with concat and not with format
;; because format does not preserve text-properties!
(let* ((method-type (semantic-token-type method-token))
(return-type (ecb-highlight-text
(if (and ecb-show-method-return-type
(> (length method-type) 0))
(if (listp method-type)
(car method-type) method-type)
"")
ecb-returntype))
(method-and-args
(concat
(ecb-highlight-text (semantic-token-name method-token)
ecb-methodname)
" ("
(if ecb-show-method-arguments
(mapconcat
(lambda(method-arg-token)
(let ((method-arg-type
(ignore-errors
(semantic-token-type method-arg-token))))
(if method-arg-type
(concat (ecb-highlight-text (if (listp method-arg-type)
(car method-arg-type)
method-arg-type)
ecb-argumenttype)
(ecb-highlight-text
(if (eq ecb-show-method-arguments 'type-and-name)
(concat " "
(if (listp method-arg-token)
(car method-arg-token)
method-arg-token)))
ecb-argumentname))
;; there is no type so we probably have an untyped language
;; like emacs-lisp etc. In such a case we display the
;; argument-name.
(ecb-highlight-text (if (listp method-arg-token)
(car method-arg-token)
method-arg-token)
ecb-argumentname))))
(delete nil (semantic-token-function-args method-token))
;; dependent of the language we separate the args either with a
;; space or with a comma. With this trick there is no need to
;; recognice in lisp-like languages such keywords like &optional to
;; set the commas correct.
(if (memq major-mode ecb-language-modes-args-separated-with-space)
" "
", ")))
")")))
;; now lets build the complete signature
(cond ((eq ecb-show-method-return-type 'before)
(concat return-type
(if (> (length return-type) 0) " " "")
method-and-args))
((eq ecb-show-method-return-type 'after)
(concat method-and-args
(if (> (length return-type) 0) " : " "")
return-type))
(t method-and-args))))
(defun ecb-get-variable-text (var-token)
(let ((type (semantic-token-type var-token)))
(concat (ecb-highlight-text (semantic-token-name var-token) ecb-variablename)
(if type
(concat " : " (ecb-highlight-text
(cond ((semantic-token-p type)
(semantic-prototype-nonterminal type))
(t type))
ecb-variabletype))
""))))
(defun ecb-add-classes (node token &optional flatten)
(let ((children (semantic-find-nonterminal-by-token 'type token)))
(dolist (type children)
(let ((n (if (and flatten (= 1 (length children)))
node
(tree-node-new (ecb-highlight-text (semantic-token-name type)
ecb-classtype)
0 type)))
(parents (delq nil
(mapcar (function
(lambda (p)
(if (or (not p)
(not ecb-exclude-parents-regexp)
(not (string-match
ecb-exclude-parents-regexp p)))
p)))
(semantic-token-type-parent type)))))
(when (and parents ecb-show-parents)
(let ((pn (tree-node-new "[Parents]" 1 nil)))
(dolist (p (if (listp parents) parents (list parents)))
(tree-node-add-child
pn
(tree-node-new (ecb-highlight-text p ecb-classtype) 2 p t)))
(when (eq ecb-show-parents 'expanded)
(tree-node-set-expanded pn t))
(tree-node-add-child n pn)))
(unless (and flatten (= 1 (length children)))
(tree-node-add-child node n))
(ecb-add-tokens n (semantic-token-type-parts type))))))
(defun ecb-add-methods (node token methods)
(if ecb-sort-methods
(setq methods (sort methods (lambda(a b)
(string< (semantic-token-name a)
(semantic-token-name b))))))
(dolist (method methods)
(tree-node-add-child node (tree-node-new
(ecb-get-method-sig method) 0
method t))))
(defun ecb-add-variables (node token variables)
(when (and ecb-show-variables variables)
(let ((var-node node))
(when (eq ecb-show-variables 'collapsed)
(setq var-node (tree-node-new "[Variables]" 1 nil))
(tree-node-add-child node var-node))
(if ecb-sort-variables
(setq variables (sort variables (lambda(a b)
(string< (semantic-token-name a)
(semantic-token-name b))))))
(dolist (var variables)
(tree-node-add-child var-node (tree-node-new
(ecb-get-variable-text var)
0 var t))))))
(defun ecb-add-tokens (node token &optional flatten)
;; Semantic 1.4beta2 fix for EIEIO class parts
;; They are really strings, but here converted to tokens
(if (stringp (car token))
(setq token (mapcar (lambda (s) (list s 'variable nil)) token)))
(let ((methods (semantic-find-nonterminal-by-token 'function token))
(variables (semantic-find-nonterminal-by-token 'variable token)))
(setq flatten (and flatten
(not methods)
(not (and variables ecb-show-variables))))
(tree-node-set-expanded node t)
(when (eq ecb-show-classes 'before)
(ecb-add-classes node token flatten))
(ecb-add-variables node token variables)
(ecb-add-methods node token methods)
(when (eq ecb-show-classes 'after)
(ecb-add-classes node token flatten))))
(defun ecb-expand-tree (path node)
(catch 'exit
(dolist (child (tree-node-get-children node))
(let ((name (tree-node-get-name child)))
(when (and (>= (length path) (length name))
(string= (substring path 0 (length name)) name)
(or (= (length path) (length name))
(eq (elt path (length name)) ecb-directory-sep-char)))
(let ((was-expanded (tree-node-is-expanded child)))
(tree-node-set-expanded child t)
(ecb-update-directory-node child)
(throw 'exit
(or (when (> (length path) (length name))
(ecb-expand-tree (substring path (1+ (length name)))
child))
(not was-expanded)))))))))
(defun ecb-get-source-files (dir files)
(let (source-files)
(dolist (file files)
(let ((long-file-name (concat dir ecb-directory-sep-string file)))
(if (and (not (file-directory-p long-file-name))
(or (string-match (cadr ecb-source-file-regexps) file)
(not (string-match (car ecb-source-file-regexps) file))))
(setq source-files (append source-files (list file))))))
source-files))
(defun ecb-set-selected-directory (path)
(let ((last-dir ecb-path-selected-directory))
(save-selected-window
(setq ecb-path-selected-directory (ecb-fix-filename path))
(when (or (not ecb-show-sources-in-directories-buffer)
ecb-auto-expand-directory-tree)
(ecb-exec-in-directories-window
(when ecb-auto-expand-directory-tree
;; Expand tree to show selected directory
(if (ecb-expand-tree ecb-path-selected-directory (tree-buffer-get-root))
(tree-buffer-update)))
(when (not ecb-show-sources-in-directories-buffer)
(tree-buffer-highlight-node-data ecb-path-selected-directory))))
(ecb-exec-in-sources-window
(let ((old-children (tree-node-get-children (tree-buffer-get-root))))
(tree-node-set-children (tree-buffer-get-root) nil)
(ecb-tree-node-add-files
(tree-buffer-get-root)
ecb-path-selected-directory
(ecb-get-source-files
ecb-path-selected-directory
(directory-files ecb-path-selected-directory nil nil t))
0
ecb-show-source-file-extension
old-children ecb-sources-sort-method t))
(tree-buffer-update)
(when (not (string= last-dir ecb-path-selected-directory))
(tree-buffer-scroll (point-min) (point-min))))))
(ecb-mode-line-format))
(defun ecb-get-source-name (filename)
"Returns the source name of a file."
(let ((f (file-name-nondirectory filename)))
(if ecb-show-source-file-extension
f
(file-name-sans-extension f))))
(defun ecb-select-source-file (filename)
"Updates the directories, sources and history buffers to match the filename
given."
(save-selected-window
(ecb-set-selected-directory (file-name-directory filename))
(setq ecb-path-selected-source filename)
;; Update directory buffer
(when ecb-show-sources-in-directories-buffer
(ecb-exec-in-directories-window
(tree-buffer-highlight-node-data ecb-path-selected-source)))
;; Update source buffer
(ecb-exec-in-sources-window
(tree-buffer-highlight-node-data ecb-path-selected-source))
;; Update history buffer
(ecb-exec-in-history-window
(tree-node-remove-child-data (tree-buffer-get-root) ecb-path-selected-source)
(tree-node-set-children
(tree-buffer-get-root)
(let ((history-items
(cons
(tree-node-new (ecb-get-source-name ecb-path-selected-source) 0
ecb-path-selected-source t
(tree-buffer-get-root))
(tree-node-get-children (tree-buffer-get-root)))))
(if ecb-sort-history-items
(sort history-items
(function (lambda (l r) (string< (tree-node-get-name l)
(tree-node-get-name r)))))
history-items)))
(tree-buffer-update)
(tree-buffer-highlight-node-data ecb-path-selected-source))))
(defun ecb-update-methods-after-saving ()
"Updates the methods-buffer after saving if this option is turned on and if
current-buffer is saved."
(when (and (equal (selected-frame) ecb-frame)
ecb-auto-update-methods-after-save
ecb-last-edit-window-with-point
;; this prevents updating the method buffer after saving a not
;; current buffer (e.g. with `save-some-buffers'), because this
;; would result in displaying a method-buffer not belonging to the
;; current source-buffer.
(equal (current-buffer)
(window-buffer ecb-last-edit-window-with-point)))
(ecb-select-source-file ecb-path-selected-source)
(ecb-update-methods-buffer--internal)))
;; This variable is only set and evaluated by the functions
;; `ecb-update-methods-buffer--internal' and
;; `ecb-rebuild-methods-buffer-after-parsing'!
(defvar ecb-method-buffer-needs-rebuild t)
(defun ecb-update-methods-buffer--internal (&optional scroll-to-top)
"Updates the methods buffer with the current buffer. The only thing what
must be done is to start the toplevel parsing of semantic, because the rest is
done by `ecb-rebuild-methods-buffer-after-parsing' because this function is in
the `semantic-after-toplevel-bovinate-hook'.
If optional argument SCROLL-TO-TOP is non nil then the method-buffer is
displayed with window-start and point at beginning of buffer."
(when (and (equal (selected-frame) ecb-frame)
(get-buffer-window ecb-methods-buffer-name))
;; Set here `ecb-method-buffer-needs-rebuild' to t so we can see below if
;; `ecb-rebuild-methods-buffer-after-parsing' was called auto. after
;; `semantic-bovinate-toplevel'.
(setq ecb-method-buffer-needs-rebuild t)
(semantic-bovinate-toplevel t)