Mercurial > emacs
diff lisp/cedet/semantic/idle.el @ 104419:b1ac14799f78
cedet/semantic/analyze.el, cedet/semantic/complete.el,
cedet/semantic/edit.el, cedet/semantic/html.el,
cedet/semantic/idle.el, cedet/semantic/texi.el: New files.
cedet/semantic/lex.el: Move defsubsts to front of file to avoid
compiler error.
author | Chong Yidong <cyd@stupidchicken.com> |
---|---|
date | Sat, 29 Aug 2009 19:00:35 +0000 |
parents | |
children | cc1b5aa56f20 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/cedet/semantic/idle.el Sat Aug 29 19:00:35 2009 +0000 @@ -0,0 +1,957 @@ +;;; idle.el --- Schedule parsing tasks in idle time + +;;; Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: syntax + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; Originally, `semantic-auto-parse-mode' handled refreshing the +;; tags in a buffer in idle time. Other activities can be scheduled +;; in idle time, all of which require up-to-date tag tables. +;; Having a specialized idle time scheduler that first refreshes +;; the tags buffer, and then enables other idle time tasks reduces +;; the amount of work needed. Any specialized idle tasks need not +;; ask for a fresh tags list. +;; +;; NOTE ON SEMANTIC_ANALYZE +;; +;; Some of the idle modes use the semantic analyzer. The analyzer +;; automatically caches the created context, so it is shared amongst +;; all idle modes that will need it. + +(require 'semantic/util-modes) +(require 'timer) + +;;; Code: + +;;; TIMER RELATED FUNCTIONS +;; +(defvar semantic-idle-scheduler-timer nil + "Timer used to schedule tasks in idle time.") + +(defvar semantic-idle-scheduler-work-timer nil + "Timer used to schedule tasks in idle time that may take a while.") + +(defcustom semantic-idle-scheduler-verbose-flag nil + "*Non-nil means that the idle scheduler should provide debug messages. +Use this setting to debug idle activities." + :group 'semantic + :type 'boolean) + +(defcustom semantic-idle-scheduler-idle-time 2 + "*Time in seconds of idle before scheduling events. +This time should be short enough to ensure that idle-scheduler will be +run as soon as Emacs is idle." + :group 'semantic + :type 'number + :set (lambda (sym val) + (set-default sym val) + (when (timerp semantic-idle-scheduler-timer) + (cancel-timer semantic-idle-scheduler-timer) + (setq semantic-idle-scheduler-timer nil) + (semantic-idle-scheduler-setup-timers)))) + +(defcustom semantic-idle-scheduler-work-idle-time 60 + "*Time in seconds of idle before scheduling big work. +This time should be long enough that once any big work is started, it is +unlikely the user would be ready to type again right away." + :group 'semantic + :type 'number + :set (lambda (sym val) + (set-default sym val) + (when (timerp semantic-idle-scheduler-timer) + (cancel-timer semantic-idle-scheduler-timer) + (setq semantic-idle-scheduler-timer nil) + (semantic-idle-scheduler-setup-timers)))) + +(defun semantic-idle-scheduler-setup-timers () + "Lazy initialization of the auto parse idle timer." + ;; REFRESH THIS FUNCTION for XEMACS FOIBLES + (or (timerp semantic-idle-scheduler-timer) + (setq semantic-idle-scheduler-timer + (run-with-idle-timer + semantic-idle-scheduler-idle-time t + #'semantic-idle-scheduler-function))) + (or (timerp semantic-idle-scheduler-work-timer) + (setq semantic-idle-scheduler-work-timer + (run-with-idle-timer + semantic-idle-scheduler-work-idle-time t + #'semantic-idle-scheduler-work-function))) + ) + +(defun semantic-idle-scheduler-kill-timer () + "Kill the auto parse idle timer." + (if (timerp semantic-idle-scheduler-timer) + (cancel-timer semantic-idle-scheduler-timer)) + (setq semantic-idle-scheduler-timer nil)) + + +;;; MINOR MODE +;; +;; The minor mode portion of this code just sets up the minor mode +;; which does the initial scheduling of the idle timers. +;; +(defcustom global-semantic-idle-scheduler-mode nil + "*If non-nil, enable global use of idle-scheduler mode." + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic/idle + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-idle-scheduler-mode (if val 1 -1)))) + +;;;###autoload +(defun global-semantic-idle-scheduler-mode (&optional arg) + "Toggle global use of option `semantic-idle-scheduler-mode'. +The idle scheduler with automatically reparse buffers in idle time, +and then schedule other jobs setup with `semantic-idle-scheduler-add'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-idle-scheduler-mode + (semantic-toggle-minor-mode-globally + 'semantic-idle-scheduler-mode arg))) + +(defcustom semantic-idle-scheduler-mode-hook nil + "*Hook run at the end of function `semantic-idle-scheduler-mode'." + :group 'semantic + :type 'hook) + +;;;###autoload +(defvar semantic-idle-scheduler-mode nil + "Non-nil if idle-scheduler minor mode is enabled. +Use the command `semantic-idle-scheduler-mode' to change this variable.") +(make-variable-buffer-local 'semantic-idle-scheduler-mode) + +(defcustom semantic-idle-scheduler-max-buffer-size 0 + "*Maximum size in bytes of buffers where idle-scheduler is enabled. +If this value is less than or equal to 0, idle-scheduler is enabled in +all buffers regardless of their size." + :group 'semantic + :type 'number) + +(defsubst semantic-idle-scheduler-enabled-p () + "Return non-nil if idle-scheduler is enabled for this buffer. +idle-scheduler is disabled when debugging or if the buffer size +exceeds the `semantic-idle-scheduler-max-buffer-size' threshold." + (and semantic-idle-scheduler-mode + (not semantic-debug-enabled) + (not semantic-lex-debug) + (or (<= semantic-idle-scheduler-max-buffer-size 0) + (< (buffer-size) semantic-idle-scheduler-max-buffer-size)))) + +(defun semantic-idle-scheduler-mode-setup () + "Setup option `semantic-idle-scheduler-mode'. +The minor mode can be turned on only if semantic feature is available +and the current buffer was set up for parsing. When minor mode is +enabled parse the current buffer if needed. Return non-nil if the +minor mode is enabled." + (if semantic-idle-scheduler-mode + (if (not (and (featurep 'semantic) (semantic-active-p))) + (progn + ;; Disable minor mode if semantic stuff not available + (setq semantic-idle-scheduler-mode nil) + (error "Buffer %s was not set up idle time scheduling" + (buffer-name))) + (semantic-idle-scheduler-setup-timers))) + semantic-idle-scheduler-mode) + +;;;###autoload +(defun semantic-idle-scheduler-mode (&optional arg) + "Minor mode to auto parse buffer following a change. +When this mode is off, a buffer is only rescanned for tokens when +some command requests the list of available tokens. When idle-scheduler +is enabled, Emacs periodically checks to see if the buffer is out of +date, and reparses while the user is idle (not typing.) + +With prefix argument ARG, turn on if positive, otherwise off. The +minor mode can be turned on only if semantic feature is available and +the current buffer was set up for parsing. Return non-nil if the +minor mode is enabled." + (interactive + (list (or current-prefix-arg + (if semantic-idle-scheduler-mode 0 1)))) + (setq semantic-idle-scheduler-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-idle-scheduler-mode))) + (semantic-idle-scheduler-mode-setup) + (run-hooks 'semantic-idle-scheduler-mode-hook) + (if (interactive-p) + (message "idle-scheduler minor mode %sabled" + (if semantic-idle-scheduler-mode "en" "dis"))) + (semantic-mode-line-update) + semantic-idle-scheduler-mode) + +(semantic-add-minor-mode 'semantic-idle-scheduler-mode + "ARP" + nil) + +(semantic-alias-obsolete 'semantic-auto-parse-mode + 'semantic-idle-scheduler-mode) +(semantic-alias-obsolete 'global-semantic-auto-parse-mode + 'global-semantic-idle-scheduler-mode) + + +;;; SERVICES services +;; +;; These are services for managing idle services. +;; +(defvar semantic-idle-scheduler-queue nil + "List of functions to execute during idle time. +These functions will be called in the current buffer after that +buffer has had its tags made up to date. These functions +will not be called if there are errors parsing the +current buffer.") + +;;;###autoload +(defun semantic-idle-scheduler-add (function) + "Schedule FUNCTION to occur during idle time." + (add-to-list 'semantic-idle-scheduler-queue function)) + +;;;###autoload +(defun semantic-idle-scheduler-remove (function) + "Unschedule FUNCTION to occur during idle time." + (setq semantic-idle-scheduler-queue + (delete function semantic-idle-scheduler-queue))) + +;;; IDLE Function +;; +(defun semantic-idle-core-handler () + "Core idle function that handles reparsing. +And also manages services that depend on tag values." + (when semantic-idle-scheduler-verbose-flag + (message "IDLE: Core handler...")) + (semantic-exit-on-input 'idle-timer + (let* ((inhibit-quit nil) + (buffers (delq (current-buffer) + (delq nil + (mapcar #'(lambda (b) + (and (buffer-file-name b) + b)) + (buffer-list))))) + safe ;; This safe is not used, but could be. + others + mode) + (when (semantic-idle-scheduler-enabled-p) + (save-excursion + ;; First, reparse the current buffer. + (setq mode major-mode + safe (semantic-safe "Idle Parse Error: %S" + ;(error "Goofy error 1") + (semantic-idle-scheduler-refresh-tags) + ) + ) + ;; Now loop over other buffers with same major mode, trying to + ;; update them as well. Stop on keypress. + (dolist (b buffers) + (semantic-throw-on-input 'parsing-mode-buffers) + (with-current-buffer b + (if (eq major-mode mode) + (and (semantic-idle-scheduler-enabled-p) + (semantic-safe "Idle Parse Error: %S" + ;(error "Goofy error") + (semantic-idle-scheduler-refresh-tags))) + (push (current-buffer) others)))) + (setq buffers others)) + ;; If re-parse of current buffer completed, evaluate all other + ;; services. Stop on keypress. + + ;; NOTE ON COMMENTED SAFE HERE + ;; We used to not execute the services if the buffer wsa + ;; unparseable. We now assume that they are lexically + ;; safe to do, because we have marked the buffer unparseable + ;; if there was a problem. + ;;(when safe + (dolist (service semantic-idle-scheduler-queue) + (save-excursion + (semantic-throw-on-input 'idle-queue) + (when semantic-idle-scheduler-verbose-flag + (message "IDLE: execture service %s..." service)) + (semantic-safe (format "Idle Service Error %s: %%S" service) + (funcall service)) + (when semantic-idle-scheduler-verbose-flag + (message "IDLE: execture service %s...done" service)) + ))) + ;;) + ;; Finally loop over remaining buffers, trying to update them as + ;; well. Stop on keypress. + (save-excursion + (dolist (b buffers) + (semantic-throw-on-input 'parsing-other-buffers) + (with-current-buffer b + (and (semantic-idle-scheduler-enabled-p) + (semantic-idle-scheduler-refresh-tags))))) + )) + (when semantic-idle-scheduler-verbose-flag + (message "IDLE: Core handler...done"))) + +(defun semantic-debug-idle-function () + "Run the Semantic idle function with debugging turned on." + (interactive) + (let ((debug-on-error t)) + (semantic-idle-core-handler) + )) + +(defun semantic-idle-scheduler-function () + "Function run when after `semantic-idle-scheduler-idle-time'. +This function will reparse the current buffer, and if successful, +call additional functions registered with the timer calls." + (when (zerop (recursion-depth)) + (let ((debug-on-error nil)) + (save-match-data (semantic-idle-core-handler)) + ))) + + +;;; WORK FUNCTION +;; +;; Unlike the shorter timer, the WORK timer will kick of tasks that +;; may take a long time to complete. +(defcustom semantic-idle-work-parse-neighboring-files-flag t + "*Non-nil means to parse files in the same dir as the current buffer. +Disable to prevent lots of excessive parsing in idle time." + :group 'semantic + :type 'boolean) + + +(defun semantic-idle-work-for-one-buffer (buffer) + "Do long-processing work for for BUFFER. +Uses `semantic-safe' and returns the output. +Returns t of all processing succeeded." + (save-excursion + (set-buffer buffer) + (not (and + ;; Just in case + (semantic-safe "Idle Work Parse Error: %S" + (semantic-idle-scheduler-refresh-tags) + t) + + ;; Force all our include files to get read in so we + ;; are ready to provide good smart completion and idle + ;; summary information + (semantic-safe "Idle Work Including Error: %S" + ;; Get the include related path. + (when (and (featurep 'semantic/db) + (semanticdb-minor-mode-p)) + (require 'semantic/db-find) + (semanticdb-find-translate-path buffer nil) + ) + t) + + ;; Pre-build the typecaches as needed. + (semantic-safe "Idle Work Typecaching Error: %S" + (when (featurep 'semantic/db-typecache) + (semanticdb-typecache-refresh-for-buffer buffer)) + t) + )) + )) + +(defun semantic-idle-work-core-handler () + "Core handler for idle work processing of long running tasks. +Visits semantic controlled buffers, and makes sure all needed +include files have been parsed, and that the typecache is up to date. +Uses `semantic-idle-work-for-on-buffer' to do the work." + (let ((errbuf nil) + (interrupted + (semantic-exit-on-input 'idle-work-timer + (let* ((inhibit-quit nil) + (cb (current-buffer)) + (buffers (delq (current-buffer) + (delq nil + (mapcar #'(lambda (b) + (and (buffer-file-name b) + b)) + (buffer-list))))) + safe errbuf) + ;; First, handle long tasks in the current buffer. + (when (semantic-idle-scheduler-enabled-p) + (save-excursion + (setq safe (semantic-idle-work-for-one-buffer (current-buffer)) + ))) + (when (not safe) (push (current-buffer) errbuf)) + + ;; Now loop over other buffers with same major mode, trying to + ;; update them as well. Stop on keypress. + (dolist (b buffers) + (semantic-throw-on-input 'parsing-mode-buffers) + (with-current-buffer b + (when (semantic-idle-scheduler-enabled-p) + (and (semantic-idle-scheduler-enabled-p) + (unless (semantic-idle-work-for-one-buffer (current-buffer)) + (push (current-buffer) errbuf))) + )) + ) + + ;; Save everything. + (semanticdb-save-all-db-idle) + + ;; Parse up files near our active buffer + (when semantic-idle-work-parse-neighboring-files-flag + (semantic-safe "Idle Work Parse Neighboring Files: %S" + (when (and (featurep 'semantic/db) + (semanticdb-minor-mode-p)) + (set-buffer cb) + (semantic-idle-scheduler-work-parse-neighboring-files)) + t) + ) + + ;; Save everything... again + (semanticdb-save-all-db-idle) + + ;; Done w/ processing + nil)))) + + ;; Done + (if interrupted + "Interrupted" + (cond ((not errbuf) + "done") + ((not (cdr errbuf)) + (format "done with 1 error in %s" (car errbuf))) + (t + (format "done with errors in %d buffers." + (length errbuf))))))) + +(defun semantic-debug-idle-work-function () + "Run the Semantic idle work function with debugging turned on." + (interactive) + (let ((debug-on-error t)) + (semantic-idle-work-core-handler) + )) + +(defun semantic-idle-scheduler-work-function () + "Function run when after `semantic-idle-scheduler-work-idle-time'. +This routine handles difficult tasks that require a lot of parsing, such as +parsing all the header files used by our active sources, or building up complex +datasets." + (when semantic-idle-scheduler-verbose-flag + (message "Long Work Idle Timer...")) + (let ((exit-type (save-match-data + (semantic-idle-work-core-handler)))) + (when semantic-idle-scheduler-verbose-flag + (message "Long Work Idle Timer...%s" exit-type))) + ) + +(defun semantic-idle-scheduler-work-parse-neighboring-files () + "Parse all the files in similar directories to buffers being edited." + ;; Lets check to see if EDE matters. + (let ((ede-auto-add-method 'never)) + (dolist (a auto-mode-alist) + (when (eq (cdr a) major-mode) + (dolist (file (directory-files default-directory t (car a) t)) + (semantic-throw-on-input 'parsing-mode-buffers) + (save-excursion + (semanticdb-file-table-object file) + )))) + )) + +(defun semantic-idle-pnf-test () + "Test `semantic-idle-scheduler-work-parse-neighboring-files' and time it." + (interactive) + (let ((start (current-time)) + (junk (semantic-idle-scheduler-work-parse-neighboring-files)) + (end (current-time))) + (message "Work took %.2f seconds." (semantic-elapsed-time start end))) + ) + + +;;; REPARSING +;; +;; Reparsing is installed as semantic idle service. +;; This part ALWAYS happens, and other services occur +;; afterwards. + +;; (defcustom semantic-idle-scheduler-no-working-message t +;; "*If non-nil, disable display of working messages during parse." +;; :group 'semantic +;; :type 'boolean) + +;; (defcustom semantic-idle-scheduler-working-in-modeline-flag nil +;; "*Non-nil means show working messages in the mode line. +;; Typically, parsing will show messages in the minibuffer. +;; This will move the parse message into the modeline." +;; :group 'semantic +;; :type 'boolean) + +(defvar semantic-before-idle-scheduler-reparse-hooks nil + "Hooks run before option `semantic-idle-scheduler' begins parsing. +If any hook throws an error, this variable is reset to nil. +This hook is not protected from lexical errors.") + +(defvar semantic-after-idle-scheduler-reparse-hooks nil + "Hooks run after option `semantic-idle-scheduler' has parsed. +If any hook throws an error, this variable is reset to nil. +This hook is not protected from lexical errors.") + +(defun semantic-idle-scheduler-refresh-tags () + "Refreshes the current buffer's tags. +This is called by `semantic-idle-scheduler-function' to update the +tags in the current buffer. + +Return non-nil if the refresh was successful. +Return nil if there is some sort of syntax error preventing a full +reparse. + +Does nothing if the current buffer doesn't need reparsing." + + (prog1 + ;; These checks actually occur in `semantic-fetch-tags', but if we + ;; do them here, then all the bovination hooks are not run, and + ;; we save lots of time. + (cond + ;; If the buffer was previously marked unparseable, + ;; then don't waste our time. + ((semantic-parse-tree-unparseable-p) + nil) + ;; The parse tree is already ok. + ((semantic-parse-tree-up-to-date-p) + t) + (t + ;; If the buffer might need a reparse and it is safe to do so, + ;; give it a try. + (let* (;(semantic-working-type nil) + (inhibit-quit nil) + ;; (working-use-echo-area-p + ;; (not semantic-idle-scheduler-working-in-modeline-flag)) + ;; (working-status-dynamic-type + ;; (if semantic-idle-scheduler-no-working-message + ;; nil + ;; working-status-dynamic-type)) + ;; (working-status-percentage-type + ;; (if semantic-idle-scheduler-no-working-message + ;; nil + ;; working-status-percentage-type)) + (lexically-safe t) + ) + ;; Let people hook into this, but don't let them hose + ;; us over! + (condition-case nil + (run-hooks 'semantic-before-idle-scheduler-reparse-hooks) + (error (setq semantic-before-idle-scheduler-reparse-hooks nil))) + + (unwind-protect + ;; Perform the parsing. + (progn + (when semantic-idle-scheduler-verbose-flag + (message "IDLE: reparse %s..." (buffer-name))) + (when (semantic-lex-catch-errors idle-scheduler + (save-excursion (semantic-fetch-tags)) + nil) + ;; If we are here, it is because the lexical step failed, + ;; proably due to unterminated lists or something like that. + + ;; We do nothing, and just wait for the next idle timer + ;; to go off. In the meantime, remember this, and make sure + ;; no other idle services can get executed. + (setq lexically-safe nil)) + (when semantic-idle-scheduler-verbose-flag + (message "IDLE: reparse %s...done" (buffer-name)))) + ;; Let people hook into this, but don't let them hose + ;; us over! + (condition-case nil + (run-hooks 'semantic-after-idle-scheduler-reparse-hooks) + (error (setq semantic-after-idle-scheduler-reparse-hooks nil)))) + ;; Return if we are lexically safe (from prog1) + lexically-safe))) + + ;; After updating the tags, handle any pending decorations for this + ;; buffer. + (semantic-decorate-flush-pending-decorations (current-buffer)) + )) + + +;;; IDLE SERVICES +;; +;; Idle Services are minor modes which enable or disable a services in +;; the idle scheduler. Creating a new services only requires calling +;; `semantic-create-idle-services' which does all the setup +;; needed to create the minor mode that will enable or disable +;; a services. The services must provide a single function. + +(defmacro define-semantic-idle-service (name doc &rest forms) + "Create a new idle services with NAME. +DOC will be a documentation string describing FORMS. +FORMS will be called during idle time after the current buffer's +semantic tag information has been updated. +This routines creates the following functions and variables:" + (let ((global (intern (concat "global-" (symbol-name name) "-mode"))) + (mode (intern (concat (symbol-name name) "-mode"))) + (hook (intern (concat (symbol-name name) "-mode-hook"))) + (map (intern (concat (symbol-name name) "-mode-map"))) + (setup (intern (concat (symbol-name name) "-mode-setup"))) + (func (intern (concat (symbol-name name) "-idle-function"))) + ) + + `(eval-and-compile + (defun ,global (&optional arg) + ,(concat "Toggle global use of option `" (symbol-name mode) "'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle.") + (interactive "P") + (setq ,global + (semantic-toggle-minor-mode-globally + ',mode arg))) + + (defcustom ,global nil + (concat "*If non-nil, enable global use of `" (symbol-name ',mode) "'. +" ,doc) + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic/idle + :initialize 'custom-initialize-default + :set (lambda (sym val) + (,global (if val 1 -1)))) + + (defcustom ,hook nil + (concat "*Hook run at the end of function `" (symbol-name ',mode) "'.") + :group 'semantic + :type 'hook) + + (defvar ,map + (let ((km (make-sparse-keymap))) + km) + (concat "Keymap for `" (symbol-name ',mode) "'.")) + + (defvar ,mode nil + (concat "Non-nil if summary minor mode is enabled. +Use the command `" (symbol-name ',mode) "' to change this variable.")) + (make-variable-buffer-local ',mode) + + (defun ,setup () + ,(concat "Setup option `" (symbol-name mode) "'. +The minor mode can be turned on only if semantic feature is available +and the idle scheduler is active. +Return non-nil if the minor mode is enabled.") + (if ,mode + (if (not (and (featurep 'semantic) (semantic-active-p))) + (progn + ;; Disable minor mode if semantic stuff not available + (setq ,mode nil) + (error "Buffer %s was not set up for parsing" + (buffer-name))) + ;; Enable the mode mode + (semantic-idle-scheduler-add #',func) + ) + ;; Disable the mode mode + (semantic-idle-scheduler-remove #',func) + ) + ,mode) + +;;;###autoload + (defun ,mode (&optional arg) + ,(concat doc " +This is a minor mode which performs actions during idle time. +With prefix argument ARG, turn on if positive, otherwise off. The +minor mode can be turned on only if semantic feature is available and +the current buffer was set up for parsing. Return non-nil if the +minor mode is enabled.") + (interactive + (list (or current-prefix-arg + (if ,mode 0 1)))) + (setq ,mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not ,mode))) + (,setup) + (run-hooks ,hook) + (if (interactive-p) + (message "%s %sabled" + (symbol-name ',mode) + (if ,mode "en" "dis"))) + (semantic-mode-line-update) + ,mode) + + (semantic-add-minor-mode ',mode + "" ; idle schedulers are quiet? + ,map) + + (defun ,func () + ,doc + ,@forms) + + ))) +(put 'define-semantic-idle-service 'lisp-indent-function 1) + + +;;; SUMMARY MODE +;; +;; A mode similar to eldoc using semantic +(require 'semantic/ctxt) + +(defcustom semantic-idle-summary-function + 'semantic-format-tag-summarize-with-file + "*Function to use when displaying tag information during idle time. +Some useful functions are found in `semantic-format-tag-functions'." + :group 'semantic + :type semantic-format-tag-custom-list) + +(defsubst semantic-idle-summary-find-current-symbol-tag (sym) + "Search for a semantic tag with name SYM in database tables. +Return the tag found or nil if not found. +If semanticdb is not in use, use the current buffer only." + (car (if (and (featurep 'semantic/db) semanticdb-current-database) + (cdar (semanticdb-deep-find-tags-by-name sym)) + (semantic-deep-find-tags-by-name sym (current-buffer))))) + +(defun semantic-idle-summary-current-symbol-info-brutish () + "Return a string message describing the current context. +Gets a symbol with `semantic-ctxt-current-thing' and then +trys to find it with a deep targetted search." + ;; Try the current "thing". + (let ((sym (car (semantic-ctxt-current-thing)))) + (when sym + (semantic-idle-summary-find-current-symbol-tag sym)))) + +(defun semantic-idle-summary-current-symbol-keyword () + "Return a string message describing the current symbol. +Returns a value only if it is a keyword." + ;; Try the current "thing". + (let ((sym (car (semantic-ctxt-current-thing)))) + (if (and sym (semantic-lex-keyword-p sym)) + (semantic-lex-keyword-get sym 'summary)))) + +(defun semantic-idle-summary-current-symbol-info-context () + "Return a string message describing the current context. +Use the semantic analyzer to find the symbol information." + (let ((analysis (condition-case nil + (semantic-analyze-current-context (point)) + (error nil)))) + (when analysis + (semantic-analyze-interesting-tag analysis)))) + +(defun semantic-idle-summary-current-symbol-info-default () + "Return a string message describing the current context. +This functin will disable loading of previously unloaded files +by semanticdb as a time-saving measure." + (let ( + (semanticdb-find-default-throttle + (if (featurep 'semantic/db-find) + (remq 'unloaded semanticdb-find-default-throttle) + nil)) + ) + (save-excursion + ;; use whicever has success first. + (or + (semantic-idle-summary-current-symbol-keyword) + + (semantic-idle-summary-current-symbol-info-context) + + (semantic-idle-summary-current-symbol-info-brutish) + )))) + +(defvar semantic-idle-summary-out-of-context-faces + '( + font-lock-comment-face + font-lock-string-face + font-lock-doc-string-face ; XEmacs. + font-lock-doc-face ; Emacs 21 and later. + ) + "List of font-lock faces that indicate a useless summary context. +Those are generally faces used to highlight comments. + +It might be useful to override this variable to add comment faces +specific to a major mode. For example, in jde mode: + +\(defvar-mode-local jde-mode semantic-idle-summary-out-of-context-faces + (append (default-value 'semantic-idle-summary-out-of-context-faces) + '(jde-java-font-lock-doc-tag-face + jde-java-font-lock-link-face + jde-java-font-lock-bold-face + jde-java-font-lock-underline-face + jde-java-font-lock-pre-face + jde-java-font-lock-code-face)))") + +(defun semantic-idle-summary-useful-context-p () + "Non-nil of we should show a summary based on context." + (if (and (boundp 'font-lock-mode) + font-lock-mode + (memq (get-text-property (point) 'face) + semantic-idle-summary-out-of-context-faces)) + ;; The best I can think of at the moment is to disable + ;; in comments by detecting with font-lock. + nil + t)) + +(define-overloadable-function semantic-idle-summary-current-symbol-info () + "Return a string message describing the current context.") + +(make-obsolete-overload 'semantic-eldoc-current-symbol-info + 'semantic-idle-summary-current-symbol-info) + +(define-semantic-idle-service semantic-idle-summary + "Display a tag summary of the lexical token under the cursor. +Call `semantic-idle-summary-current-symbol-info' for getting the +current tag to display information." + (or (eq major-mode 'emacs-lisp-mode) + (not (semantic-idle-summary-useful-context-p)) + (let* ((found (semantic-idle-summary-current-symbol-info)) + (str (cond ((stringp found) found) + ((semantic-tag-p found) + (funcall semantic-idle-summary-function + found nil t)))) + ) + ;; Show the message with eldoc functions + (require 'eldoc) + (unless (and str (boundp 'eldoc-echo-area-use-multiline-p) + eldoc-echo-area-use-multiline-p) + (let ((w (1- (window-width (minibuffer-window))))) + (if (> (length str) w) + (setq str (substring str 0 w))))) + (eldoc-message str)))) + +(semantic-alias-obsolete 'semantic-summary-mode + 'semantic-idle-summary-mode) +(semantic-alias-obsolete 'global-semantic-summary-mode + 'global-semantic-idle-summary-mode) + +;;; Current symbol highlight +;; +;; This mode will use context analysis to perform highlighting +;; of all uses of the symbol that is under the cursor. +;; +;; This is to mimic the Eclipse tool of a similar nature. +(defvar semantic-idle-summary-highlight-face 'region + "Face used for the summary highlight.") + +(defun semantic-idle-summary-maybe-highlight (tag) + "Perhaps add highlighting onto TAG. +TAG was found as the thing under point. If it happens to be +visible, then highlight it." + (let* ((region (when (and (semantic-tag-p tag) + (semantic-tag-with-position-p tag)) + (semantic-tag-overlay tag))) + (file (when (and (semantic-tag-p tag) + (semantic-tag-with-position-p tag)) + (semantic-tag-file-name tag))) + (buffer (when file (get-file-buffer file))) + ;; We use pulse, but we don't want the flashy version, + ;; just the stable version. + (pulse-flag nil) + ) + (cond ((semantic-overlay-p region) + (save-excursion + (set-buffer (semantic-overlay-buffer region)) + (goto-char (semantic-overlay-start region)) + (when (pos-visible-in-window-p + (point) (get-buffer-window (current-buffer) 'visible)) + (if (< (semantic-overlay-end region) (point-at-eol)) + (pulse-momentary-highlight-overlay + region semantic-idle-summary-highlight-face) + ;; Not the same + (pulse-momentary-highlight-region + (semantic-overlay-start region) + (point-at-eol) + semantic-idle-summary-highlight-face))) + )) + ((vectorp region) + (let ((start (aref region 0)) + (end (aref region 1))) + (save-excursion + (when buffer (set-buffer buffer)) + ;; As a vector, we have no filename. Perhaps it is a + ;; local variable? + (when (and (<= end (point-max)) + (pos-visible-in-window-p + start (get-buffer-window (current-buffer) 'visible))) + (goto-char start) + (when (re-search-forward + (regexp-quote (semantic-tag-name tag)) + end t) + ;; This is likely it, give it a try. + (pulse-momentary-highlight-region + start (if (<= end (point-at-eol)) end + (point-at-eol)) + semantic-idle-summary-highlight-face))) + )))) + nil)) + +(define-semantic-idle-service semantic-idle-tag-highlight + "Highlight the tag, and references of the symbol under point. +Call `semantic-analyze-current-context' to find the reference tag. +Call `semantic-symref-hits-in-region' to identify local references." + (when (semantic-idle-summary-useful-context-p) + (let* ((ctxt (semantic-analyze-current-context)) + (Hbounds (when ctxt (oref ctxt bounds))) + (target (when ctxt (car (reverse (oref ctxt prefix))))) + (tag (semantic-current-tag)) + ;; We use pulse, but we don't want the flashy version, + ;; just the stable version. + (pulse-flag nil)) + (when ctxt + ;; Highlight the original tag? Protect against problems. + (condition-case nil + (semantic-idle-summary-maybe-highlight target) + (error nil)) + ;; Identify all hits in this current tag. + (when (semantic-tag-p target) + (semantic-symref-hits-in-region + target (lambda (start end prefix) + (when (/= start (car Hbounds)) + (pulse-momentary-highlight-region + start end)) + (semantic-throw-on-input 'symref-highlight) + ) + (semantic-tag-start tag) + (semantic-tag-end tag))) + )))) + + +;;; Completion Popup Mode +;; +;; This mode uses tooltips to display a (hopefully) short list of possible +;; completions available for the text under point. It provides +;; NO provision for actually filling in the values from those completions. + +(defun semantic-idle-completion-list-default () + "Calculate and display a list of completions." + (when (semantic-idle-summary-useful-context-p) + ;; This mode can be fragile. Ignore problems. + ;; If something doesn't do what you expect, run + ;; the below command by hand instead. + (condition-case nil + (let ( + ;; Don't go loading in oodles of header libraries in + ;; IDLE time. + (semanticdb-find-default-throttle + (if (featurep 'semantic/db-find) + (remq 'unloaded semanticdb-find-default-throttle) + nil)) + ) + ;; Use idle version. + (semantic-complete-analyze-inline-idle) + ) + (error nil)) + )) + +(define-semantic-idle-service semantic-idle-completions + "Display a list of possible completions in a tooltip." + ;; Add the ability to override sometime. + (semantic-idle-completion-list-default)) + +(provide 'semantic/idle) + +;;; semantic-idle.el ends here