Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| ;;; ecb-common-browser.el --- common browsing stuff for Emacs | |
| ;; Copyright (C) 2000 - 2015 Jesper Nordenberg, | |
| ;; Klaus Berndl, | |
| ;; Kevin A. Burton, | |
| ;; Ryan Ware, | |
| ;; Free Software Foundation, Inc. | |
| ;; Author: Jesper Nordenberg <mayhem@home.se> | |
| ;; Klaus Berndl <klaus.berndl@sdm.de> | |
| ;; Kevin A. Burton <burton@openprivacy.org> | |
| ;; Ryan Ware <ryan.r.ware@intel.com> | |
| ;; Maintainer: Ryan Ware <ryan.r.ware@intel.com> | |
| ;; Keywords: browser, code, programming, tools | |
| ;; Created: 2004 | |
| ;; 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 PATICULAR 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. | |
| ;; $Id$ | |
| ;;; History | |
| ;; | |
| ;; For the ChangeLog of this file see the CVS-repository. For a complete | |
| ;; history of the ECB-package see the file NEWS. | |
| ;;; Code: | |
| (eval-when-compile | |
| (require 'silentcomp)) | |
| (require 'ecb-util) | |
| (require 'tree-buffer) | |
| (require 'ecb-mode-line) | |
| (require 'ecb-navigate) | |
| (eval-when-compile | |
| ;; to avoid compiler grips | |
| (require 'cl)) | |
| (silentcomp-defvar modeline-map) | |
| (defgroup ecb-tree-buffer nil | |
| "General settings related to the tree-buffers of ECB." | |
| :group 'ecb | |
| :prefix "ecb-") | |
| (defcustom ecb-bucket-node-display '("" "" ecb-bucket-node-face) | |
| "*How ECB displays bucket-nodes in a ECB tree-buffer. | |
| Bucket-nodes have only one job: Nodes with similar properties will be dropped | |
| into one bucket for such a common property and all these nodes will be added | |
| as children to the bucket-node. Besides being expandable and collapsable a | |
| bucket-node has no senseful action assigned. Examples for bucket-nodes are | |
| \"[+] Variables\", \"[+] Dependencies\" etc. in the Methods-buffer or buckets | |
| which combine filenames with same extension under a bucket-node with name this | |
| extension. | |
| This option defines how bucket-node should be displayed. The name of the | |
| bucket-node is computed by ECB but you can define a prefix, a suffix and a | |
| special face for the bucket-node | |
| The default are empty prefix/suffix-strings and 'ecb-bucket-node-face'. But | |
| an alternative can be for example '\(\"[\" \"]\" nil) which means no special | |
| face and a display like \"[+] [<bucket-name>]\"." | |
| :group 'ecb-tree-buffer | |
| :set (function (lambda (symbol value) | |
| (set symbol value) | |
| (ecb-clear-tag-tree-cache))) | |
| :type '(list (string :tag "Bucket-prefix" :value "[") | |
| (string :tag "Bucket-suffix" :value "]") | |
| (choice :tag "Bucket-face" :menu-tag "Bucket-face" | |
| (const :tag "No special face" :value nil) | |
| (face :tag "Face" :value ecb-bucket-node-face))) | |
| :initialize 'custom-initialize-default) | |
| (defcustom ecb-use-speedbar-instead-native-tree-buffer nil | |
| "*If true then uses speedbar for directories, sources or methods. | |
| This means that speedbar is integrated in the ECB-frame and is displayed in | |
| that window normally displaying the standard ECB-directories-buffer, | |
| ECB-sources-buffer or ECB-methods-buffer. | |
| This option takes effect in all layouts which contain either a directory | |
| window, a sources window or a method window. | |
| This option can have four valid values: | |
| - nil: Do not use speedbar \(default) | |
| - dir: Use speedbar instead of the standard directories-buffer | |
| - source: Use speedbar instead of the standard sources-buffer | |
| - method: Use speedbar instead of the standard methods-buffer | |
| Note: For directories and sources a similar effect and usability is available | |
| by setting this option to nil \(or 'method) and setting | |
| `ecb-show-sources-in-directories-buffer' to not nil, because this combination | |
| displays also directories and sources in one window. | |
| `ecb-use-speedbar-instead-native-tree-buffer' is for people who like the | |
| speedbar way handling directories and source-files or methods and want it in | |
| conjunction with ECB." | |
| :group 'ecb-tree-buffer | |
| :group 'ecb-directories | |
| :group 'ecb-sources | |
| :group 'ecb-methods | |
| :type '(radio (const :tag "Do not use speedbar" :value nil) | |
| (const :tag "For directories" :value dir) | |
| (const :tag "For sources" :value source) | |
| (const :tag "For methods" :value method)) | |
| :initialize 'custom-initialize-default | |
| :set (function (lambda (sym val) | |
| (set sym val) | |
| (if (and (boundp 'ecb-minor-mode) ecb-minor-mode) | |
| (ecb-redraw-layout-full))))) | |
| (defvar ecb-tree-do-not-leave-window-after-select--internal nil | |
| "Only set by customizing `ecb-tree-do-not-leave-window-after-select' or | |
| calling `ecb-toggle-do-not-leave-window-after-select'! Do not set this | |
| variable directly, it is only for internal uses!") | |
| (defcustom ecb-tree-do-not-leave-window-after-select nil | |
| "*Tree-buffers which stay selected after a key- or mouse-selection. | |
| If a buffer \(either its name or the variable-symbol which holds the name) is | |
| contained in this list then selecting a tree-node either by RET or by a | |
| mouse-click doesn't leave that tree-buffer after the node-selection but | |
| performes only the appropriate action \(opening a new source, selecting a | |
| method etc.) but point stays in the tree-buffer. In tree-buffers not contained | |
| in this option normaly a node-selection selects as \"last\" action the right | |
| edit-window or maximizes the next senseful tree-buffer in case of a currently | |
| maximized tree-buffer \(see `ecb-maximize-next-after-maximized-select'). | |
| The buffer-name can either be defined as plain string or with a symbol which | |
| contains the buffer-name as value. The latter one is recommended for the | |
| builtin ECB-tree-buffers because then simply the related option-symbol can be | |
| used. | |
| A special remark for the `ecb-directories-buffer-name': Of course here the | |
| value of this option is only relevant if the name of the current layout is | |
| contained in `ecb-show-sources-in-directories-buffer' or if the value of | |
| `ecb-show-sources-in-directories-buffer' is 'always and the clicked ot hitted | |
| node represents a sourcefile \(otherwise this would not make any sense)! | |
| The setting in this option is only the default for each tree-buffer. With the | |
| command `ecb-toggle-do-not-leave-window-after-select' the behavior of a | |
| node-selection can be changed fast and easy in a tree-buffer without | |
| customizing this option, but of course not for future Emacs sessions!" | |
| :group 'ecb-tree-buffer | |
| :set (function (lambda (sym val) | |
| (set sym val) | |
| (setq ecb-tree-do-not-leave-window-after-select--internal | |
| (ecb-copy-list val)))) | |
| :type '(repeat (choice :menu-tag "Buffer-name" | |
| (string :tag "Buffer-name as string") | |
| (symbol :tag "Symbol holding buffer-name")))) | |
| (defcustom ecb-tree-indent 4 | |
| "*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-tree-buffer | |
| :group 'ecb-most-important | |
| :type 'integer) | |
| (defcustom ecb-tree-expand-symbol-before t | |
| "*Show the expand symbol before the items in a tree. | |
| When the expand-symbol is located before the items then the tree looks like: | |
| \[-] ECB | |
| \[+] code-save | |
| \[-] ecb-images | |
| \[-] directories | |
| When located after then the tree looks like: | |
| ECB \[-] | |
| code-save \[+] | |
| ecb-images \[-] | |
| directories \[-] | |
| The after-example above use a value of 2 for `ecb-tree-indent' whereas the | |
| before-example uses a value of 4. | |
| It is recommended to display the expand-symbol before because otherwise it | |
| could be that with a deep nested item-structure with and/or with long | |
| item-names \(e.g. a deep directory-structure with some long | |
| subdirectory-names) the expand-symbol is not visible in the tree-buffer and | |
| the tree-buffer has to be horizontal scrolled to expand an item." | |
| :group 'ecb-tree-buffer | |
| :group 'ecb-most-important | |
| :type 'boolean) | |
| ;; we can savely set here 'image as default because the tree-buffer-lib checks | |
| ;; itslef if image-support is possible and switches back to 'ascii-style if not. | |
| (defcustom ecb-tree-buffer-style 'image | |
| "*The style of the tree-buffers. | |
| There are three different styles available: | |
| Image-style \(value 'image): | |
| Very nice and modern - just try it. For this style the options | |
| `ecb-tree-indent' and `ecb-tree-expand-symbol-before' have no effect! | |
| The value 'image means use image-style if images can be displayed with current | |
| Emacs-setup \(otherwise auto. 'ascii-style is used). | |
| Note: GNU Emacs <= 21.3.X for Windows does not support image-display so ECB | |
| uses always 'ascii-guides even when here 'image is set! | |
| Ascii-style with guide-lines \(value 'ascii-guides): | |
| \[-] ECB | |
| | \[+] code-save | |
| `- \[-] ecb-images | |
| | \[-] directories | |
| | | \[-] height-15 | |
| | | | * close.xpm | |
| | | | * empty.xpm | |
| | | | * leaf.xpm | |
| | | `- * open.xpm | |
| | | \[+] height-17 | |
| | | \[+] height-19 | |
| | `- \[+] height-21 | |
| | \[x] history | |
| | \[x] methods | |
| `- \[x] sources | |
| Ascii-style without guide-lines \(value 'ascii-no-guides) - this is the style | |
| used by ECB <= 1.96: | |
| \[-] ECB | |
| \[+] code-save | |
| \[-] ecb-images | |
| \[-] directories | |
| \[-] height-15 | |
| * close.xpm | |
| * empty.xpm | |
| * leaf.xpm | |
| * open.xpm | |
| \[+] height-17 | |
| \[+] height-19 | |
| \[+] height-21 | |
| \[x] history | |
| \[x] methods | |
| \[x] sources | |
| With both ascii-styles the tree-layout can be affected with the options | |
| `ecb-tree-indent' and `ecb-tree-expand-symbol-before'." | |
| :group 'ecb-tree-buffer | |
| :group 'ecb-most-important | |
| :type '(radio (const :tag "Images-style" :value image) | |
| (const :tag "Ascii-style with guide-lines" :value ascii-guides) | |
| (const :tag "Ascii-style w/o guide-lines" :value ascii-no-guides))) | |
| ;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: add here the analyse buffer if | |
| ;; additonal images are necessary - but currently i don't think we need | |
| ;; special images for this analyse-stuff. | |
| (defcustom ecb-tree-image-icons-directories | |
| (let ((base (concat (if ecb-regular-xemacs-package-p | |
| (format "%s" (locate-data-directory "ecb")) | |
| ecb-ecb-dir) | |
| "ecb-images/"))) | |
| (cons (concat base "default/height-17") | |
| (mapcar (function (lambda (i) | |
| (cons (car i) (concat base (cdr i))))) | |
| '((ecb-directories-buffer-name . "directories/height-17") | |
| (ecb-sources-buffer-name . "sources/height-14_to_21") | |
| (ecb-methods-buffer-name . "methods/height-14_to_21"))))) | |
| "*Directories where the images for the tree-buffer can be found. | |
| This is a cons cell where: | |
| car: Default directory where the default images for the tree-buffer can be | |
| found. It should contain an image for every name of | |
| `tree-buffer-tree-image-names'. The name of an image-file must be: | |
| \"ecb-<NAME of TREE-BUFFER-TREE-IMAGE-NAMES>.<ALLOWED EXTENSIONS>\". | |
| cdr: This is a list where each element is a cons again with: car is the buffer | |
| name of the tree-buffer for which a special image-path should be used. The | |
| buffer-name can either be defined as plain string or with a symbol which | |
| contains the buffer-name as value. The latter one is recommended for the | |
| builtin ECB-tree-buffers because then simply the related option-symbol can be | |
| used \(e.g. the symbol `ecb-directories-buffer-name'). The cdr is the the | |
| full-path of an additional image-directorie which is searched first for images | |
| needed for the related tree-buffer. If the image can not be found in this | |
| directory then the default-directory \(see above) is searched. If the | |
| image can't even be found there the related ascii-symbol is used - which is | |
| defined in `tree-buffer-tree-image-names'. If a tree-buffer is not contained | |
| in this list then there is no additional special image-directory for it. | |
| ECB comes with predefined images in several different heights - so for the | |
| most senseful font-heights of a tree-buffer a fitting image-size should be | |
| available. The images reside either in the subdirectory \"ecb-images\" of the | |
| ECB-installation or - if ECB is installed as regular XEmacs-package - in the | |
| ECB-etc data-directory \(the directory returned by \(locate-data-directory | |
| \"ecb\")." | |
| :group 'ecb-tree-buffer | |
| :type '(cons (directory :tag "Full default image-path") | |
| (repeat (cons (choice :menu-tag "Buffer-name" | |
| (string :tag "Buffer-name as string") | |
| (symbol :tag "Symbol holding | |
| buffer-name")) | |
| (directory :tag "Full image-path for this tree-buffer"))))) | |
| ;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: rename this to ecb-truncate-lines. | |
| (defcustom ecb-tree-truncate-lines '(ecb-directories-buffer-name | |
| ecb-sources-buffer-name | |
| ecb-methods-buffer-name | |
| ecb-history-buffer-name | |
| ecb-analyse-buffer-name) | |
| "*Truncate lines in ECB buffers. | |
| If a buffer \(either its name or the variable-symbol which holds the name) is | |
| contained in this list then line-truncation is switched on for this buffer | |
| otherwise it is off. | |
| The buffer-name can either be defined as plain string or with a symbol which | |
| contains the buffer-name as value. The latter one is recommended to switch on | |
| line-truncation for one of the builtin ECB-tree-buffers because then simply | |
| the related option-symbol can be used. To truncate lines in the builtin | |
| directories tree-buffer just add the symbol `ecb-directories-buffer-name' to | |
| this option. | |
| If you change this during ECB is activated you must deactivate and activate | |
| ECB again to take effect." | |
| :group 'ecb-tree-buffer | |
| :group 'ecb-most-important | |
| :type '(repeat (choice :menu-tag "Buffer-name" | |
| (string :tag "Buffer-name as string") | |
| (symbol :tag "Symbol holding buffer-name")))) | |
| (defcustom ecb-tree-easy-hor-scroll 5 | |
| "*Scroll step for easy hor. scrolling via mouse-click in tree-buffers. | |
| XEmacs has horizontal scroll-bars so invisible parts beyond the right | |
| window-border of a tree-buffer can always made visible very easy. | |
| GNU Emacs does not have hor. scroll-bars so especially with the mouse it is | |
| quite impossible to scroll smoothly right and left. The functions | |
| `scroll-left' and `scroll-right' can be annoying and are also not bound to | |
| mouse-buttons. | |
| If this option is a positive integer S then in all ECB-tree-buffers the keys | |
| \[M-mouse-1] and \[M-mouse-3] are bound to scrolling left rsp. right with | |
| scroll-step S - clicking with mouse-1 or mouse-2 onto the edge of the modeline | |
| has the same effect, i.e. if you click with mouse-1 onto the left \(rsp. | |
| right) edge of the modeline you will scroll left \(rsp. right). Additionally | |
| \[C-M-mouse-1] and \[C-M-mouse-3] are bound to scrolling left rsp. right with | |
| scroll-step `window-width' - 2. Default is a scroll-step of 5. If the value is | |
| nil then no keys for horizontal scrolling are bound." | |
| :group 'ecb-tree-buffer | |
| :type '(radio :value 5 | |
| (const :tag "No hor. mouse scrolling" :value nil) | |
| (integer :tag "Scroll step"))) | |
| (defcustom ecb-tree-make-parent-node-sticky t | |
| "*Make the parent-node sticky in the headerline of the tree-buffer. | |
| If not nil then the first line of the tree-buffer is used as header-line which | |
| is used to display the next unvisible parent of the first visible node as | |
| sticky, so always the parent of a node is visible and clickable. If a node has | |
| no parent then just the next node above is displayed in the header-line. The | |
| node displayed in the header-line is exactly in the same manner clickable as | |
| all other nodes. | |
| See also `ecb-tree-stickynode-indent-string'. | |
| This feature is only available with Gnu Emacs, not with XEmacs." | |
| :group 'ecb-tree-buffer | |
| :group 'ecb-most-important | |
| :type 'boolean) | |
| (defcustom ecb-tree-stickynode-indent-string (tree-buffer-sticky-default-indent-string) | |
| "*String used to indent the stickynode. | |
| This string is used to match the space used by scrollbars and | |
| fringe so it does not appear that the node-name is moving left/right | |
| when it lands in the sticky line. | |
| Normally the needed value is computed automatically by ECB. But if the result | |
| is not matching this option allows to customize the indent-string. | |
| The default value is computed by the function | |
| `tree-buffer-sticky-default-indent-string', so you can change the needed value | |
| with that starting-point. | |
| Changing this option takes only effect after restarting Emacs!" | |
| :group 'ecb-tree-buffer | |
| :type 'string) | |
| ;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: maybe we should change this to a | |
| ;; type analogous to ecb-tree-truncate-lines | |
| (defcustom ecb-truncate-long-names t | |
| "*Truncate long names that don't fit in the width of the ECB windows. | |
| If you change this during ECB is activated you must deactivate and activate | |
| ECB again to take effect." | |
| :group 'ecb-tree-buffer | |
| :type 'boolean) | |
| ;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: maybe we should change this to a | |
| ;; type analogous to ecb-tree-truncate-lines | |
| (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-tree-buffer | |
| :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-tree-navigation-by-arrow t | |
| "*Enable smart navigation in the tree-windows by horizontal arrow-keys. | |
| If not nil then the left- and right-arrow keys work in the ECB tree-window in | |
| the following smart way if onto an expandable node: | |
| + Left-arrow: If node is expanded then it will be collapsed otherwise point | |
| jumps to the next \"higher\" node in the hierarchical tree \(higher means | |
| the next higher tree-level or - if no higher level available - the next | |
| higher node on the same level). | |
| + Right-arrow: If node is not expanded then it will be expanded. | |
| Onto a not expandable node the horizontal arrow-keys go one character in the | |
| senseful correct direction. | |
| If this option is changed the new value takes first effect after deactivating | |
| ECB and then activating it again!" | |
| :group 'ecb-tree-buffer | |
| :type 'boolean) | |
| (defun ecb-show-any-node-info-by-mouse-moving-p () | |
| "Return not nil if for at least one tree-buffer showing node info only by | |
| moving the mouse over a node is activated. See | |
| `ecb-directories-show-node-info' etc...." | |
| (let ((when-list (mapcar (function (lambda (elem) | |
| (car (symbol-value elem)))) | |
| '(ecb-directories-show-node-info | |
| ecb-sources-show-node-info | |
| ecb-methods-show-node-info | |
| ecb-history-show-node-info | |
| ecb-analyse-show-node-info | |
| )))) | |
| (or (member 'if-too-long when-list) | |
| (member 'always when-list)))) | |
| (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-mouse-click-destination'. | |
| - ECB Methods: Jumping to the method in that edit-window specified by the | |
| option `ecb-mouse-click-destination'. | |
| A click with the primary mouse-button while the SHIFT-key is pressed called | |
| the POWER-click and does the following \(depending on the ECB-buffer where the | |
| POWER-click occurs): | |
| + Directory-buffer: Refreshing the directory-contents-cache \(see | |
| `ecb-cache-directory-contents'). | |
| + Sources- and History-buffer: Only displaying the source-contents in the | |
| method-buffer but not displaying the source-file in the edit-window. | |
| + Methods-buffer: Narrowing to the clicked method/variable/ect... \(see | |
| `ecb-tag-visit-post-actions'). This works only for sources supported by | |
| semantic! | |
| In addition always the whole node-name is displayed in the minibuffer after a | |
| POWER-click \(for this see `ecb-directories-show-node-info' etc...). | |
| The secondary mouse-button is for opening \(jumping to) the file in another | |
| edit-window \(see the documentation `ecb-mouse-click-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 | |
| Note: If the tree-buffers are used with the keyboard instead with the mouse | |
| then [RET] is interpreted as primary mouse-button and [C-RET] as secondary | |
| mouse-button! | |
| If you change this during ECB is activated you must deactivate and activate | |
| ECB again to take effect!" | |
| :group 'ecb-tree-buffer | |
| :group 'ecb-most-important | |
| :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))) | |
| (defcustom ecb-tree-mouse-action-trigger 'button-release | |
| "*When the tree-buffer mouse-action should be triggered. | |
| This option determines the moment a mouse-action in a tree-buffer is | |
| triggered. This can be either direct after pressing a mouse-button \(value | |
| 'button-press) or not until releasing the mouse-button \(value: | |
| 'button-release). | |
| If you change this during ECB is activated you must deactivate and activate | |
| ECB again to take effect!" | |
| :group 'ecb-tree-buffer | |
| :type '(radio (const :tag "After button release" :value button-release) | |
| (const :tag "After button press" :value button-press))) | |
| (defcustom ecb-mouse-click-destination 'last-point | |
| "*Destination of a mouse-button click. | |
| Defines in which edit-window \(if splitted) ECB does the \"right\" action | |
| \(opening a source, jumping to a method/variable etc.) after clicking with a | |
| mouse-button \(see `ecb-primary-secondary-mouse-buttons') onto a node. 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. | |
| This is if the user has clicked either with the primary mouse-button or | |
| has activated a popup-menu in the tree-buffer. | |
| A click with the secondary mouse-button \(see again | |
| `ecb-primary-secondary-mouse-buttons') does the \"right\" action always in | |
| another edit-window related to the setting in this option: If there are two | |
| edit-windows then the \"other\" edit-window is used and for more than 2 | |
| edit-windows the \"next\" edit-window is used \(whereas the next edit-window | |
| of the last edit-window is the first edit-window). | |
| If the edit-window is not splitted this setting has no effect. | |
| Note: If the tree-buffers are used with the keyboard instead with the mouse | |
| then this option takes effect too because [RET] is interpreted as primary | |
| mouse-button and [C-RET] as secondary mouse-button!" | |
| :group 'ecb-tree-buffer | |
| :group 'ecb-most-important | |
| :type '(radio (const :tag "Left/topmost edit-window" | |
| :value left-top) | |
| (const :tag "Last edit-window with point" | |
| :value last-point))) | |
| (defcustom ecb-common-tree-buffer-after-create-hook nil | |
| "*Local hook running at the end of each tree-buffer creation. | |
| Every function of this hook is called once without arguments direct after | |
| creating a tree-buffer of ECB and it's local key-map. So for example a function | |
| could be added which performs calls of `local-set-key' to define new | |
| key-bindings for EVERY tree-buffer. | |
| The following keys must not be rebind in all tree-buffers: | |
| - <RET> and all combinations with <Shift> and <Ctrl> | |
| - <TAB> | |
| - `C-t'" | |
| :group 'ecb-tree-buffer | |
| :type 'hook) | |
| (defcustom ecb-basic-buffer-sync '(Info-mode dired-mode) | |
| "*Synchronize the basic ECB-buffers automatically with current edit buffer. | |
| The basic ECB-buffers are the buffers for directories, sources, methods and | |
| history. | |
| If 'always then the synchronization takes place always a buffer changes in the | |
| edit window, if nil then never. If a list of major-modes then only if the | |
| `major-mode' of the new buffer belongs NOT to this list. | |
| But in every case the synchronization takes only place if the current-buffer | |
| in the edit-window has a relation to files or directories. Examples for the | |
| former one are all programming-language-modes, `Info-mode' too, an example for | |
| the latter one is `dired-mode'. For all major-modes related to | |
| non-file/directory-buffers like `help-mode', `customize-mode' and others never | |
| an autom. synchronization will be done! | |
| It's recommended to exclude at least `Info-mode' because it makes no sense to | |
| synchronize the ECB-windows after calling the Info help. Per default also | |
| `dired-mode' is excluded but it can also making sense to synchronize the | |
| ECB-directories/sources windows with the current directory in the | |
| dired-buffer. | |
| IMPORTANT NOTE: Every time the synchronization is done the hook | |
| `ecb-basic-buffer-sync-hook' is evaluated." | |
| :group 'ecb-tree-buffer | |
| :type '(radio :tag "Synchronize basic ECB buffers" | |
| (const :tag "Always" :value always) | |
| (const :tag "Never" nil) | |
| (repeat :tag "Not with these modes" | |
| (symbol :tag "mode")))) | |
| (defcustom ecb-basic-buffer-sync-delay 0.25 | |
| "*Time Emacs must be idle before the special ECB-buffers are synchronized. | |
| Synchronizing is done with the current source displayed in the edit window. If | |
| nil then there is no delay, means synchronization takes place immediately. A | |
| small value of about 0.25 seconds saves CPU resources and you get even though | |
| almost the same effect as if you set no delay." | |
| :group 'ecb-tree-buffer | |
| :type '(radio (const :tag "No synchronizing delay" | |
| :value nil) | |
| (number :tag "Idle time before synchronizing" | |
| :value 0.25)) | |
| :set (function (lambda (symbol value) | |
| (set symbol value) | |
| (if ecb-minor-mode | |
| (ecb-activate-ecb-autocontrol-function | |
| value 'ecb-basic-buffer-sync)))) | |
| :initialize 'custom-initialize-default) | |
| (defcustom ecb-basic-buffer-sync-hook nil | |
| "*Hook run at the end of `ecb-basic-buffer-sync'. | |
| See documentation of `ecb-basic-buffer-sync' for conditions when | |
| synchronization takes place and so in turn these hooks are evaluated. | |
| Precondition for such a hook: | |
| Current buffer is the buffer of the currently selected edit-window. | |
| Postcondition for such a hook: | |
| Point must stay in the same edit-window as before evaluating the hook. | |
| Important note: If the option `ecb-basic-buffer-sync' is not nil | |
| the function `ecb-basic-buffer-sync' is running either every time | |
| Emacs is idle or even after every command \(see | |
| `ecb-basic-buffer-sync-delay'). So these hooks can be really | |
| called very often! Therefore each function of this hook | |
| should/must check in an efficient way at beginning if its task | |
| have to be really performed and then do them only if really | |
| necessary! Otherwise performance of Emacs could slow down | |
| dramatically! | |
| It is strongly recommended that each function added to this hook uses the | |
| macro `ecb-do-if-buffer-visible-in-ecb-frame' at beginning! See | |
| `ecb-speedbar-buffer-sync' and `ecb-eshell-buffer-sync' for | |
| examples how to use this macro!" | |
| :group 'ecb-tree-buffer | |
| :type 'hook) | |
| ;;==================================================== | |
| ;; Internals | |
| ;;==================================================== | |
| ;;; ----- advice stuff ------------------------------------- | |
| (defvar ecb-adviced-function-sets nil | |
| "A list of adviced-function sets defined with `defecb-advice-set'. | |
| Each element is a cons-cell where car is the advice-set-var and cdr is an | |
| indicator if the caller of `ecb-with-original-adviced-function-set' is the | |
| outmost caller. | |
| DO NOT CHANGE THIS!") | |
| (defvar ecb-adviced-permanent-function-sets nil | |
| "A list of symbols, each of them an advice-set which should be permanent. | |
| Permanent means this advice set will not be disabled during deactivation of | |
| ECB. This variable is only set by `defecb-advice-set'. | |
| DO NOT CHANGE THIS!") | |
| (defvar ecb-adviced-functions nil | |
| "A list of all advices defined with `defecb-advice'. | |
| This list is the set union of the values of all function-sets of | |
| `ecb-adviced-function-sets'. | |
| DO NOT CHANGE THIS!") | |
| (defvar ecb-advices-debug-error nil | |
| "It not nil then each advice of ECB reports when it's en/disabled or called.") | |
| (defun ecb-advices-debug-error (advice class action &rest args) | |
| "Run ARGS through `format' and write it to the *Messages*-buffer. | |
| ADVICE is the adviced-function-symbol, CLASS is the advice-class \(after, | |
| around or before) and ACTION is one of the symbols 'calling, 'enabling, | |
| 'disabling or 'reporting. | |
| This will build up a message string like: | |
| ECB <version>: debug <ACTION> of '<CLASS>' advice ADVICE: ARGS. | |
| If ARGS is nil then only the message above is reported." | |
| (when ecb-advices-debug-error | |
| (message (concat (format "ECB %s: debug %s of '%s' advice %s " | |
| ecb-version | |
| action | |
| class | |
| advice) | |
| (if args | |
| (apply 'format args)))))) | |
| (defmacro defecb-advice-set (advice-set docstring &optional permanent) | |
| "Defines an advice-set for ECB. | |
| This defines a variable which will contain adviced functions defined by | |
| `defecb-advice-set'. This is a set of advices which can be enabled or disabled | |
| \"en block\" which must be done either by `ecb-enable-advices', | |
| `ecb-disable-advices' or `ecb-with-original-adviced-function-set'. | |
| Before defining a new advice-set it's recommended to take a look at the value | |
| of `ecb-adviced-function-sets' if there is already a suitable advice-set. | |
| IMPORTANT: Each advice in ECB must be defined by `defecb-advice' and must | |
| belong to an advice-set previously defined by `defecb-advice-set'! | |
| All advice-sets of ECB will be automatically\(!) disabled at load-time of the | |
| ecb-library and at deactivation-time of ECB. But: Enabling of a certain | |
| advice-set must be done appropriately. | |
| If optional argument PERMANENT is t then this advice-set will NOT be disabled | |
| at deactivation-time of ECB! Calling `ecb-disable-advices' for an advice set | |
| defined with permanent is t will take no effect unless the optional argument | |
| FORCE-PERMANENT of this function is set to not nil. | |
| PERMANENT can also be a function which will be called by `ecb-disable-advices' | |
| for this advice set \(the function gets one argument: the symbol of the | |
| advice-set) and have to return not nil if the advice-set should not be disable | |
| by `ecb-disable-advices' unless the FORCE-PERMANENT of this function is set to | |
| not nil. | |
| Example: | |
| \(defecb-advice-set ecb-always-disabled-advices | |
| \"These advices are always disabled.\")" | |
| `(eval-and-compile | |
| (add-to-list 'ecb-adviced-function-sets (cons (quote ,advice-set), nil)) | |
| ,(if permanent | |
| `(add-to-list 'ecb-adviced-permanent-function-sets | |
| (cons (quote ,advice-set) ,permanent))) | |
| (defvar ,advice-set nil ,docstring))) | |
| (put 'defecb-advice-set 'lisp-indent-function 1) | |
| (defmacro defecb-advice (adviced-function advice-class advice-set advice-docstring &rest body) | |
| "Defines an advice for ADVICED-FUNCTION with ADVICE-CLASS for ADVICE-SET. | |
| ADVICED-FUNCTION must be an advicable object \(e.g. a function, a subr | |
| etc...). ADVICE-CLASS must be one of around, after or before. ADVICE-SET must | |
| ba an advice-set previously defined by `defecb-advice-set'. ADVICE-DOCSTRING | |
| ist the docstring for the advice. BODY is the program-code for the advice as | |
| it would be written with `defadvice'. | |
| Do not quote ADVICED-FUNCTION, ADVICE-CLASS and ADVICE-SET. | |
| Example: | |
| \(defecb-advice delete-frame around ecb-layout-basic-adviced-functions | |
| \"If FRAME is equal to the ECB frame then...\" | |
| \(let \(\(frame \(or \(ad-get-arg 0) \(selected-frame)))) | |
| \(if \(and ecb-minor-mode | |
| \(equal frame ecb-frame)) | |
| \(when \(ecb-confirm \"Attempt to delete the ECB-frame....Proceed? \") | |
| \(ecb-deactivate-internal) | |
| ad-do-it) | |
| ad-do-it)))" | |
| `(progn | |
| (if (assoc (quote ,advice-set) ecb-adviced-function-sets) | |
| (add-to-list (quote ,advice-set) | |
| (cons (quote ,adviced-function) (quote ,advice-class))) | |
| (error "The advice-set %s does not exist!" | |
| (symbol-name (quote ,advice-set)))) | |
| (if (not (member (quote ,advice-class) | |
| '(around after before))) | |
| (error "The advice-class %s is not allowed - only around, after and before!" | |
| (symbol-name (quote ,advice-class)))) | |
| (add-to-list 'ecb-adviced-functions (cons (quote ,adviced-function) (quote ,advice-class))) | |
| (eval-and-compile | |
| (defadvice ,adviced-function (,advice-class ecb) | |
| ,advice-docstring | |
| (ecb-advices-debug-error (quote ,adviced-function) | |
| (quote ,advice-class) | |
| 'calling) | |
| ,@body)))) | |
| (put 'defecb-advice 'lisp-indent-function 3) | |
| (defun ecb-enable-ecb-advice (function-symbol advice-class arg) | |
| "If ARG is greater or equal zero then enable the adviced version of | |
| FUNCTION-SYMBOL. Otherwise disable the adviced version. The advice must be | |
| defined with class ADVICE-CLASS by `defecb-advice'. | |
| IMPORTANT: Do not use the function directly. Always use `ecb-enable-advices', | |
| `ecb-disable-advices' or `ecb-with-original-adviced-function-set'!." | |
| (if (< arg 0) | |
| (progn | |
| (ad-disable-advice function-symbol advice-class 'ecb) | |
| (ad-activate function-symbol) | |
| (ecb-advices-debug-error function-symbol advice-class 'disabling)) | |
| (ad-enable-advice function-symbol advice-class 'ecb) | |
| (ad-activate function-symbol) | |
| (ecb-advices-debug-error function-symbol advice-class 'enabling))) | |
| (defun ecb-enable-advices (adviced-function-set-var) | |
| "Enable all advices of ADVICED-FUNCTION-SET-VAR, which must be defined by | |
| `defecb-advice-set'." | |
| (if ecb-advices-debug-error | |
| (message "ECB %s: debug enabling the advice-set: %s" | |
| ecb-version adviced-function-set-var)) | |
| (if (eq adviced-function-set-var 'ecb-always-disabled-advices) | |
| (error "The advice-set ecb-always-disabled-advices must not be enabled!")) | |
| (if (not (assq adviced-function-set-var ecb-adviced-function-sets)) | |
| (error "The adviced function set %s is not defined by defecb-advice-set!" | |
| (symbol-name adviced-function-set-var))) | |
| (dolist (elem (symbol-value adviced-function-set-var)) | |
| (ecb-enable-ecb-advice (car elem) (cdr elem) 1))) | |
| (defun ecb-disable-advices (adviced-function-set-var &optional force-permanent) | |
| "Disable all advices of ADVICED-FUNCTION-SET-VAR, which must be defined by | |
| `defecb-advice-set' | |
| This function tests if ADVICED-FUNCTION-SET-VAR has been defined as permanent | |
| by `defecb-advice-set'. | |
| Calling `ecb-disable-advices' for an advice set defined with | |
| permanent t will take no effect unless the optional argument | |
| FORCE-PERMANENT is set to not nil. If the advice set is defined as permanent | |
| with a permanent-disable-function then this function is called with | |
| ADVICED-FUNCTION-SET-VAR as argument; if this function returns not nil then | |
| the adviced will be treated as permanent and will not being disabled. | |
| If optional FORCE-PERMANENT is not nil then ADVICED-FUNCTION-SET-VAR will | |
| be disabled regardless if permanent or not." | |
| (if ecb-advices-debug-error | |
| (message "ECB %s: debug disabling the advice-set: %s" | |
| ecb-version adviced-function-set-var)) | |
| (if (not (assq adviced-function-set-var ecb-adviced-function-sets)) | |
| (error "The adviced function set %s is not defined by defecb-advice-set!" | |
| (symbol-name adviced-function-set-var))) | |
| (let ((permanent (if force-permanent | |
| nil | |
| (cdr (assq adviced-function-set-var | |
| ecb-adviced-permanent-function-sets))))) | |
| (unless (or (eq permanent t) | |
| (and (functionp permanent) | |
| (funcall permanent adviced-function-set-var))) | |
| (dolist (elem (symbol-value adviced-function-set-var)) | |
| (ecb-enable-ecb-advice (car elem) (cdr elem) -1))))) | |
| ;; for the outmost-caller-stuff see ecb-with-original-adviced-function-set | |
| (defmacro ecb-with-ecb-advice (function-symbol advice-class &rest body) | |
| "Evaluates BODY with the adviced version of FUNCTION-SYMBOL. The advice must | |
| be defined by `defecb-advice' with class ADVICE-CLASS for the advice-set | |
| `ecb-always-disabled-advices'. Otherwise an error occurs. The advice is only | |
| active during BODY. | |
| BODY is protected by `unwind-protect' so in each case the advice | |
| will be disabled after finishing this macro unless it is nested | |
| within a call to this macro for the *same* FUNCTION-SYMBOL and | |
| ADVICE-CLASS-combination! This means that the usage of this macro | |
| is save for arbitrary nested calls, so full BODY is guaranted | |
| being evaluated with enabled ADVICE-CLASS advice for | |
| FUNCTION-SYMBOL. | |
| Returns the value of BODY. | |
| Example where this macro is used for `walk-windows' within another advice: | |
| \(ecb-with-ecb-advice 'walk-windows 'around | |
| ad-do-it)" | |
| (let ((outmost-caller-p (make-symbol "outmost-caller-p"))) | |
| ;; we have to check if we are the outmost-caller of this macro for this | |
| ;; adviced function AND the advice-class! different advice-classes for the | |
| ;; same function have to be treated differently!! | |
| `(let ((,outmost-caller-p (unless (member ,advice-class (get ,function-symbol 'ecb-with-ecb-advice)) | |
| (put ,function-symbol 'ecb-with-ecb-advice | |
| (append (list ,advice-class) (get ,function-symbol 'ecb-with-ecb-advice))) | |
| ,advice-class))) | |
| (if (not (member (cons ,function-symbol ,advice-class) | |
| ecb-always-disabled-advices)) | |
| (error "Advice for %s with class %s not registered in ecb-always-disabled-advices!" | |
| (symbol-name ,function-symbol) | |
| (symbol-name ,advice-class))) | |
| (if ecb-advices-debug-error | |
| (message "ECB %s: debug with always disabled ecb-advice: %s %s - ENTRY" | |
| ecb-version ,advice-class ,function-symbol)) | |
| (unwind-protect | |
| (progn | |
| (when ,outmost-caller-p | |
| (ecb-enable-ecb-advice ,function-symbol ,advice-class 1)) | |
| ,@body) | |
| (when ,outmost-caller-p | |
| ;; Only if we are the outmost caller we are allowed to disable the | |
| ;; enabled advice | |
| (put ,function-symbol 'ecb-with-ecb-advice | |
| (delete ,advice-class (get ,function-symbol 'ecb-with-ecb-advice))) | |
| (ecb-enable-ecb-advice ,function-symbol ,advice-class -1)) | |
| (if ecb-advices-debug-error | |
| (message "ECB %s: debug with always disabled ecb-advice: %s %s - EXIT" | |
| ecb-version ,advice-class ,function-symbol)))))) | |
| (put 'ecb-with-ecb-advice 'lisp-indent-function 2) | |
| ;; (insert (pp (macroexpand '(ecb-with-ecb-advice 'one-window-p 'around | |
| ;; (message ""))))) | |
| (defmacro ecb-with-original-adviced-function-set (adviced-function-set-var &rest body) | |
| "Evaluates BODY with all adviced functions of ADVICED-FUNCTION-SET-VAR | |
| being disabled \(means with their original definition). Restores always \(even | |
| if an error occurs during evaluating BODY) the previous state of the adviced | |
| functions, means it depends if the call to this macro is the outermost call: | |
| Only if it is the outermost-call the advices of the used advice-set will be | |
| disabled after finishing. So full BODY is guaranted being evaluated with | |
| disabled advices of ADVICED-FUNCTION-SET-VAR. | |
| ADVICED-FUNCTION-SET-VAR must be defined by `defecb-advice-set' and all | |
| advices of this set must be defined by `defecb-advice'. Otherwise an error | |
| occurs. | |
| Example: | |
| \(ecb-with-original-adviced-function-set 'ecb-layout-basic-adviced-functions | |
| \(do-something..))" | |
| (let ((outmost-caller-p (make-symbol "outmost-caller-p"))) | |
| `(let ((,outmost-caller-p | |
| (unless (equal (cdr (assq ,adviced-function-set-var ecb-adviced-function-sets)) | |
| 'outmost-caller) | |
| ;; if we are the outmost caller of this macro we store this | |
| ;; for | |
| ;; a) following callers | |
| ;; b) ourself, so we can later reset is | |
| (setcdr (assq ,adviced-function-set-var ecb-adviced-function-sets) 'outmost-caller)) | |
| )) | |
| (if ecb-advices-debug-error | |
| (message "ECB %s: debug with original advice-set: %s - ENTRY" | |
| ecb-version ,adviced-function-set-var)) | |
| (unwind-protect | |
| (progn | |
| (when ,outmost-caller-p | |
| ;; we must force disabling permanent advice-sets too | |
| (ecb-disable-advices ,adviced-function-set-var t)) | |
| ,@body) | |
| (when ,outmost-caller-p | |
| ;; Only if we are the outmost caller we are allowed to re-enable the | |
| ;; disabled advice-set | |
| (setcdr (assq ,adviced-function-set-var ecb-adviced-function-sets) nil) | |
| (ecb-enable-advices ,adviced-function-set-var)) | |
| (if ecb-advices-debug-error | |
| (message "ECB %s: debug with original advice-set: %s - EXIT" | |
| ecb-version ,adviced-function-set-var)))))) | |
| (put 'ecb-with-original-adviced-function-set 'lisp-indent-function 1) | |
| ;; !!!!!!!!!!!!!!!! Caution !!!!!!!!!!!!!!!!!!!!!!!!!! | |
| ;; when editing that file which defines such an advice and then saving and | |
| ;; byte-compiling this file then this reactivates this advice - just a hint - | |
| ;; should be not a problem for ECB because the users should not edit the | |
| ;; ecb-code ;-) But we should have this in mind!!!!!!!!!!!!!!!!!!!!!!! | |
| (defecb-advice-set ecb-always-disabled-advices | |
| "These advices are always disabled. | |
| This advice-set can not be enabled by `ecb-enable-advices' but such an | |
| advice has to be activated 'on demand' by the caller. Such an advice must be | |
| used with the macro `ecb-with-ecb-advice'.") | |
| (defun ecb-source-make (filename &optional buffer) | |
| "Build a source-object from FILENAME and BUFFER. | |
| If optional arg BUFFER is nil then the just FILENAME is returned. | |
| If BUFFER is not nil then it can be either a buffer-object or a buffer-name. | |
| A cons is returned where car is FILENAME and cdr is the buffername of BUFFER." | |
| (let ((buffername (when buffer | |
| (if (bufferp buffer) | |
| (buffer-name buffer) | |
| buffer)))) | |
| (if buffername | |
| (cons filename buffername) | |
| filename))) | |
| (defun ecb-source-get-filename (source) | |
| "SOURCE is either a string, then it is a filename or a cons, then the car is | |
| the filename and the cdr is the buffer-name, whereas the latter one can be the | |
| name of an indirect-buffer." | |
| (if (consp source) | |
| (car source) | |
| source)) | |
| (defun ecb-source-get-buffername (source) | |
| "SOURCE is either a string, then it is a filename or a cons, then the car is | |
| the filename and the cdr is the buffer-name, whereas the latter one can be the | |
| name of an indirect-buffer." | |
| (if (consp source) | |
| (cdr source))) | |
| (defun ecb-source-get-buffer (source) | |
| "Return a living buffer-object for SOURCE. | |
| SOURCE is either a string, then it is a filename or a cons, then the car is | |
| the filename and the cdr is the buffer-name, whereas the latter one can be the | |
| name of an indirect-buffer. | |
| If SOURCE contains a living buffer then this buffer is returned. Otherwise a | |
| buffer for the filename-part of SOURCE is created and returned. For an | |
| existing ans readable file this means the file is loaded into a buffer. | |
| Note: The buffer is just returned but not displayed." | |
| (let* ((my-source (if (consp source) source (cons source nil))) | |
| (filename (car my-source)) | |
| (buffer (ecb-buffer-obj (cdr my-source)))) | |
| (or buffer | |
| (find-file-noselect filename)))) | |
| (defvar ecb-path-selected-directory nil | |
| "Path to currently selected directory.") | |
| (defvar ecb-path-selected-source nil | |
| "Path to currently selected source. | |
| It is a cons where the cdr is a buffer-object of the current selected source | |
| The name of this file is the car of the cons: | |
| \(<filename> . <indirect-buffer-object>). | |
| This variable is only set by `ecb-path-selected-source-set' and evaluated by | |
| the function `ecb-path-selected-source'. | |
| Do not use it directly! Use always one of the mentioned functions!") | |
| (defun ecb-path-selected-source-set (filename buffer) | |
| "Set `ecb-path-selected-source' to FILENAME and BUFFER. | |
| Returns in the new value. FILENAME and BUFFER must not be nil. | |
| For a description of FILENAME and BUFFER see `ecb-source-make'." | |
| (unless (and filename buffer) | |
| (error "ECB %s: Invalid setting of `ecb-path-selected-source with file %s, buffer %s" | |
| ecb-version filename buffer)) | |
| (setq ecb-path-selected-source (ecb-source-make filename buffer))) | |
| (defun ecb-path-selected-source (&optional type) | |
| "Get the value of the internal variable `ecb-path-selected-source'. | |
| If optional arg TYPE is the symbol 'file then the filename-part | |
| is returned as string, if it is the symbol 'buffername then the | |
| stored buffername is returned if there is any and and if it is the | |
| symbol 'buffer then the buffer-object of the stored buffername is | |
| returned if there is any or nil. | |
| In all other cases of TYPE always that value is returned | |
| `ecb-path-selected-source' has been set by most recent | |
| `ecb-path-selected-source-set'." | |
| (case type | |
| (file (ecb-source-get-filename ecb-path-selected-source)) | |
| (buffername (ecb-source-get-buffername ecb-path-selected-source)) | |
| (buffer (ecb-source-get-buffer ecb-path-selected-source)) | |
| (otherwise ecb-path-selected-source))) | |
| ;; all defined tree-buffer creators | |
| (defvar ecb-tree-buffer-creators nil | |
| "The tree-buffer creators of ECB. | |
| An alist where each element is a cons where the car is a symbol which contains | |
| the name of a tree-buffer \(e.g. `ecb-sources-buffer-name') and the cdr is the | |
| associated function-symbol which creates the tree-buffer with that name.") | |
| (defun ecb-tree-buffer-creators-init () | |
| "Initialize `ecb-tree-buffer-creators'. | |
| Removes all creators and set it to nil." | |
| (setq ecb-tree-buffer-creators nil)) | |
| (defun ecb-tree-buffer-creators-register (name-symbol fn) | |
| "Register the creator-function FN for the tree-buffer NAME-SYMBOL." | |
| (add-to-list 'ecb-tree-buffer-creators (cons name-symbol fn))) | |
| (defun ecb-tree-buffer-creators-run () | |
| "Run all currently registered creator-functions." | |
| (dolist (creator-elem ecb-tree-buffer-creators) | |
| ;; create all the tree-buffers if they don't already exist | |
| (funcall (cdr creator-elem)))) | |
| (defmacro defecb-tree-buffer-creator (creator | |
| tree-buffer-name-symbol | |
| docstring &rest body) | |
| "Define a creator-function CREATOR for a tree-buffer which name is hold in | |
| the symbol TREE-BUFFER-NAME-SYMBOL. Do not quote CREATOR and | |
| TREE-BUFFER-NAME-SYMBOL. DOCSTRING is the docstring for CREATOR. BODY is all | |
| the program-code of CREATOR \(must contain a call to `tree-buffer-create'). It | |
| makes sense that BODY returns the created tree-buffer. | |
| When creating a tree-buffer with this macro then this tree-buffer will be | |
| automatically created \(i.e. its creator-function defined with this macro will | |
| be called) when activating ECB and the tree-buffer will automatically | |
| registered at ECB. This means that some features of ECB will work | |
| automatically out of the box with this tree-buffer. | |
| When creating a tree-buffer for ECB then it MUST be created with this macro | |
| and not with `tree-buffer-create'!" | |
| `(eval-and-compile | |
| (ecb-tree-buffer-creators-register (quote ,tree-buffer-name-symbol) | |
| (quote ,creator)) | |
| (defun ,creator () | |
| ,docstring | |
| (unless (ecb-ecb-buffer-registry-get-symbol ,tree-buffer-name-symbol) | |
| (ecb-ecb-buffer-registry-add ,tree-buffer-name-symbol | |
| (quote ,tree-buffer-name-symbol) | |
| t) | |
| ,@body)))) | |
| (put 'defecb-tree-buffer-creator 'lisp-indent-function 2) | |
| ;; all created ecb-buffers | |
| ;; KB: `defecb-tree-buffer-creator' calls ecb-ecb-buffer-registry-add. Besides | |
| ;; `defecb-window-dedicator-to-ecb-buffer' this is the only modifier of that | |
| ;; variable! | |
| (defvar ecb-ecb-buffer-registry nil | |
| "The ecb-buffers registry. | |
| Each special ecb-buffer must be registered at ECB in this registry. | |
| Do not change this variable! This registration is done completely | |
| by the macro `defecb-window-dedicator-to-ecb-buffer' and the | |
| results are stored in this registry. Each item is a 4-element | |
| list with the following contents: | |
| 1. elem: The buffer-name of the registered ecb-buffer | |
| 2. elem: The symbol which holds this buffer-name | |
| 3. elem: An indicator if the registered ecb-buffer is of type tree-buffer | |
| \(i.e. this tree-buffer is created with `defecb-tree-buffer-creator'). Not | |
| nil in case of a tree-buffer otherwise nil | |
| 4. elem: A function which displays that buffer in current window | |
| when called and makes this window dedicated to this buffer. This is the | |
| DEDICATOR-argument of `defecb-window-dedicator-to-ecb-buffer'. | |
| The dedicator-function must do: | |
| 1. switch to that buffer in current window | |
| 2. all things necessary for this buffer - e.g. making it read-only | |
| The setting function must ensure that the current window is still | |
| current at the end and that the related ecb-buffer is displayed | |
| in this window at the end. One examples of such a setting | |
| function is `ecb-set-history-buffer' for the buffer with name | |
| `ecb-history-buffer-name'. | |
| See `defecb-window-dedicator-to-ecb-buffer' for more details and an example.") | |
| (defun ecb-ecb-buffer-registry-init () | |
| (setq ecb-ecb-buffer-registry nil)) | |
| (defun ecb-ecb-buffer-registry-add (name name-symbol tree-buffer-p &optional set-fcn-symbol) | |
| (if (assoc name ecb-ecb-buffer-registry) | |
| (ecb-set-elt (assoc name ecb-ecb-buffer-registry) 3 set-fcn-symbol) | |
| (setq ecb-ecb-buffer-registry | |
| (cons (list name name-symbol tree-buffer-p set-fcn-symbol) | |
| ecb-ecb-buffer-registry)))) | |
| (defsubst ecb-ecb-buffer-registry-name-list (&optional only-tree-buffers) | |
| (delq nil (mapcar (function (lambda (e) | |
| (and (or (not only-tree-buffers) | |
| (nth 2 e)) | |
| (nth 0 e)))) | |
| ecb-ecb-buffer-registry))) | |
| (defsubst ecb-ecb-buffer-registry-symbol-list (&optional only-tree-buffers) | |
| (delq nil (mapcar (function (lambda (e) | |
| (and (or (not only-tree-buffers) | |
| (nth 2 e)) | |
| (nth 1 e)))) | |
| ecb-ecb-buffer-registry))) | |
| (defsubst ecb-ecb-buffer-registry-buffer-list (&optional only-tree-buffers) | |
| (delq nil (mapcar (function (lambda (e) | |
| (and (or (not only-tree-buffers) | |
| (nth 2 e)) | |
| (ecb-buffer-obj (nth 0 e))))) | |
| ecb-ecb-buffer-registry))) | |
| (defsubst ecb-ecb-buffer-registry-get-symbol (name) | |
| (nth 1 (assoc name ecb-ecb-buffer-registry))) | |
| (defsubst ecb-ecb-buffer-registry-get-set-fcn (name) | |
| (nth 3 (assoc name ecb-ecb-buffer-registry))) | |
| (defvar ecb-tree-buffer-callbacks '((expand . nil) (select . nil)) | |
| "All callback-functions for the tree-buffers of ECB. | |
| This list contains two items of the form: | |
| \(<callback-type> .\(<buffer-callback-alist>)) | |
| where <callback-type> is 'select and 'expand and | |
| <buffer-callback-alist> is an alist where each item is a cons | |
| like \(<buffer-name-symbol> . <callback-symbol>)." | |
| ) | |
| (defun ecb-tree-buffer-callbacks-add (type buffer-name-symbol callback) | |
| (unless (member type '(select expand)) | |
| (error "ECB %s tries to add tree-buffer-callback of unknown type %s" | |
| ecb-version type)) | |
| ;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: maybe we should add a check if | |
| ;; the buffer-name-symbol is already registered with defecb-tree-buffer-creator | |
| (let ((type-elem (ecb-find-assoc type ecb-tree-buffer-callbacks))) | |
| (unless (ecb-find-assoc buffer-name-symbol type-elem) | |
| (setcdr type-elem (cons (cons buffer-name-symbol callback) | |
| (cdr type-elem)))))) | |
| (defun ecb-tree-buffer-callbacks-alist-of-type (type) | |
| (unless (member type '(select expand)) | |
| (error "ECB %s tries to get tree-buffer-callback of unknown type %s" | |
| ecb-version type)) | |
| (cdr (assoc type ecb-tree-buffer-callbacks))) | |
| (defmacro defecb-tree-buffer-callback (callback | |
| tree-buffer-name-symbol | |
| callback-type | |
| optional-arg-list | |
| docstring &rest body) | |
| "Define a callback-function CALLBACK for a tree-buffer which name is hold in | |
| the symbol TREE-BUFFER-NAME-SYMBOL. Do not quote CALLBACK and | |
| TREE-BUFFER-NAME-SYMBOL and CALLBACK-TYPE. DOCSTRING is the | |
| docstring for CALLBACK. BODY is all the program-code of CALLBACK. | |
| CALLBACK-TYPE must be either 'expand or 'select, whereas the | |
| former one defines a callback for handling expanding a node and | |
| the latter one for clicking onto a node. | |
| CALLBACK is defined as a function with at least five arguments: | |
| NODE, ECB-BUTTON, EDIT-WINDOW-NR, SHIFT-MODE and META-MODE. | |
| CALLBACK must handle clicking onto NODE in the tree-buffer for | |
| which the callback is defined. ECB-BUTTON can be 1, 2 or 3 \(= | |
| mouse-buttons). If 3 then EDIT-WINDOW-NR contains the number of | |
| the edit-window the NODE should be displayed or whatever should | |
| be done with NODE. For 1 and 2 the value of EDIT-WINDOW-NR is | |
| ignored. SHIFT-MODE and META-MODE are not nil if the user has | |
| pressed the shift- rsp. the meta-button during his click. Note: | |
| using the keyboard in the tree-buffer instead the mouse is | |
| already handled by the caller of CALLBACK, so CALLBACK has no | |
| need to bother with keyboard or mouse specific stuff! | |
| If OPTIONAL-ARG-LIST is not nil then it must be a list with all | |
| optional or rest arguments. You have to include the keywords | |
| &optional or/and &rest! The first item of this list must be | |
| either the keyword &optional or &rest! The defined CALLBACK gets | |
| exactly these additional arguments after the reqired 5 arguments | |
| described above. Do not quote OPTIONAL-ARG-LIST! | |
| The defined CALLBACK automatically hides the ecb-windows after | |
| selecting a node in case META-MODE is not nil and if the CALLBACK | |
| is of type 'select; this is a must for every tree-buffer. Do not | |
| do this within BODY! But: During the evaluation of BODY the local | |
| variable no-meta-hiding is bound and set to nil per default. If | |
| BODY sets it to not nil then the hiding of the ecb-windows is | |
| prevented even if META-MODE is not nil. | |
| The value of the last expression of BODY is returned. | |
| This macro automatically adds the appropriate description of the | |
| 5 arguments of the defined CALLBACK to DOCSTRING. So just | |
| describe what the CALLBACK does! | |
| It is strongly recommended defining a callback-function for a | |
| tree-buffer of ECB with this macro and not with plain `defun', | |
| because then a lot of stuff needed to be done by every | |
| tree-buffer is automatically performed." | |
| `(eval-and-compile | |
| (ecb-tree-buffer-callbacks-add (quote ,callback-type) | |
| (quote ,tree-buffer-name-symbol) | |
| (quote ,callback)) | |
| (defun ,callback ,(append '(node ecb-button edit-window-nr shift-mode meta-mode) | |
| optional-arg-list) | |
| ,(concat (if (equal callback-type 'select) | |
| (concat | |
| "Handle clicking onto NODE in the current tree-buffer.\n" | |
| "ECB-BUTTON can be 1, 2 or 3. If 3 then EDIT-WINDOW-NR contains the number\n" | |
| "of the edit-window the NODE should be displayed or whatever should be done\n" | |
| "with NODE. For 1 and 2 the value of EDIT-WINDOW-NR is ignored.\n" | |
| "SHIFT-MODE and META-MODE are self-explanatory.") | |
| (concat | |
| "Handle expanding NODE in the current tree-buffer.\n" | |
| "ECB-BUTTON can be 1, 2 or 3. If 3 then EDIT-WINDOW-NR contains the number\n" | |
| "of the edit-window the NODE should be displayed or whatever should be done\n" | |
| "with NODE. For 1 and 2 the value of EDIT-WINDOW-NR is ignored.\n" | |
| "SHIFT-MODE and META-MODE are self-explanatory.")) | |
| "\n\n" | |
| docstring) | |
| (let ((no-meta-hiding nil)) | |
| (prog1 | |
| (progn | |
| ,@body) | |
| ,(if (equal callback-type 'select) | |
| `(when (and (not no-meta-hiding) meta-mode) | |
| (ecb-run-with-idle-timer 0.001 nil 'ecb-hide-ecb-windows)))))))) | |
| (put 'defecb-tree-buffer-callback 'lisp-indent-function 4) | |
| ;; the filename/path cache | |
| (defecb-multicache ecb-filename-cache 500 nil '(FILES-AND-SUBDIRS | |
| EMPTY-DIR-P | |
| SOURCES | |
| VC | |
| FIXED-FILENAMES | |
| REMOTE-PATH | |
| HOST-ACCESSIBLE) | |
| "Cache used for the filebrowser to cache all necessary informations | |
| associated to file- or directory-names. | |
| Currently there are the following subcaches managed within this cache: | |
| FILES-AND-SUBDIRS: | |
| Cache for every directory all subdirs and files. This is a cache with | |
| key: <directory> | |
| value: \(<file-list> . <subdirs-list>) | |
| EMPTY-DIR-P: | |
| Cache for every directory if it is empty or not. This is a cache with | |
| key: <directory> | |
| value: \(\[nil|t] . <checked-with-show-sources>) | |
| SOURCES: | |
| Cache for the contents of the buffer `ecb-sources-buffer-name'. This is a | |
| cache with | |
| key: <directory> | |
| value: \(<full-content> . <filtered-content>) | |
| whereas <full-content> is a 3-elem list \(tree-buffer-root <copy of | |
| tree-buffer-displayed-nodes> buffer-string) for a full \(i.e. all files) | |
| cache and <filtered-content> is a 4-elem list \(tree-buffer-root <copy of | |
| tree-buffer-displayed-nodes> sources-buffer-string <filter>) for a filtered | |
| cache where <filter> is a cons-cell \(<filter-regexp> . <filter-display>). | |
| VC: | |
| Cache necessary informations for the version-control-support. This is a | |
| cache for filenames and directories. In case of a file with | |
| key: <filename> of a sourcefile | |
| value: \(<state> <check-timestamp> <checked-buffers>) | |
| whereas <state> is the that VC-state the file had at time <check-timestamp>. | |
| <checked-buffers> is a list of tree-buffer-names for which <state> was | |
| checked. | |
| In case of a directory with | |
| key: <dirname> of a directory | |
| value: <vc-state-fcn> or 'NO-VC | |
| <vc-state-fcn> is the function used to get the VC-state if <check-timestamp> | |
| is older than the most recent modification-timestamp of <filename>. | |
| FIXED-FILENAMES: | |
| Cache for fixed filenames which can speedup handling-remote-paths \(like | |
| tramp-paths) | |
| key: The concatenation of the args PATH and FILENAME of `ecb-fix-filename'. | |
| value: The result of `ecb-fix-filename' for these args. | |
| REMOTE-PATH: | |
| Cache if a path is a remote path and store its components if yes. | |
| key: a path | |
| value: 'NOT-REMOTE if not a remote path otherwise the result of | |
| `ecb-remote-path'. | |
| HOST-ACCESSIBLE: | |
| Cache if a host is accessible or not. | |
| key: a host \(e.g. ecb.sourceforge.net) | |
| value: \(<timestamp> . <value>) whereas <timestamp> is the cache time of | |
| <value> and <value> is either 'NOT-ACCESSIBLE if host is not accessible | |
| or t if accessible. | |
| ") | |
| (defun ecb-filename-cache-init () | |
| "Initialize the whole cache for file- and directory-names" | |
| (if (ecb-multicache-p 'ecb-filename-cache) | |
| (ecb-multicache-clear 'ecb-filename-cache))) | |
| ;; directory separator | |
| (defconst ecb-directory-sep-char | |
| (if ecb-running-xemacs | |
| ;; to avoid compiler complainings | |
| (symbol-value 'directory-sep-char) | |
| ?/)) | |
| (defsubst ecb-directory-sep-char (&optional refdir) | |
| (if (or (null refdir) | |
| (not (ecb-remote-path refdir))) | |
| ecb-directory-sep-char | |
| ?/)) | |
| (defsubst ecb-directory-sep-string (&optional refdir) | |
| (char-to-string (ecb-directory-sep-char refdir))) | |
| ;; autocontrol/sync-stuff -------------------------------------------- | |
| (defvar ecb-autotrace-autocontrol/sync-functions nil | |
| "Allow autotracing the internal autocontrol/synchronisations of ECB. | |
| All functions defined with `defecb-autocontrol/sync-function' can | |
| be autotraced. The value of this variable is either nil \(then no | |
| autotracing will be performed) or t \(then all functions defined with | |
| `defecb-autocontrol/sync-function' will be traced) or a list of | |
| function-symbols \(then exactly these functions will be traced). | |
| Auto-tracing means that each\(!) time the function runs \(either | |
| by idle-timer or within pre- or post-command-hook) a trace-message on | |
| function-entry and a trace-message on function exit is writen.") | |
| (defvar ecb-bodytrace-autocontrol/sync-functions nil | |
| "Allows a body tracing of the internal autocontrol/synchronisations of ECB. | |
| For allowed values for this variable see `ecb-autotrace-autocontrol/sync-functions' | |
| Body-tracing means calls to `ecb-bodytrace-autocontrol/sync-fcn-error' | |
| somewhere within the BODY of functions defined by | |
| `defecb-autocontrol/sync-function'. So by setting this variable tracing of the | |
| BODY of autocontrol/sync-function can be switched on or off.") | |
| (defun ecb-autotrace-autocontrol/sync-fcn-error (autocontrol-fcn &rest args) | |
| "Run ARGS through `format' and write it to the *Messages*-buffer. | |
| Do not use this function for own traces, but use the function | |
| `ecb-bodytrace-autocontrol/sync-fcn-error'! | |
| Entry and exit-traces are already buildin in functions defined with | |
| `defecb-autocontrol/sync-function' and can be switched on/off by | |
| `ecb-autotrace-autocontrol/sync-functions'." | |
| (when (or (eq ecb-autotrace-autocontrol/sync-functions t) | |
| (member autocontrol-fcn ecb-autotrace-autocontrol/sync-functions)) | |
| (message (concat (format "ECB %s autocontrol-fcn %s autotrace [%s] " | |
| ecb-version autocontrol-fcn | |
| (format-time-string "%H:%M:%S")) | |
| (apply 'format args))))) | |
| (defun ecb-bodytrace-autocontrol/sync-fcn-error (autocontrol-fcn &rest args) | |
| "Run ARGS through `format' and write it to the *Messages*-buffer. | |
| Use this function only for traces used within the BODY of a function defined | |
| with `defecb-autocontrol/sync-function' and not for entry and exit-traces | |
| because these ones are already buildin in functions defined with | |
| `defecb-autocontrol/sync-function' and can be switched on/off by | |
| `ecb-autotrace-autocontrol/sync-functions'. | |
| Body-tracing can be switched on/off with | |
| `ecb-bodytrace-autocontrol/sync-function'." | |
| (when (or (eq ecb-bodytrace-autocontrol/sync-functions t) | |
| (member autocontrol-fcn ecb-bodytrace-autocontrol/sync-functions)) | |
| (message (concat (format "ECB %s autocontrol-fcn %s bodytrace [%s] " | |
| ecb-version autocontrol-fcn | |
| (format-time-string "%H:%M:%S")) | |
| (apply 'format args))))) | |
| (defvar ecb-autocontrol/sync-fcn-register nil | |
| "List of autocontrol/sync-functions defined by | |
| `defecb-autocontrol/sync-function'. Each element is a cons where car is the | |
| function symbol and cdr is either the symbol of a variable containing a | |
| buffer-name of a special ECB-buffer or nil.") | |
| (defvar ecb-idle-timer-alist nil | |
| "List of active idle-timers of ECB") | |
| (defvar ecb-post-command-hooks nil | |
| "List of functions which have to be part of the value of `post-command-hook'") | |
| (defvar ecb-pre-command-hooks nil | |
| "List of functions which have to be part of the value of `pre-command-hook'") | |
| (defun ecb-register-autocontrol/sync-function (fcn-symbol buffer-name-symbol) | |
| (add-to-list 'ecb-autocontrol/sync-fcn-register (cons fcn-symbol buffer-name-symbol))) | |
| (defun ecb-stop-all-autocontrol/sync-functions () | |
| (dolist (fcn-elem ecb-autocontrol/sync-fcn-register) | |
| (ecb-stop-autocontrol/sync-function (car fcn-elem)))) | |
| (defun ecb-stop-autocontrol/sync-function (fcn-symbol) | |
| (let* ((timer-elem (assoc fcn-symbol ecb-idle-timer-alist)) | |
| (timer (cdr timer-elem))) | |
| (when timer-elem | |
| (ecb-cancel-timer timer) | |
| (setq ecb-idle-timer-alist (delq timer-elem ecb-idle-timer-alist))) | |
| (remove-hook 'post-command-hook fcn-symbol) | |
| (remove-hook 'pre-command-hook fcn-symbol) | |
| (setq ecb-post-command-hooks (delq fcn-symbol ecb-post-command-hooks)) | |
| (setq ecb-pre-command-hooks (delq fcn-symbol ecb-pre-command-hooks)))) | |
| (defun ecb-activate-ecb-autocontrol-function (value func) | |
| "Adds function FUNC to `ecb-idle-timer-alist' and activates an idle-timer | |
| with idle-time VALUE if VALUE is a number. If nil or 'post the | |
| FUNC is added to `post-command-hook' and `ecb-post-command-hooks' | |
| and removed from the idle-list \(if it has been contained). If | |
| 'pre the FUNC is added to `pre-command-hook' and | |
| `ecb-pre-command-hooks' and removed from the idle-list \(if it | |
| has been contained)." | |
| (unless (assoc func ecb-autocontrol/sync-fcn-register) | |
| (error "ECB %s: Try to activate unregistered %s as autocontrol/sync-function" | |
| ecb-version func)) | |
| ;; `ecb-basic-buffer-sync-delay' can never have the value 'basic so this | |
| ;; recursion is save | |
| (if (equal 'basic value) | |
| (ecb-activate-ecb-autocontrol-function ecb-basic-buffer-sync-delay func) | |
| (ecb-stop-autocontrol/sync-function func) | |
| (case value | |
| ((nil post) | |
| (add-hook 'post-command-hook func) | |
| (add-to-list 'ecb-post-command-hooks func)) | |
| (pre | |
| (add-hook 'pre-command-hook func) | |
| (add-to-list 'ecb-pre-command-hooks func)) | |
| (otherwise | |
| (add-to-list 'ecb-idle-timer-alist | |
| (cons func | |
| (ecb-run-with-idle-timer value t func))))))) | |
| (defmacro defecb-autocontrol/sync-function (fcn buffer-name-symbol | |
| buffer-sync-option-symbol | |
| ecb-interactive-p docstring | |
| &rest body) | |
| "Define a function run either by idle-timer or before or after each command. | |
| Such a function is used either for automatic self-controlling certain aspects | |
| of ECB or for synchronizing a special window/buffer of ECB with contents of | |
| the active buffer in the edit-area. | |
| FCN is the name of the defined function and BUFFER-NAME-SYMBOL is | |
| either nil or a variable-symbol containing the buffer-name | |
| of a special ECB-window/buffer for which the defined function is | |
| used for synchronizing it with the edit-area. In the latter case | |
| BODY is encapsulated with the macros a) | |
| `ecb-when-point-in-edit-window-ecb-windows-visible' and b) | |
| `ecb-do-if-buffer-visible-in-ecb-frame' so BODY runs only if a) | |
| point stays in an edit-window \(ie. the currently selected window | |
| is an edit-window) and the ecb-windows of current layout are | |
| not hidden and b) the buffer of BUFFER-NAME-SYMBOL is displayed in a | |
| window of the ECB-frame \(for details see the documentation of | |
| this macro). | |
| Please note: If BUFFER-NAME-SYMBOL is nil then BODY is not | |
| encapsulated with these two macros mentioned above! | |
| The defined function has an optional argument FORCE which can be used within | |
| BODY. | |
| BUFFER-SYNC-OPTION-SYMBOL is either nil or the name of an option | |
| which defines if and under which circumstances the | |
| synchronization should take place. Such an option must be of the | |
| same type and must offer exactly the same values as `ecb-analyse-buffer-sync'. | |
| If BUFFER-SYNC-OPTION-SYMBOL is not nil and a valid symbol then the generated | |
| function encapsulates BODY in a when-statement whereas the condition-clause is | |
| exactly true when either: | |
| - force is not nil or | |
| - The value of BUFFER-SYNC-OPTION-SYMBOL is t or 'always or | |
| - The major-mode of current buffer is not contained in the list-value of | |
| BUFFER-SYNC-OPTION-SYMBOL or | |
| - The value of BUFFER-SYNC-OPTION-SYMBOL is 'basic and the conditions above | |
| are true for `ecb-basic-buffer-sync'. | |
| If ECB-INTERACTIVE-P is not nil then FCN will be defined as an interactice | |
| command, i.e. it will containe the clause \(interactive \"P\"). | |
| The defined function is automatically prepared for tracing its calls when | |
| `ecb-autotrace-autocontrol/sync-functions' is either set to t or the symbol of FCN | |
| is contained in the list of this variable. In this case a trace-statemant with | |
| time-details is reported to the message-buffer directly before and after BODY. | |
| Do not quote FCN, BUFFER-NAME-SYMBOL and BUFFER-SYNC-OPTION-SYMBOL! | |
| Example: | |
| \(defecb-autocontrol/sync-function ecb-sync-a-buffer ecb-a-special-buffer-name | |
| ecb-a-special-buffer-sync nil | |
| \"Synchronize the buffer of ECB-A-SPECIAL-BUFFER-NAME with...\" | |
| \(let \(\(x nil)) | |
| \(if force | |
| ;; do something | |
| ;; do something else | |
| ) | |
| )) | |
| This defines a non interactive function `ecb-sync-a-buffer' which | |
| should be used for synchronizing the special buffer the name is | |
| hold in the variable `ecb-a-special-buffer-name'. | |
| " | |
| `(eval-and-compile | |
| (ecb-register-autocontrol/sync-function (quote ,fcn) (quote ,buffer-name-symbol)) | |
| (defun ,fcn (&optional force) | |
| ,docstring | |
| ,(if ecb-interactive-p | |
| '(interactive "P")) | |
| (ecb-autotrace-autocontrol/sync-fcn-error (quote ,fcn) | |
| "Begin: Cur-buf: %s" (current-buffer)) | |
| (let (,(if (and buffer-sync-option-symbol (symbolp buffer-sync-option-symbol)) | |
| `(,buffer-sync-option-symbol (if (equal | |
| ,buffer-sync-option-symbol | |
| 'basic) | |
| ecb-basic-buffer-sync | |
| ,buffer-sync-option-symbol)) | |
| ;; we need anything to bound in the else-fork, so we just bind | |
| ;; major-mode to major-mode - we could use any variable, takes | |
| ;; no effect | |
| `(major-mode major-mode)) | |
| ;; `(,(make-symbol "abc123xyz456efg789") nil)) | |
| ) | |
| (when ,(if (and buffer-sync-option-symbol (symbolp buffer-sync-option-symbol)) | |
| `(or force | |
| (equal 'always ,buffer-sync-option-symbol) | |
| (equal t ,buffer-sync-option-symbol) | |
| (and ,buffer-sync-option-symbol | |
| (listp ,buffer-sync-option-symbol) | |
| (not (member major-mode ,buffer-sync-option-symbol)))) | |
| t) | |
| ,(if (and buffer-name-symbol (symbolp buffer-name-symbol)) | |
| `(ecb-when-point-in-edit-window-ecb-windows-visible | |
| (ecb-do-if-buffer-visible-in-ecb-frame (quote ,buffer-name-symbol) | |
| (ecb-bodytrace-autocontrol/sync-fcn-error (quote ,fcn) | |
| "After conditions: Cur-buf: %s" (current-buffer)) | |
| ,@body | |
| (ecb-autotrace-autocontrol/sync-fcn-error (quote ,fcn) | |
| "End: Cur-buf: %s" (current-buffer)) | |
| nil ;; we always return nil | |
| )) | |
| `(progn | |
| (ecb-bodytrace-autocontrol/sync-fcn-error (quote ,fcn) | |
| "After conditions: Cur-buf: %s" (current-buffer)) | |
| ,@body | |
| (ecb-autotrace-autocontrol/sync-fcn-error (quote ,fcn) | |
| "End: Cur-buf: %s" | |
| (current-buffer)) | |
| nil ;; we always return nil | |
| ) | |
| )))))) | |
| (put 'defecb-autocontrol/sync-function 'lisp-indent-function 4) | |
| (defecb-autocontrol/sync-function ecb-monitor-autocontrol-functions nil nil nil | |
| "Checks if all necessary ECB-hooks are contained in `post-command-hook' rsp. | |
| `pre-command-hook'. If one of them has been removed by Emacs \(Emacs resets | |
| these hooks to nil if any of the contained functions fails!) then this | |
| function reads them to these hooks." | |
| ;; post-command-hook | |
| (dolist (hook ecb-post-command-hooks) | |
| (when (not (member hook post-command-hook)) | |
| (add-hook 'post-command-hook hook))) | |
| ;; pre-command-hook | |
| (dolist (hook ecb-pre-command-hooks) | |
| (when (not (member hook pre-command-hook)) | |
| (add-hook 'pre-command-hook hook)))) | |
| ;; -- end of sync stuff | |
| ;;; ----- Wrappers for file- and directory-operations ------ | |
| (dolist (f '(file-name-nondirectory | |
| file-exists-p | |
| file-name-directory | |
| file-readable-p | |
| file-attributes | |
| file-name-extension | |
| file-directory-p | |
| file-accessible-directory-p | |
| file-name-sans-extension | |
| file-writable-p | |
| file-name-as-directory)) | |
| (fset (intern (format "ecb-%s" f)) | |
| `(lambda (file-or-dir-name &rest args) | |
| ,(format "Delegate all args to `%s' but call first `ecb-fix-path' for FILE-OR-DIR-NAME." f) | |
| (apply (quote ,f) (ecb-fix-path file-or-dir-name) args)))) | |
| (defun ecb-directory-files (dir &rest args) | |
| "Wrapper for directory-files that fixes the file name & catch file errors" | |
| (condition-case nil | |
| (apply 'directory-files (ecb-fix-path dir) args) | |
| (error nil))) | |
| (defun ecb-expand-file-name (name &optional default-dir) | |
| "Delegate all args to `expand-file-name' but call first `ecb-fix-path' | |
| for both args." | |
| (expand-file-name (ecb-fix-path name) (ecb-fix-path default-dir))) | |
| ;;; ----- Canonical filenames ------------------------------ | |
| (defun ecb-fix-path (path) | |
| "Fixes an annoying behavior of the native windows-version of XEmacs: | |
| When PATH contains only a drive-letter and a : then `expand-file-name' does | |
| not interpret this PATH as root of that drive. So we add a trailing | |
| `directory-sep-char' and return this new path because then `expand-file-name' | |
| treats this as root-dir of that drive. For all \(X)Emacs-version besides the | |
| native-windows-XEmacs PATH is returned." | |
| (if (and ecb-running-xemacs | |
| (equal system-type 'windows-nt)) | |
| (if (and (= (length path) 2) | |
| (equal (aref path 1) ?:)) | |
| (concat path (ecb-directory-sep-string)) | |
| path) | |
| path)) | |
| ;; accessors for the FIXED-FILENAMES-cache | |
| (defsubst ecb-fixed-filename-cache-put (path filename fixed-filename) | |
| "Add FIXED-FILENAME for PATH and FILENAME to the FIXED-FILENAMES-cache | |
| of `ecb-filename-cache'." | |
| (ecb-multicache-put-value 'ecb-filename-cache | |
| (concat path filename) | |
| 'FIXED-FILENAMES | |
| fixed-filename)) | |
| (defsubst ecb-fixed-filename-cache-get (path filename) | |
| "Get the cached value for PATH and FILENAME from the FIXED-FILENAMES-cache | |
| in `ecb-filename-cache'. If no vaue is cached for PATH and FILENAME then nil | |
| is returned." | |
| (ecb-multicache-get-value 'ecb-filename-cache | |
| (concat path filename) | |
| 'FIXED-FILENAMES)) | |
| (defun ecb-fixed-filename-cache-dump (&optional no-nil-value) | |
| "Dump the whole FIXED-FILENAMES-cache. If NO-NIL-VALUE is not nil then these | |
| cache-entries are not dumped. This command is not intended for end-users of ECB." | |
| (interactive "P") | |
| (ecb-multicache-print-subcache 'ecb-filename-cache | |
| 'FIXED-FILENAMES | |
| no-nil-value)) | |
| ;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: What about the new cygwin-version | |
| ;; of GNU Emacs 21? We have to test if this function and all locations where | |
| ;; `ecb-fix-path' is used work correctly with the cygwin-port of GNU Emacs. | |
| (silentcomp-defun mswindows-cygwin-to-win32-path) | |
| (defun ecb-fix-filename (path &optional filename substitute-env-vars) | |
| "Normalizes path- and filenames for ECB. If FILENAME is not nil its pure | |
| filename \(i.e. without directory part) will be concatenated to PATH. The | |
| result will never end with the directory-separator! If SUBSTITUTE-ENV-VARS is | |
| not nil then in both PATH and FILENAME env-var substitution is done. If the | |
| `system-type' is 'cygwin32 then the path is converted to win32-path-style!" | |
| (when (stringp path) | |
| (or (ecb-fixed-filename-cache-get path filename) | |
| (let ((remote-path (ecb-remote-path path)) | |
| (norm-path nil) | |
| (result nil)) | |
| (if (or (not remote-path) | |
| (ecb-host-accessible-p (nth 1 remote-path))) | |
| (progn | |
| (setq norm-path (if ecb-running-xemacs | |
| (case system-type | |
| (cygwin32 | |
| (mswindows-cygwin-to-win32-path | |
| (expand-file-name path))) | |
| (windows-nt | |
| (expand-file-name (ecb-fix-path path))) | |
| (otherwise | |
| (expand-file-name path))) | |
| (expand-file-name path))) | |
| ;; substitute environment-variables | |
| (setq norm-path (expand-file-name (if substitute-env-vars | |
| (substitute-in-file-name norm-path) | |
| norm-path)))) | |
| (setq norm-path path)) | |
| ;; For windows systems we normalize drive-letters to downcase | |
| (setq norm-path (if (and (member system-type '(windows-nt cygwin32)) | |
| (> (length norm-path) 1) | |
| (equal (aref norm-path 1) ?:)) | |
| (concat (downcase (substring norm-path 0 2)) | |
| (substring norm-path 2)) | |
| norm-path)) | |
| ;; delete a trailing directory-separator if there is any | |
| (setq norm-path (if (and (> (length norm-path) 1) | |
| (= (aref norm-path (1- (length norm-path))) | |
| (ecb-directory-sep-char path))) | |
| (substring norm-path 0 (1- (length norm-path))) | |
| norm-path)) | |
| (setq result | |
| (concat norm-path | |
| (if (stringp filename) | |
| (concat (when (> (length norm-path) 1) | |
| ;; currently all protocols like tramp, | |
| ;; ange-ftp or efs support only not | |
| ;; windows-remote-hosts ==> we must not | |
| ;; add a backslash here (would be done | |
| ;; in case of a native Windows-XEmacs) | |
| (ecb-directory-sep-string path)) | |
| (file-name-nondirectory (if substitute-env-vars | |
| (substitute-in-file-name filename) | |
| filename)))))) | |
| (ecb-fixed-filename-cache-put path filename result) | |
| result)))) | |
| ;; -- end of canonical filenames | |
| (defun ecb-format-bucket-name (name &optional ignore-prefix-suffix ignore-bucket-face) | |
| "Format NAME as a bucket-name according to `ecb-bucket-node-display'. | |
| If optional arg IGNORE-PREFIX-SUFFIX is not nil then | |
| these settings of `ecb-bucket-node-display' are ignored. If IGNORE-BUCKET-FACE | |
| it t then the face of `ecb-bucket-node-display' is completely ignored, if it | |
| is 'only-name then the face of `ecb-bucket-node-display' is only ignored for | |
| NAME but not for a prefix or suffix of `ecb-bucket-node-display' \(if any)." | |
| (let ((formated-prefix (unless ignore-prefix-suffix | |
| (if (eq ignore-bucket-face t) | |
| (nth 0 ecb-bucket-node-display) | |
| (ecb-merge-face-into-text | |
| (nth 0 ecb-bucket-node-display) | |
| (nth 2 ecb-bucket-node-display))))) | |
| (formated-suffix (unless ignore-prefix-suffix | |
| (if (eq ignore-bucket-face t) | |
| (nth 1 ecb-bucket-node-display) | |
| (ecb-merge-face-into-text | |
| (nth 1 ecb-bucket-node-display) | |
| (nth 2 ecb-bucket-node-display))))) | |
| (formatted-name (if ignore-bucket-face | |
| name | |
| (ecb-merge-face-into-text name (nth 2 ecb-bucket-node-display))))) | |
| (concat formated-prefix formatted-name formated-suffix))) | |
| (defun ecb-toggle-do-not-leave-window-after-select () | |
| "Toggles if a node-selection in a tree-buffer leaves the tree-window. | |
| See also the option `ecb-tree-do-not-leave-window-after-select'." | |
| (interactive) | |
| (let ((tree-buffer (ecb-point-in-ecb-tree-buffer))) | |
| (if tree-buffer | |
| (let ((tree-buf-name (buffer-name tree-buffer))) | |
| (if (ecb-member-of-symbol/value-list | |
| tree-buf-name | |
| ecb-tree-do-not-leave-window-after-select--internal) | |
| (progn | |
| (setq ecb-tree-do-not-leave-window-after-select--internal | |
| ;; we must try both - the symbol of the tree-buffer-name | |
| ;; and the tree-buffer-name because we do not know what | |
| ;; the user has specified in | |
| ;; `ecb-tree-do-not-leave-window-after-select'! | |
| (delete (ecb-ecb-buffer-registry-get-symbol tree-buf-name) | |
| (delete tree-buf-name | |
| ecb-tree-do-not-leave-window-after-select--internal))) | |
| (message "Selections leave the tree-window of %s" tree-buf-name)) | |
| (setq ecb-tree-do-not-leave-window-after-select--internal | |
| (append ecb-tree-do-not-leave-window-after-select--internal | |
| (list (ecb-ecb-buffer-registry-get-symbol tree-buf-name)))) | |
| (message "Selections don't leave the tree-window of %s." tree-buf-name))) | |
| (message "Point must stay in an ECB tree-buffer!")))) | |
| (defun ecb-common-tree-buffer-modeline-menu-creator (buf-name) | |
| "Return a menu for the modeline of all ECB-tree-buffers." | |
| '((delete-other-windows "Maximize Window Above") | |
| (ecb-redraw-layout-preserving-compwin-state "Display All ECB-windows"))) | |
| (defun ecb-common-after-tree-buffer-create-actions () | |
| "Things which should be performed after creating a tree-buffer. | |
| The tree-buffer is the current buffer." | |
| (local-set-key (kbd "C-t") 'ecb-toggle-do-not-leave-window-after-select) | |
| (if ecb-running-xemacs | |
| ;; TODO: Klaus Berndl <klaus.berndl@sdm.de>: Is it necessary to make | |
| ;; modeline-map buffer-local for current buffer first?! | |
| (define-key modeline-map | |
| '(button2up) | |
| 'ecb-toggle-maximize-ecb-window-with-mouse) | |
| (local-set-key [mode-line mouse-2] | |
| 'ecb-toggle-maximize-ecb-window-with-mouse))) | |
| ;;==================================================== | |
| ;; Mouse callbacks | |
| ;;==================================================== | |
| (defun ecb-tree-buffer-node-select-callback (node | |
| mouse-button | |
| shift-pressed | |
| control-pressed | |
| meta-pressed | |
| tree-buffer-name) | |
| "This is the callback-function ecb.el gives to every tree-buffer to call | |
| when a node has been selected. This function does nothing if the click | |
| combination is invalid \(see `ecb-interpret-mouse-click'." | |
| (let* ((ecb-button-list (ecb-interpret-mouse-click mouse-button | |
| shift-pressed | |
| control-pressed | |
| meta-pressed | |
| tree-buffer-name)) | |
| (ecb-button (nth 0 ecb-button-list)) | |
| (shift-mode (nth 1 ecb-button-list)) | |
| (meta-mode (nth 2 ecb-button-list)) | |
| (keyboard-p (equal (nth 3 ecb-button-list) 'keyboard)) | |
| (maximized-p (ecb-buffer-is-maximized-p tree-buffer-name)) | |
| (select-callbacks (ecb-tree-buffer-callbacks-alist-of-type 'select)) | |
| (callback-fcn nil)) | |
| ;; we need maybe later that something has clicked in a tree-buffer, e.g. | |
| ;; in `ecb-handle-major-mode-visibilty'. | |
| (setq ecb-item-in-tree-buffer-selected t) | |
| (if (not keyboard-p) | |
| (setq ecb-layout-prevent-handle-ecb-window-selection t)) | |
| ;; first we dispatch to the right action | |
| (when ecb-button-list | |
| (setq callback-fcn | |
| (ecb-member-of-symbol/value-list tree-buffer-name | |
| select-callbacks | |
| 'car | |
| 'cdr)) | |
| (when (functionp callback-fcn) | |
| (funcall callback-fcn node ecb-button nil shift-mode meta-mode))) | |
| ;; now we go back to the tree-buffer but only if all of the following | |
| ;; conditions are true: | |
| ;; 1. The ecb-windows are now not hidden | |
| ;; 2. The tree-buffer-name is contained in | |
| ;; ecb-tree-do-not-leave-window-after-select--internal | |
| ;; 3. Either it is not the ecb-directories-buffer-name or | |
| ;; at least `ecb-show-sources-in-directories-buffer-p' is true and the | |
| ;; hitted node is a sourcefile | |
| (when (and (not (ecb-windows-all-hidden)) | |
| (ecb-member-of-symbol/value-list | |
| tree-buffer-name | |
| ecb-tree-do-not-leave-window-after-select--internal) | |
| (or (not (ecb-string= tree-buffer-name ecb-directories-buffer-name)) | |
| (and (ecb-show-sources-in-directories-buffer-p) | |
| (= ecb-directories-nodetype-sourcefile | |
| (tree-node->type node))))) | |
| ;; If there is currently no maximized window then we can savely call | |
| ;; `ecb-goto-ecb-window'. If we have now a maximized window then there | |
| ;; are two possibilities: | |
| ;; - if it is not equal to the maximzed tree-buffer before the selection | |
| ;; then we must maximi | |
| (if (and maximized-p | |
| (not (ecb-buffer-is-maximized-p tree-buffer-name))) | |
| (ecb-maximize-ecb-buffer tree-buffer-name t) | |
| (ecb-goto-ecb-window tree-buffer-name)) | |
| (tree-buffer-remove-highlight)))) | |
| (defun ecb-tree-buffer-node-collapsed-callback (node | |
| mouse-button | |
| shift-pressed | |
| control-pressed | |
| meta-pressed | |
| tree-buffer-name) | |
| "This is the callback-function ecb.el gives to every tree-buffer to call | |
| when a node has been collapsed." | |
| (let* ((ecb-button-list (ecb-interpret-mouse-click mouse-button | |
| shift-pressed | |
| control-pressed | |
| meta-pressed | |
| tree-buffer-name)) | |
| (keyboard-p (equal (nth 3 ecb-button-list) 'keyboard))) | |
| (if (not keyboard-p) | |
| (setq ecb-layout-prevent-handle-ecb-window-selection t)))) | |
| (defun ecb-tree-buffer-node-expand-callback (node | |
| mouse-button | |
| shift-pressed | |
| control-pressed | |
| meta-pressed | |
| tree-buffer-name) | |
| "This is the callback-function ecb.el gives to every tree-buffer to call | |
| when a node should be expanded. This function does nothing if the click | |
| combination is invalid \(see `ecb-interpret-mouse-click')." | |
| (let* ((ecb-button-list (ecb-interpret-mouse-click mouse-button | |
| shift-pressed | |
| control-pressed | |
| meta-pressed | |
| tree-buffer-name)) | |
| (ecb-button (nth 0 ecb-button-list)) | |
| (shift-mode (nth 1 ecb-button-list)) | |
| (meta-mode (nth 2 ecb-button-list)) | |
| (keyboard-p (equal (nth 3 ecb-button-list) 'keyboard)) | |
| (expand-callbacks (ecb-tree-buffer-callbacks-alist-of-type 'expand)) | |
| (callback-fcn nil)) | |
| (if (not keyboard-p) | |
| (setq ecb-layout-prevent-handle-ecb-window-selection t)) | |
| ;; we just dispatch to the right action | |
| (when ecb-button-list | |
| (setq callback-fcn | |
| (ecb-member-of-symbol/value-list tree-buffer-name | |
| expand-callbacks | |
| 'car | |
| 'cdr)) | |
| (when (functionp callback-fcn) | |
| (funcall callback-fcn node ecb-button nil shift-mode meta-mode))))) | |
| (defun ecb-interpret-mouse-click (mouse-button | |
| shift-pressed | |
| control-pressed | |
| meta-pressed | |
| tree-buffer-name) | |
| "Converts the physically pressed MOUSE-BUTTON \(1 = mouse-1, 2 = mouse-2, 0 = | |
| no mouse-button but the keys RET or TAB) to ECB-mouse-buttons: either primary | |
| or secondary mouse-button depending on the value of CONTROL-PRESSED and the | |
| setting in `ecb-primary-secondary-mouse-buttons'. Returns a list | |
| '\(<ECB-button> <shift-mode> <meta-mode> <device>) where <ECB-button> is | |
| either 1 \(= primary) or 2 \(= secondary) and <shift-mode> and <meta-mode> are | |
| non nil if SHIFT-PRESSED rsp. META-PRESSED is non nil. <device> is either | |
| 'mouse or 'keyboard dependent if the uses has used the mouse rsp. the keyboard | |
| in the tree-buffer. For an invalid and not accepted click combination nil is | |
| returned. | |
| Note: If MOUSE-BUTTON is 0 \(means no mouse-button but a key like RET or TAB | |
| was hitted) then CONTROL-PRESSED is interpreted as ECB-button 2. | |
| Currently the fourth argument TREE-BUFFER-NAME is not used here." | |
| (if (eq mouse-button 0) | |
| (list (if control-pressed 2 1) shift-pressed meta-pressed 'keyboard) | |
| (if (and (not (eq mouse-button 1)) (not (eq mouse-button 2))) | |
| nil | |
| (case ecb-primary-secondary-mouse-buttons | |
| (mouse-1--mouse-2 | |
| (if control-pressed | |
| nil | |
| (list mouse-button shift-pressed meta-pressed 'mouse))) | |
| (mouse-1--C-mouse-1 | |
| (if (not (eq mouse-button 1)) | |
| nil | |
| (list (if control-pressed 2 1) shift-pressed meta-pressed 'mouse))) | |
| (mouse-2--C-mouse-2 | |
| (if (not (eq mouse-button 2)) | |
| nil | |
| (list (if control-pressed 2 1) shift-pressed meta-pressed 'mouse))) | |
| (otherwise nil))))) | |
| (defun ecb-show-minibuffer-info (node window when-spec) | |
| "Checks if any info about the current node in the ECB-window WINDOW should | |
| be displayed. WHEN-SPEC must have the same format as the car of | |
| `ecb-directories-show-node-info'." | |
| (or (eq when-spec 'always) | |
| (and (eq when-spec 'if-too-long) | |
| window | |
| (>= (tree-node-linelength node) | |
| (window-width window))))) | |
| (tree-buffer-defpopup-command ecb-maximize-ecb-window-menu-wrapper | |
| "Expand the current ECB-window from popup-menu." | |
| (ecb-maximize-ecb-buffer (buffer-name (current-buffer)) t)) | |
| ;; stealthy mechanism | |
| (defvar ecb-stealthy-function-list nil | |
| "List of functions which ECB runs stealthy. Do not modify this variable! | |
| This variable is autom. set by the macro `defecb-stealthy'!") | |
| (defvar ecb-stealthy-function-state-alist nil | |
| "Alist which stores the state of each function of | |
| `ecb-stealthy-function-list'. Do not add new items to this variable because | |
| this is autom. done by the macro `defecb-stealthy'!") | |
| (defun ecb-stealthy-function-list-add (fcn) | |
| (add-to-list 'ecb-stealthy-function-list fcn)) | |
| (defun ecb-stealthy-function-state-alist-add (fcn) | |
| (add-to-list 'ecb-stealthy-function-state-alist | |
| (cons fcn 'done))) | |
| (defun ecb-stealthy-function-state-get (fcn) | |
| "Getter for `ecb-stealthy-function-state-alist'. Return state for the | |
| stealthy function FCN." | |
| (cdr (assoc fcn ecb-stealthy-function-state-alist))) | |
| (defun ecb-stealthy-function-state-set (fcn state) | |
| "Setter for `ecb-stealthy-function-state-alist'. Set STATE for the | |
| stealthy function FCN. Return STATE." | |
| (setcdr (assoc fcn ecb-stealthy-function-state-alist) state)) | |
| (defun ecb-stealthy-function-p (fcn) | |
| "Return not nil if FCN is a stealthy function defined with | |
| `defecb-stealthy'." | |
| (member fcn ecb-stealthy-function-list)) | |
| (defun ecb-stealthy-function-state-init (&optional fcn state) | |
| "Reset the state of stealthy functions. If first optional arg FCN is a | |
| stealthy function then only the state of this function is reset - otherwise | |
| all stealthy functions of `ecb-stealthy-function-list' are reset. If second | |
| optional arg STATE is nil then the state will be reset to the special state | |
| 'restart - otherwise to the value STATE." | |
| (if (and fcn (ecb-stealthy-function-p fcn)) | |
| (ecb-stealthy-function-state-set fcn (or state 'restart)) | |
| (dolist (f ecb-stealthy-function-list) | |
| (ecb-stealthy-function-state-set f (or state 'restart))))) | |
| (defmacro defecb-stealthy (name docstring &rest body) | |
| "Define a so called stealthy function with NAME. This function will be | |
| registered by this macro in `ecb-stealthy-function-list' and | |
| `ecb-stealthy-function-state-alist'. During the evaluation of BODY the | |
| variable `state' will be bound and initialized with the stealthy state. BODY | |
| can use and modify `state'. After evaluating BODY `state' will be | |
| automatically saved so its available at the runtime of this stealthy function. | |
| BODY will only be evaluated if `state' is not 'done. BODY should be designed | |
| to be interruptable by the user, so its recommended to use for this | |
| `ecb-exit-on-input' ans `ecb-throw-on-input' \(see example in | |
| `ecb-test-throw-on-input'). If BODY completes then BODY has to set `state' to | |
| the special value 'done! If BODY has been interrupted then `state' can have an | |
| arbitrary value which will be autom. stored and at next runtime of the | |
| stealthy function NAME `state' will be initialized with this stored value. If | |
| `state' is initialized with the special value 'restart then this means the | |
| stealthy function should start from scratch because an eventually stored state | |
| is not longer valid. If the stealthy function sets `state' to 'done then this | |
| function will first being called after the state for this function has been | |
| reset to something else than 'done \(mostly to 'restart)\; such a reset of the | |
| state for a stealthy function can be done by any code and must be done via | |
| `ecb-stealthy-function-state-init'!" | |
| `(progn | |
| (unless (fboundp (quote ,name)) | |
| (ecb-stealthy-function-list-add (quote ,name)) | |
| (ecb-stealthy-function-state-alist-add (quote ,name))) | |
| (eval-and-compile | |
| (unless (fboundp (quote ,name)) | |
| (defun ,name nil | |
| ,docstring | |
| (let ((state (ecb-stealthy-function-state-get (quote ,name)))) | |
| (unless (equal state 'done) | |
| ,@body) | |
| (ecb-stealthy-function-state-set (quote ,name) state))))))) | |
| (put 'defecb-stealthy 'lisp-indent-function 1) | |
| (defvar ecb-stealthy-update-running nil | |
| "Recursion avoidance variable for stealthy performance.") | |
| (defecb-autocontrol/sync-function ecb-stealthy-updates nil nil nil | |
| "Run all functions in the stealthy function list. | |
| Each function returns 'done if it completes successfully, or something else if | |
| interrupted by the user \(i.e. the function has been interrupted by the | |
| user). If a function is interrupted then `ecb-stealthy-function-list' is | |
| rotated so the interrupted function is the first element so the next stealthy | |
| run starts with this interrupted function." | |
| (unless ecb-stealthy-update-running | |
| (let ((l ecb-stealthy-function-list) | |
| (ecb-stealthy-update-running t) | |
| ;; necessary because timers set this locally to t to prevent | |
| ;; timer-actions from being quitted by C-g. Our potentially long | |
| ;; lasting actions must be quit-able! | |
| (inhibit-quit nil)) | |
| (while (and l (equal 'done (funcall (car l)))) | |
| (setq l (cdr l))) | |
| ;; if l is nil this means all functions have successfully completed - | |
| ;; otherwise we ensure that next time we start with the interrupted | |
| ;; function. | |
| (when l | |
| (setq ecb-stealthy-function-list | |
| (ecb-rotate ecb-stealthy-function-list (car l))))))) | |
| ; | |
| ;; generation of nodes rsp. of attributes of nodes | |
| (defun ecb-generate-node-name (text-name first-chars icon-name name-of-buffer) | |
| "Generate a new name from TEXT-NAME by adding an appropriate image according | |
| to ICON-NAME to the first FIRST-CHARS of TEXT-NAME. If FIRST-CHARS is < 0 then | |
| a string with length abs\(FIRST-CHARS) is created, the image is applied to | |
| this new string and this \"image\"-string is added to the front of TEXT-NAME. | |
| If no image can be found for ICON-NAME then the original TEXT-NAME is | |
| returned. NAME-OF-BUFFER is the name of the tree-buffer where the resulting | |
| node-name will be displayed. | |
| If an image is added then two text-properties are added to the FIRST-CHARS of | |
| the returned string: 'tree-buffer-image-start which holds 0 as value and | |
| 'tree-buffer-image-length which holds the absolute value of FIRST-CHARS | |
| value." | |
| (let ((image nil) | |
| (ret nil)) | |
| (with-current-buffer name-of-buffer | |
| (setq image (and icon-name (tree-buffer-find-image icon-name))) | |
| (setq ret | |
| (if image | |
| (if (> first-chars 0) | |
| (tree-buffer-add-image-icon-maybe | |
| 0 first-chars text-name image) | |
| (concat (tree-buffer-add-image-icon-maybe | |
| 0 (- first-chars) | |
| (make-string (- first-chars) ? ) image) | |
| text-name)) | |
| text-name))) | |
| ret)) | |
| (silentcomp-provide 'ecb-common-browser) | |
| ;;; ecb-common-browser.el ends here |