diff options
author | Chong Yidong <cyd@stupidchicken.com> | 2009-09-28 15:15:00 +0000 |
---|---|---|
committer | Chong Yidong <cyd@stupidchicken.com> | 2009-09-28 15:15:00 +0000 |
commit | b90caf50d04d2c51742054bb6b0e836f6d425203 (patch) | |
tree | 945883cac64de9ceff0c8207c8b8ec2bc6c11932 /lisp/cedet/semantic | |
parent | 0e7b286792c2879dba8e1dd8b94a4a30293e20b3 (diff) | |
parent | a2095e2edba95e01f3be50ead7cc4b1c53bd40f3 (diff) | |
download | emacs-b90caf50d04d2c51742054bb6b0e836f6d425203.tar.gz |
CEDET (development tools) package merged.
* cedet/*.el:
* cedet/ede/*.el:
* cedet/semantic/*.el:
* cedet/srecode/*.el: New files.
Diffstat (limited to 'lisp/cedet/semantic')
75 files changed, 45028 insertions, 0 deletions
diff --git a/lisp/cedet/semantic/analyze.el b/lisp/cedet/semantic/analyze.el new file mode 100644 index 00000000000..4948bba794e --- /dev/null +++ b/lisp/cedet/semantic/analyze.el @@ -0,0 +1,798 @@ +;;; semantic/analyze.el --- Analyze semantic tags against local context + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Semantic, as a tool, provides a nice list of searchable tags. +;; That information can provide some very accurate answers if the current +;; context of a position is known. +;; +;; Semantic-ctxt provides ways of analyzing, and manipulating the +;; semantic context of a language in code. +;; +;; This library provides routines for finding intelligent answers to +;; tough problems, such as if an argument to a function has the correct +;; return type, or all possible tags that fit in a given local context. +;; + +;;; Vocabulary: +;; +;; Here are some words used to describe different things in the analyzer: +;; +;; tag - A single entity +;; prefix - The beginning of a symbol, usually used to look up something +;; incomplete. +;; type - The name of a datatype in the langauge. +;; metatype - If a type is named in a declaration like: +;; struct moose somevariable; +;; that name "moose" can be turned into a concrete type. +;; tag sequence - In C code, a list of dereferences, such as: +;; this.that.theother(); +;; parent - For a datatype in an OO language, another datatype +;; inherited from. This excludes interfaces. +;; scope - A list of tags that can be dereferenced that cannot +;; be found from the global namespace. +;; scopetypes - A list of tags which are datatype that contain +;; the scope. The scopetypes need to have the scope extracted +;; in a way that honors the type of inheritance. +;; nest/nested - When one tag is contained entirely in another. +;; +;; context - A semantic datatype representing a point in a buffer. +;; +;; constriant - If a context specifies a specific datatype is needed, +;; that is a constraint. +;; constants - Some datatypes define elements of themselves as a +;; constant. These need to be returned as there would be no +;; other possible completions. + +(require 'semantic) +(require 'semantic/format) +(require 'semantic/ctxt) +(require 'semantic/scope) +(require 'semantic/sort) +(require 'semantic/analyze/fcn) + +(eval-when-compile (require 'semantic/find)) + +(declare-function data-debug-new-buffer "data-debug") +(declare-function data-debug-insert-object-slots "eieio-datadebug") + +;;; Code: +(defvar semantic-analyze-error-stack nil + "Collection of any errors thrown during analysis.") + +(defun semantic-analyze-push-error (err) + "Push the error in ERR-DATA onto the error stack. +Argument ERR" + (push err semantic-analyze-error-stack)) + +;;; Analysis Classes +;; +;; These classes represent what a context is. Different types +;; of contexts provide differing amounts of information to help +;; provide completions. +;; +(defclass semantic-analyze-context () + ((bounds :initarg :bounds + :type list + :documentation "The bounds of this context. +Usually bound to the dimension of a single symbol or command.") + (prefix :initarg :prefix + :type list + :documentation "List of tags defining local text. +This can be nil, or a list where the last element can be a string +representing text that may be incomplete. Preceeding elements +must be semantic tags representing variables or functions +called in a dereference sequence.") + (prefixclass :initarg :prefixclass + :type list + :documentation "Tag classes expected at this context. +These are clases for tags, such as 'function, or 'variable.") + (prefixtypes :initarg :prefixtypes + :type list + :documentation "List of tags defining types for :prefix. +This list is one shorter than :prefix. Each element is a semantic +tag representing a type matching the semantic tag in the same +position in PREFIX.") + (scope :initarg :scope + :type (or null semantic-scope-cache) + :documentation "List of tags available in scopetype. +See `semantic-analyze-scoped-tags' for details.") + (buffer :initarg :buffer + :type buffer + :documentation "The buffer this context is derived from.") + (errors :initarg :errors + :documentation "Any errors thrown an caught during analysis.") + ) + "Base analysis data for a any context.") + +(defclass semantic-analyze-context-assignment (semantic-analyze-context) + ((assignee :initarg :assignee + :type list + :documentation "A sequence of tags for an assignee. +This is a variable into which some value is being placed. The last +item in the list is the variable accepting the value. Earlier +tags represent the variables being derefernece to get to the +assignee.")) + "Analysis class for a value in an assignment.") + +(defclass semantic-analyze-context-functionarg (semantic-analyze-context) + ((function :initarg :function + :type list + :documentation "A sequence of tags for a function. +This is a function being called. The cursor will be in the position +of an argument. +The last tag in :function is the function being called. Earlier +tags represent the variables being dereferenced to get to the +function.") + (index :initarg :index + :type integer + :documentation "The index of the argument for this context. +If a function takes 4 arguments, this value should be bound to +the values 1 through 4.") + (argument :initarg :argument + :type list + :documentation "A sequence of tags for the :index argument. +The argument can accept a value of some type, and this contains the +tag for that definition. It should be a tag, but might +be just a string in some circumstances.") + ) + "Analysis class for a value as a function argument.") + +(defclass semantic-analyze-context-return (semantic-analyze-context) + () ; No extra data. + "Analysis class for return data. +Return data methods identify the requred type by the return value +of the parent function.") + +;;; METHODS +;; +;; Simple methods against the context classes. +;; +(defmethod semantic-analyze-type-constraint + ((context semantic-analyze-context) &optional desired-type) + "Return a type constraint for completing :prefix in CONTEXT. +Optional argument DESIRED-TYPE may be a non-type tag to analyze." + (when (semantic-tag-p desired-type) + ;; Convert the desired type if needed. + (if (not (eq (semantic-tag-class desired-type) 'type)) + (setq desired-type (semantic-tag-type desired-type))) + ;; Protect against plain strings + (cond ((stringp desired-type) + (setq desired-type (list desired-type 'type))) + ((and (stringp (car desired-type)) + (not (semantic-tag-p desired-type))) + (setq desired-type (list (car desired-type) 'type))) + ((semantic-tag-p desired-type) + ;; We have a tag of some sort. Yay! + nil) + (t (setq desired-type nil)) + ) + desired-type)) + +(defmethod semantic-analyze-type-constraint + ((context semantic-analyze-context-functionarg)) + "Return a type constraint for completing :prefix in CONTEXT." + (call-next-method context (car (oref context argument)))) + +(defmethod semantic-analyze-type-constraint + ((context semantic-analyze-context-assignment)) + "Return a type constraint for completing :prefix in CONTEXT." + (call-next-method context (car (reverse (oref context assignee))))) + +(defmethod semantic-analyze-interesting-tag + ((context semantic-analyze-context)) + "Return a tag from CONTEXT that would be most interesting to a user." + (let ((prefix (reverse (oref context :prefix)))) + ;; Go back through the prefix until we find a tag we can return. + (while (and prefix (not (semantic-tag-p (car prefix)))) + (setq prefix (cdr prefix))) + ;; Return the found tag, or nil. + (car prefix))) + +(defmethod semantic-analyze-interesting-tag + ((context semantic-analyze-context-functionarg)) + "Try the base, and if that fails, return what we are assigning into." + (or (call-next-method) (car-safe (oref context :function)))) + +(defmethod semantic-analyze-interesting-tag + ((context semantic-analyze-context-assignment)) + "Try the base, and if that fails, return what we are assigning into." + (or (call-next-method) (car-safe (oref context :assignee)))) + +;;; ANALYSIS +;; +;; Start out with routines that will calculate useful parts of +;; the general analyzer function. These could be used directly +;; by an application that doesn't need to calculate the full +;; context. + +(define-overloadable-function semantic-analyze-find-tag-sequence (sequence &optional + scope typereturn throwsym) + "Attempt to find all tags in SEQUENCE. +Optional argument LOCALVAR is the list of local variables to use when +finding the details on the first element of SEQUENCE in case +it is not found in the global set of tables. +Optional argument SCOPE are additional terminals to search which are currently +scoped. These are not local variables, but symbols available in a structure +which doesn't need to be dereferneced. +Optional argument TYPERETURN is a symbol in which the types of all found +will be stored. If nil, that data is thrown away. +Optional argument THROWSYM specifies a symbol the throw on non-recoverable error.") + +(defun semantic-analyze-find-tag-sequence-default (sequence &optional + scope typereturn + throwsym) + "Attempt to find all tags in SEQUENCE. +SCOPE are extra tags which are in scope. +TYPERETURN is a symbol in which to place a list of tag classes that +are found in SEQUENCE. +Optional argument THROWSYM specifies a symbol the throw on non-recoverable error." + (let ((s sequence) ; copy of the sequence + (tmp nil) ; tmp find variable + (tag nil) ; tag return list + (tagtype nil) ; tag types return list + (fname nil) + (miniscope (clone scope)) + ) + ;; First order check. Is this wholely contained in the typecache? + (setq tmp (semanticdb-typecache-find sequence)) + + (if tmp + (progn + ;; We are effectively done... + (setq s nil) + (setq tag (list tmp))) + + ;; For the first entry, it better be a variable, but it might + ;; be in the local context too. + ;; NOTE: Don't forget c++ namespace foo::bar. + (setq tmp (or + ;; Is this tag within our scope. Scopes can sometimes + ;; shadow other things, so it goes first. + (and scope (semantic-scope-find (car s) nil scope)) + ;; Find the tag out there... somewhere, but not in scope + (semantic-analyze-find-tag (car s)) + )) + + (if (and (listp tmp) (semantic-tag-p (car tmp))) + (setq tmp (semantic-analyze-select-best-tag tmp))) + (if (not (semantic-tag-p tmp)) + (if throwsym + (throw throwsym "Cannot find definition") + (error "Cannot find definition for \"%s\"" (car s)))) + (setq s (cdr s)) + (setq tag (cons tmp tag)) ; tag is nil here... + (setq fname (semantic-tag-file-name tmp)) + ) + + ;; For the middle entries + (while s + ;; Using the tag found in TMP, lets find the tag + ;; representing the full typeographic information of its + ;; type, and use that to determine the search context for + ;; (car s) + (let* ((tmptype + ;; In some cases the found TMP is a type, + ;; and we can use it directly. + (cond ((semantic-tag-of-class-p tmp 'type) + ;; update the miniscope when we need to analyze types directly. + (let ((rawscope + (apply 'append + (mapcar 'semantic-tag-type-members + tagtype)))) + (oset miniscope fullscope rawscope)) + ;; Now analayze the type to remove metatypes. + (or (semantic-analyze-type tmp miniscope) + tmp)) + (t + (semantic-analyze-tag-type tmp scope)))) + (typefile + (when tmptype + (semantic-tag-file-name tmptype))) + (slots nil)) + + ;; Get the children + (setq slots (semantic-analyze-scoped-type-parts tmptype scope)) + + ;; find (car s) in the list o slots + (setq tmp (semantic-find-tags-by-name (car s) slots)) + + ;; If we have lots + (if (and (listp tmp) (semantic-tag-p (car tmp))) + (setq tmp (semantic-analyze-select-best-tag tmp))) + + ;; Make sure we have a tag. + (if (not (semantic-tag-p tmp)) + (if (cdr s) + ;; In the middle, we need to keep seeking our types out. + (error "Cannot find definition for \"%s\"" (car s)) + ;; Else, it's ok to end with a non-tag + (setq tmp (car s)))) + + (setq fname (or typefile fname)) + (when (and fname (semantic-tag-p tmp) + (not (semantic-tag-in-buffer-p tmp))) + (semantic--tag-put-property tmp :filename fname)) + (setq tag (cons tmp tag)) + (setq tagtype (cons tmptype tagtype)) + ) + (setq s (cdr s))) + + (if typereturn (set typereturn (nreverse tagtype))) + ;; Return the mess + (nreverse tag))) + +(defun semantic-analyze-find-tag (name &optional tagclass scope) + "Return the first tag found with NAME or nil if not found. +Optional argument TAGCLASS specifies the class of tag to return, such +as 'function or 'variable. +Optional argument SCOPE specifies a scope object which has +additional tags which are in SCOPE and do not need prefixing to +find. + +This is a wrapper on top of semanticdb, semanticdb-typecache, +semantic-scope, and semantic search functions. Almost all +searches use the same arguments." + (let ((namelst (if (consp name) name ;; test if pre-split. + (semantic-analyze-split-name name)))) + (cond + ;; If the splitter gives us a list, use the sequence finder + ;; to get the list. Since this routine is expected to return + ;; only one tag, return the LAST tag found from the sequence + ;; which is supposedly the nested reference. + ;; + ;; Of note, the SEQUENCE function below calls this function + ;; (recursively now) so the names that we get from the above + ;; fcn better not, in turn, be splittable. + ((listp namelst) + ;; If we had a split, then this is likely a c++ style namespace::name sequence, + ;; so take a short-cut through the typecache. + (or (semanticdb-typecache-find namelst) + ;; Ok, not there, try the usual... + (let ((seq (semantic-analyze-find-tag-sequence + namelst scope nil))) + (semantic-analyze-select-best-tag seq tagclass) + ))) + ;; If NAME is solo, then do our searches for it here. + ((stringp namelst) + (let ((retlist (and scope (semantic-scope-find name tagclass scope)))) + (if retlist + (semantic-analyze-select-best-tag + retlist tagclass) + (if (eq tagclass 'type) + (semanticdb-typecache-find name) + ;; Search in the typecache. First entries in a sequence are + ;; often there. + (setq retlist (semanticdb-typecache-find name)) + (if retlist + retlist + (semantic-analyze-select-best-tag + (semanticdb-strip-find-results + (semanticdb-find-tags-by-name name) + 'name) + tagclass) + ))))) + ))) + +;;; SHORT ANALYSIS +;; +;; Create a mini-analysis of just the symbol under point. +;; +(define-overloadable-function semantic-analyze-current-symbol + (analyzehookfcn &optional position) + "Call ANALYZEHOOKFCN after analyzing the symbol under POSITION. +The ANALYZEHOOKFCN is called with the current symbol bounds, and the +analyzed prefix. It should take the arguments (START END PREFIX). +The ANALYZEHOOKFCN is only called if some sort of prefix with bounds was +found under POSITION. + +The results of ANALYZEHOOKFCN is returned, or nil if there was nothing to +call it with. + +For regular analysis, you should call `semantic-analyze-current-context' +to calculate the context information. The purpose for this function is +to provide a large number of non-cached analysis for filtering symbols." + ;; Only do this in a Semantic enabled buffer. + (when (not (semantic-active-p)) + (error "Cannot analyze buffers not supported by Semantic.")) + ;; Always refresh out tags in a safe way before doing the + ;; context. + (semantic-refresh-tags-safe) + ;; Do the rest of the analysis. + (save-match-data + (save-excursion + (:override))) + ) + +(defun semantic-analyze-current-symbol-default (analyzehookfcn position) + "Call ANALYZEHOOKFCN on the analyzed symbol at POSITION." + (let* ((semantic-analyze-error-stack nil) + (LLstart (current-time)) + (prefixandbounds (semantic-ctxt-current-symbol-and-bounds (or position (point)))) + (prefix (car prefixandbounds)) + (bounds (nth 2 prefixandbounds)) + (scope (semantic-calculate-scope position)) + (end nil) + ) + ;; Only do work if we have bounds (meaning a prefix to complete) + (when bounds + + (if debug-on-error + (catch 'unfindable + ;; If debug on error is on, allow debugging in this fcn. + (setq prefix (semantic-analyze-find-tag-sequence + prefix scope 'prefixtypes 'unfindable))) + ;; Debug on error is off. Capture errors and move on + (condition-case err + ;; NOTE: This line is duplicated in + ;; semantic-analyzer-debug-global-symbol + ;; You will need to update both places. + (setq prefix (semantic-analyze-find-tag-sequence + prefix scope 'prefixtypes)) + (error (semantic-analyze-push-error err)))) + + (setq end (current-time)) + ;;(message "Analysis took %.2f sec" (semantic-elapsed-time LLstart end)) + + ) + (when prefix + (prog1 + (funcall analyzehookfcn (car bounds) (cdr bounds) prefix) + ;;(setq end (current-time)) + ;;(message "hookfcn took %.5f sec" (semantic-elapsed-time LLstart end)) + ) + + ))) + +;;; MAIN ANALYSIS +;; +;; Create a full-up context analysis. +;; +;;;###autoload +(define-overloadable-function semantic-analyze-current-context (&optional position) + "Analyze the current context at optional POSITION. +If called interactively, display interesting information about POSITION +in a separate buffer. +Returns an object based on symbol `semantic-analyze-context'. + +This function can be overriden with the symbol `analyze-context'. +When overriding this function, your override will be called while +cursor is at POSITION. In addition, your function will not be called +if a cached copy of the return object is found." + (interactive "d") + ;; Only do this in a Semantic enabled buffer. + (when (not (semantic-active-p)) + (error "Cannot analyze buffers not supported by Semantic.")) + ;; Always refresh out tags in a safe way before doing the + ;; context. + (semantic-refresh-tags-safe) + ;; Do the rest of the analysis. + (if (not position) (setq position (point))) + (save-excursion + (goto-char position) + (let* ((answer (semantic-get-cache-data 'current-context))) + (with-syntax-table semantic-lex-syntax-table + (when (not answer) + (setq answer (:override)) + (when (and answer (oref answer bounds)) + (with-slots (bounds) answer + (semantic-cache-data-to-buffer (current-buffer) + (car bounds) + (cdr bounds) + answer + 'current-context + 'exit-cache-zone))) + ;; Check for interactivity + (when (interactive-p) + (if answer + (semantic-analyze-pop-to-context answer) + (message "No Context.")) + )) + + answer)))) + +(defun semantic-analyze-current-context-default (position) + "Analyze the current context at POSITION. +Returns an object based on symbol `semantic-analyze-context'." + (let* ((semantic-analyze-error-stack nil) + (context-return nil) + (prefixandbounds (semantic-ctxt-current-symbol-and-bounds (or position (point)))) + (prefix (car prefixandbounds)) + (bounds (nth 2 prefixandbounds)) + ;; @todo - vv too early to really know this answer! vv + (prefixclass (semantic-ctxt-current-class-list)) + (prefixtypes nil) + (scope (semantic-calculate-scope position)) + (function nil) + (fntag nil) + arg fntagend argtag + assign asstag + ) + + ;; Pattern for Analysis: + ;; + ;; Step 1: Calculate DataTypes in Scope: + ;; + ;; a) Calculate the scope (above) + ;; + ;; Step 2: Parse context + ;; + ;; a) Identify function being called, or variable assignment, + ;; and find source tags for those references + ;; b) Identify the prefix (text cursor is on) and find the source + ;; tags for those references. + ;; + ;; Step 3: Assemble an object + ;; + + ;; Step 2 a: + + (setq function (semantic-ctxt-current-function)) + + (when function + ;; Calculate the argument for the function if there is one. + (setq arg (semantic-ctxt-current-argument)) + + ;; Find a tag related to the function name. + (condition-case err + (setq fntag + (semantic-analyze-find-tag-sequence function scope)) + (error (semantic-analyze-push-error err))) + + ;; fntag can have the last entry as just a string, meaning we + ;; could not find the core datatype. In this case, the searches + ;; below will not work. + (when (stringp (car (last fntag))) + ;; Take a wild guess! + (setcar (last fntag) (semantic-tag (car (last fntag)) 'function)) + ) + + (when fntag + (let ((fcn (semantic-find-tags-by-class 'function fntag))) + (when (not fcn) + (let ((ty (semantic-find-tags-by-class 'type fntag))) + (when ty + ;; We might have a constructor with the same name as + ;; the found datatype. + (setq fcn (semantic-find-tags-by-name + (semantic-tag-name (car ty)) + (semantic-tag-type-members (car ty)))) + (if fcn + (let ((lp fcn)) + (while lp + (when (semantic-tag-get-attribute (car lp) + :constructor) + (setq fcn (cons (car lp) fcn))) + (setq lp (cdr lp)))) + ;; Give up, go old school + (setq fcn fntag)) + ))) + (setq fntagend (car (reverse fcn)) + argtag + (when (semantic-tag-p fntagend) + (nth (1- arg) (semantic-tag-function-arguments fntagend))) + fntag fcn)))) + + ;; Step 2 b: + + ;; Only do work if we have bounds (meaning a prefix to complete) + (when bounds + + (if debug-on-error + (catch 'unfindable + ;; If debug on error is on, allow debugging in this fcn. + (setq prefix (semantic-analyze-find-tag-sequence + prefix scope 'prefixtypes 'unfindable))) + ;; Debug on error is off. Capture errors and move on + (condition-case err + ;; NOTE: This line is duplicated in + ;; semantic-analyzer-debug-global-symbol + ;; You will need to update both places. + (setq prefix (semantic-analyze-find-tag-sequence + prefix scope 'prefixtypes)) + (error (semantic-analyze-push-error err)))) + ) + + ;; Step 3: + + (cond + (fntag + ;; If we found a tag for our function, we can go into + ;; functional context analysis mode, meaning we have a type + ;; for the argument. + (setq context-return + (semantic-analyze-context-functionarg + "functionargument" + :buffer (current-buffer) + :function fntag + :index arg + :argument (list argtag) + :scope scope + :prefix prefix + :prefixclass prefixclass + :bounds bounds + :prefixtypes prefixtypes + :errors semantic-analyze-error-stack))) + + ;; No function, try assignment + ((and (setq assign (semantic-ctxt-current-assignment)) + ;; We have some sort of an assignment + (condition-case err + (setq asstag (semantic-analyze-find-tag-sequence + assign scope)) + (error (semantic-analyze-push-error err) + nil))) + + (setq context-return + (semantic-analyze-context-assignment + "assignment" + :buffer (current-buffer) + :assignee asstag + :scope scope + :bounds bounds + :prefix prefix + :prefixclass prefixclass + :prefixtypes prefixtypes + :errors semantic-analyze-error-stack))) + + ;; TODO: Identify return value condition. + ;;((setq return .... what to do?) + ;; ...) + + (bounds + ;; Nothing in particular + (setq context-return + (semantic-analyze-context + "context" + :buffer (current-buffer) + :scope scope + :bounds bounds + :prefix prefix + :prefixclass prefixclass + :prefixtypes prefixtypes + :errors semantic-analyze-error-stack))) + + (t (setq context-return nil)) + ) + + ;; Return our context. + context-return)) + + +(defun semantic-adebug-analyze (&optional ctxt) + "Perform `semantic-analyze-current-context'. +Display the results as a debug list. +Optional argument CTXT is the context to show." + (interactive) + (require 'data-debug) + (let ((start (current-time)) + (ctxt (or ctxt (semantic-analyze-current-context))) + (end (current-time))) + (if (not ctxt) + (message "No Analyzer Results") + (message "Analysis took %.2f seconds." + (semantic-elapsed-time start end)) + (semantic-analyze-pulse ctxt) + (if ctxt + (progn + (data-debug-new-buffer "*Analyzer ADEBUG*") + (data-debug-insert-object-slots ctxt "]")) + (message "No Context to analyze here."))))) + + +;;; DEBUG OUTPUT +;; +;; Friendly output of a context analysis. +;; +(declare-function pulse-momentary-highlight-region "pulse") + +(defmethod semantic-analyze-pulse ((context semantic-analyze-context)) + "Pulse the region that CONTEXT affects." + (require 'pulse) + (save-excursion + (set-buffer (oref context :buffer)) + (let ((bounds (oref context :bounds))) + (when bounds + (pulse-momentary-highlight-region (car bounds) (cdr bounds)))))) + +(defcustom semantic-analyze-summary-function 'semantic-format-tag-prototype + "Function to use when creating items in Imenu. +Some useful functions are found in `semantic-format-tag-functions'." + :group 'semantic + :type semantic-format-tag-custom-list) + +(defun semantic-analyze-princ-sequence (sequence &optional prefix buff) + "Send the tag SEQUENCE to standard out. +Use PREFIX as a label. +Use BUFF as a source of override methods." + (while sequence + (princ prefix) + (cond + ((semantic-tag-p (car sequence)) + (princ (funcall semantic-analyze-summary-function + (car sequence)))) + ((stringp (car sequence)) + (princ "\"") + (princ (semantic--format-colorize-text (car sequence) 'variable)) + (princ "\"")) + (t + (princ (format "'%S" (car sequence))))) + (princ "\n") + (setq sequence (cdr sequence)) + (setq prefix (make-string (length prefix) ? )) + )) + +(defmethod semantic-analyze-show ((context semantic-analyze-context)) + "Insert CONTEXT into the current buffer in a nice way." + (semantic-analyze-princ-sequence (oref context prefix) "Prefix: " ) + (semantic-analyze-princ-sequence (oref context prefixclass) "Prefix Classes: ") + (semantic-analyze-princ-sequence (oref context prefixtypes) "Prefix Types: ") + (semantic-analyze-princ-sequence (oref context errors) "Encountered Errors: ") + (princ "--------\n") + ;(semantic-analyze-princ-sequence (oref context scopetypes) "Scope Types: ") + ;(semantic-analyze-princ-sequence (oref context scope) "Scope: ") + ;(semantic-analyze-princ-sequence (oref context localvariables) "LocalVars: ") + (when (oref context scope) + (semantic-analyze-show (oref context scope))) + ) + +(defmethod semantic-analyze-show ((context semantic-analyze-context-assignment)) + "Insert CONTEXT into the current buffer in a nice way." + (semantic-analyze-princ-sequence (oref context assignee) "Assignee: ") + (call-next-method)) + +(defmethod semantic-analyze-show ((context semantic-analyze-context-functionarg)) + "Insert CONTEXT into the current buffer in a nice way." + (semantic-analyze-princ-sequence (oref context function) "Function: ") + (princ "Argument Index: ") + (princ (oref context index)) + (princ "\n") + (semantic-analyze-princ-sequence (oref context argument) "Argument: ") + (call-next-method)) + +(defun semantic-analyze-pop-to-context (context) + "Display CONTEXT in a temporary buffer. +CONTEXT's content is described in `semantic-analyze-current-context'." + (semantic-analyze-pulse context) + (with-output-to-temp-buffer "*Semantic Context Analysis*" + (princ "Context Type: ") + (princ (object-name context)) + (princ "\n") + (princ "Bounds: ") + (princ (oref context bounds)) + (princ "\n") + (semantic-analyze-show context) + ) + (shrink-window-if-larger-than-buffer + (get-buffer-window "*Semantic Context Analysis*")) + ) + +(provide 'semantic/analyze) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/analyze" +;; End: + +;;; semantic/analyze.el ends here diff --git a/lisp/cedet/semantic/analyze/complete.el b/lisp/cedet/semantic/analyze/complete.el new file mode 100644 index 00000000000..5d858e59949 --- /dev/null +++ b/lisp/cedet/semantic/analyze/complete.el @@ -0,0 +1,263 @@ +;;; semantic/analyze/complete.el --- Smart Completions + +;;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Caclulate smart completions. +;; +;; Uses the analyzer context routine to determine the best possible +;; list of completions. +;; +;;; History: +;; +;; Code was moved here from semantic-analyze.el + +(require 'semantic/analyze) + +;; For semantic-find-* macros: +(eval-when-compile (require 'semantic/find)) + +;;; Code: + +;;; Helper Fcns +;; +;; +;;;###autoload +(define-overloadable-function semantic-analyze-type-constants (type) + "For the tag TYPE, return any constant symbols of TYPE. +Used as options when completing.") + +(defun semantic-analyze-type-constants-default (type) + "Do nothing with TYPE." + nil) + +(defun semantic-analyze-tags-of-class-list (tags classlist) + "Return the tags in TAGS that are of classes in CLASSLIST." + (let ((origc tags)) + ;; Accept only tags that are of the datatype specified by + ;; the desired classes. + (setq tags (apply 'nconc ;; All input lists are permutable. + (mapcar (lambda (class) + (semantic-find-tags-by-class class origc)) + classlist))) + tags)) + +;;; MAIN completion calculator +;; +;;;###autoload +(define-overloadable-function semantic-analyze-possible-completions (context) + "Return a list of semantic tags which are possible completions. +CONTEXT is either a position (such as point), or a precalculated +context. Passing in a context is useful if the caller also needs +to access parts of the analysis. +Completions run through the following filters: + * Elements currently in scope + * Constants currently in scope + * Elements match the :prefix in the CONTEXT. + * Type of the completion matches the type of the context. +Context type matching can identify the following: + * No specific type + * Assignment into a variable of some type. + * Argument to a function with type constraints. +When called interactively, displays the list of possible completions +in a buffer." + (interactive "d") + ;; In theory, we don't need the below since the context will + ;; do it for us. + ;;(semantic-refresh-tags-safe) + (with-syntax-table semantic-lex-syntax-table + (let* ((context (if (semantic-analyze-context-child-p context) + context + (semantic-analyze-current-context context))) + (ans (if (not context) + (error "Nothing to Complete.") + (:override)))) + ;; If interactive, display them. + (when (interactive-p) + (with-output-to-temp-buffer "*Possible Completions*" + (semantic-analyze-princ-sequence ans "" (current-buffer))) + (shrink-window-if-larger-than-buffer + (get-buffer-window "*Possible Completions*"))) + ans))) + +(defun semantic-analyze-possible-completions-default (context) + "Default method for producing smart completions. +Argument CONTEXT is an object specifying the locally derived context." + (let* ((a context) + (desired-type (semantic-analyze-type-constraint a)) + (desired-class (oref a prefixclass)) + (prefix (oref a prefix)) + (prefixtypes (oref a prefixtypes)) + (completetext nil) + (completetexttype nil) + (scope (oref a scope)) + (localvar (oref scope localvar)) + (c nil)) + + ;; Calculate what our prefix string is so that we can + ;; find all our matching text. + (setq completetext (car (reverse prefix))) + (if (semantic-tag-p completetext) + (setq completetext (semantic-tag-name completetext))) + + (if (and (not completetext) (not desired-type)) + (error "Nothing to complete")) + + (if (not completetext) (setq completetext "")) + + ;; This better be a reasonable type, or we should fry it. + ;; The prefixtypes should always be at least 1 less than + ;; the prefix since the type is never looked up for the last + ;; item when calculating a sequence. + (setq completetexttype (car (reverse prefixtypes))) + (when (or (not completetexttype) + (not (and (semantic-tag-p completetexttype) + (eq (semantic-tag-class completetexttype) 'type)))) + ;; What should I do here? I think this is an error condition. + (setq completetexttype nil) + ;; If we had something that was a completetexttype but it wasn't + ;; valid, then express our dismay! + (when (> (length prefix) 1) + (let* ((errprefix (car (cdr (reverse prefix))))) + (error "Cannot find types for `%s'" + (cond ((semantic-tag-p errprefix) + (semantic-format-tag-prototype errprefix)) + (t + (format "%S" errprefix))))) + )) + + ;; There are many places to get our completion stream for. + ;; Here we go. + (if completetexttype + + (setq c (semantic-find-tags-for-completion + completetext + (semantic-analyze-scoped-type-parts completetexttype scope) + )) + + ;; No type based on the completetext. This is a free-range + ;; var or function. We need to expand our search beyond this + ;; scope into semanticdb, etc. + (setq c (nconc + ;; Argument list and local variables + (semantic-find-tags-for-completion completetext localvar) + ;; The current scope + (semantic-find-tags-for-completion completetext (oref scope fullscope)) + ;; The world + (semantic-analyze-find-tags-by-prefix completetext)) + ) + ) + + (let ((origc c) + (dtname (semantic-tag-name desired-type))) + + ;; Reset c. + (setq c nil) + + ;; Loop over all the found matches, and catagorize them + ;; as being possible features. + (while origc + + (cond + ;; Strip operators + ((semantic-tag-get-attribute (car origc) :operator-flag) + nil + ) + + ;; If we are completing from within some prefix, + ;; then we want to exclude constructors and destructors + ((and completetexttype + (or (semantic-tag-get-attribute (car origc) :constructor-flag) + (semantic-tag-get-attribute (car origc) :destructor-flag))) + nil + ) + + ;; If there is a desired type, we need a pair of restrictions + (desired-type + + (cond + ;; Ok, we now have a completion list based on the text we found + ;; we want to complete on. Now filter that stream against the + ;; type we want to search for. + ((string= dtname (semantic-analyze-type-to-name (semantic-tag-type (car origc)))) + (setq c (cons (car origc) c)) + ) + + ;; Now anything that is a compound type which could contain + ;; additional things which are of the desired type + ((semantic-tag-type (car origc)) + (let ((att (semantic-analyze-tag-type (car origc) scope)) + ) + (if (and att (semantic-tag-type-members att)) + (setq c (cons (car origc) c)))) + ) + + ) ; cond + ); desired type + + ;; No desired type, no other restrictions. Just add. + (t + (setq c (cons (car origc) c))) + + ); cond + + (setq origc (cdr origc))) + + (when desired-type + ;; Some types, like the enum in C, have special constant values that + ;; we could complete with. Thus, if the target is an enum, we can + ;; find possible symbol values to fill in that value. + (let ((constants + (semantic-analyze-type-constants desired-type))) + (if constants + (progn + ;; Filter + (setq constants + (semantic-find-tags-for-completion + completetext constants)) + ;; Add to the list + (setq c (nconc c constants))) + ))) + ) + + (when desired-class + (setq c (semantic-analyze-tags-of-class-list c desired-class))) + + ;; Pull out trash. + ;; NOTE TO SELF: Is this too slow? + ;; OTHER NOTE: Do we not want to strip duplicates by name and + ;; only by position? When are duplicate by name but not by tag + ;; useful? + (setq c (semantic-unique-tag-table-by-name c)) + + ;; All done! + + c)) + +(provide 'semantic/analyze/complete) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/analyze/complete" +;; End: + +;;; semantic/analyze/complete.el ends here diff --git a/lisp/cedet/semantic/analyze/debug.el b/lisp/cedet/semantic/analyze/debug.el new file mode 100644 index 00000000000..e482f074b31 --- /dev/null +++ b/lisp/cedet/semantic/analyze/debug.el @@ -0,0 +1,624 @@ +;;; semantic/analyze/debug.el --- Debug the analyzer + +;;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Provide a top-order debugging tool for figuring out what's going on with +;; smart completion and analyzer mode. + +(require 'semantic) +(require 'semantic/analyze) +(require 'semantic/analyze/complete) +(require 'semantic/db-typecache) + +;; For semantic-find-tags-by-class: +(eval-when-compile (require 'semantic/find)) + +(declare-function ede-get-locator-object "ede/files") + +;;; Code: + +(defun semantic-analyze-debug-assist () + "Debug semantic analysis at the current point." + (interactive) + (let ((actualfcn (fetch-overload 'semantic-analyze-current-context)) + (ctxt (semantic-analyze-current-context)) + ) + ;; What to show. + (if actualfcn + (message "Mode %s does not use the default analyzer." + major-mode) + ;; Debug our context. + ) + (or (semantic-analyzer-debug-test-local-context) + (and ctxt (semantic-analyzer-debug-found-prefix ctxt)) + ) + + )) + +(defun semantic-analyzer-debug-found-prefix (ctxt) + "Debug the prefix found by the analyzer output CTXT." + (let* ((pf (oref ctxt prefix)) + (pft (oref ctxt prefixtypes)) + (idx 0) + (stop nil) + (comp (condition-case nil + (semantic-analyze-possible-completions ctxt) + (error nil))) + ) + (while (and (nth idx pf) (not stop)) + (let ((pentry (nth idx pf)) + (ptentry (nth idx pft))) + (if (or (stringp pentry) (not ptentry)) + ;; Found someting ok. stop + (setq stop t) + (setq idx (1+ idx))))) + ;; We found the first non-tag entry. What is the situation? + (cond + ((and (eq idx 0) (stringp (car pf))) + ;; First part, we couldn't find it. + (semantic-analyzer-debug-global-symbol ctxt (car pf) comp)) + ((not (nth (1- idx) pft)) ;; idx can't be 0 here. + ;; The previous entry failed to have an identifiable data + ;; type, which is a global search. + (semantic-analyzer-debug-missing-datatype ctxt idx comp)) + ((and (nth (1- idx) pft) (stringp (nth idx pf))) + ;; Non-first search, didn't find string in known data type. + (semantic-analyzer-debug-missing-innertype ctxt idx comp)) + (t + ;; Things are ok? + (message "Things look ok.")) + ))) + +(defun semantic-analyzer-debug-global-symbol (ctxt prefix comp) + "Debug why we can't find the first entry in the CTXT PREFIX. +Argument COMP are possible completions here." + (let ((tab semanticdb-current-table) + (finderr nil) + (origbuf (current-buffer)) + ) + (with-output-to-temp-buffer (help-buffer) + (with-current-buffer standard-output + (princ "Unable to find prefix ") + (princ prefix) + (princ ".\n\n") + + ;; NOTE: This line is copied from semantic-analyze-current-context. + ;; You will need to update both places. + (condition-case err + (save-excursion + (set-buffer origbuf) + (let* ((position (or (cdr-safe (oref ctxt bounds)) (point))) + (prefixtypes nil) ; Used as type return + (scope (semantic-calculate-scope position)) + ) + (semantic-analyze-find-tag-sequence + (list prefix "") scope 'prefixtypes) + ) + ) + (error (setq finderr err))) + + (if finderr + (progn + (princ "The prefix lookup code threw the following error:\n ") + (prin1 finderr) + (princ "\n\nTo debug this error you can do this: + M-x toggle-debug-on-error RET +and then re-run the debug analyzer.\n") + ) + ;; No find error, just not found + (princ "The prefix ") + (princ prefix) + (princ " could not be found in the local scope, +nor in any search tables.\n") + ) + (princ "\n") + + ;; Describe local scope, and why we might not be able to + ;; find it. + (semantic-analyzer-debug-describe-scope ctxt) + + (semantic-analyzer-debug-show-completions comp) + + (princ "When Semantic cannot find a symbol, it could be because the include +path was setup incorrectly.\n") + + (semantic-analyzer-debug-insert-include-summary tab) + + )) + (semantic-analyzer-debug-add-buttons) + )) + +(defun semantic-analyzer-debug-missing-datatype (ctxt idx comp) + "Debug why we can't find a datatype entry for CTXT prefix at IDX. +Argument COMP are possible completions here." + (let* ((prefixitem (nth idx (oref ctxt prefix))) + (dt (nth (1- idx) (oref ctxt prefixtypes))) + (tt (semantic-tag-type prefixitem)) + (tab semanticdb-current-table) + ) + (when dt (error "Missing Datatype debugger is confused")) + (with-output-to-temp-buffer (help-buffer) + (with-current-buffer standard-output + (princ "Unable to find datatype for: \"") + (princ (semantic-format-tag-prototype prefixitem)) + (princ "\". +Declared type is: ") + (when (semantic-tag-p tt) + (semantic-analyzer-debug-insert-tag tt) + (princ "\nRaw data type is: ")) + (princ (format "%S" tt)) + (princ " + +Semantic could not find this data type in any of its global tables. + +Semantic locates datatypes through either the local scope, or the global +typecache. +") + + ;; Describe local scope, and why we might not be able to + ;; find it. + (semantic-analyzer-debug-describe-scope ctxt '(type)) + + ;; Describe the typecache. + (princ "\nSemantic creates and maintains a type cache for each buffer. +If the type is a global type, then it should appear in they typecache. +To examine the typecache, type: + + M-x semanticdb-typecache-dump RET + +Current typecache Statistics:\n") + (princ (format " %4d types global in this file\n %4d types from includes.\n" + (length (semanticdb-typecache-file-tags tab)) + (length (semanticdb-typecache-include-tags tab)))) + + (princ "\nIf the datatype is not in the typecache, then your include +path may be incorrect. ") + + (semantic-analyzer-debug-insert-include-summary tab) + + ;; End with-buffer + )) + (semantic-analyzer-debug-add-buttons) + )) + +(defun semantic-analyzer-debug-missing-innertype (ctxt idx comp) + "Debug why we can't find an entry for CTXT prefix at IDX for known type. +We need to see if we have possible completions against the entry before +being too vocal about it. +Argument COMP are possible completions here." + (let* ((prefixitem (nth idx (oref ctxt prefix))) + (prevprefix (nth (1- idx) (oref ctxt prefix))) + (dt (nth (1- idx) (oref ctxt prefixtypes))) + (desired-type (semantic-analyze-type-constraint ctxt)) + (orig-buffer (current-buffer)) + (ots (semantic-analyze-tag-type prevprefix + (oref ctxt scope) + t ; Don't deref + )) + ) + (when (not dt) (error "Missing Innertype debugger is confused")) + (with-output-to-temp-buffer (help-buffer) + (with-current-buffer standard-output + (princ "Cannot find prefix \"") + (princ prefixitem) + (princ "\" in datatype: + ") + (semantic-analyzer-debug-insert-tag dt) + (princ "\n") + + (cond + ;; Any language with a namespace. + ((string= (semantic-tag-type dt) "namespace") + (princ "Semantic may not have found all possible namespaces with +the name ") + (princ (semantic-tag-name dt)) + (princ ". You can debug the entire typecache, including merged namespaces +with the command: + + M-x semanticdb-typecache-dump RET") + ) + + ;; @todo - external declarations?? + (nil + nil) + + ;; A generic explanation + (t + (princ "\nSemantic has found the datatype ") + (semantic-analyzer-debug-insert-tag dt) + (if (or (not (semantic-equivalent-tag-p ots dt)) + (not (save-excursion + (set-buffer orig-buffer) + (car (semantic-analyze-dereference-metatype + ots (oref ctxt scope)))))) + (let ((lasttype ots) + (nexttype (save-excursion + (set-buffer orig-buffer) + (car (semantic-analyze-dereference-metatype + ots (oref ctxt scope)))))) + (if (eq nexttype lasttype) + (princ "\n [ Debugger error trying to help with metatypes ]") + + (if (eq ots dt) + (princ "\nwhich is a metatype") + (princ "\nwhich is derived from metatype ") + (semantic-analyzer-debug-insert-tag lasttype))) + + (princ ".\nThe Metatype stack is:\n") + (princ " ") + (semantic-analyzer-debug-insert-tag lasttype) + (princ "\n") + (while (and nexttype + (not (eq nexttype lasttype))) + (princ " ") + (semantic-analyzer-debug-insert-tag nexttype) + (princ "\n") + (setq lasttype nexttype + nexttype + (save-excursion + (set-buffer orig-buffer) + (car (semantic-analyze-dereference-metatype + nexttype (oref ctxt scope))))) + ) + (when (not nexttype) + (princ " nil\n\n") + (princ + "Last metatype is nil. This means that semantic cannot derive +the list of members because the type referred to cannot be found.\n") + ) + ) + (princ "\nand its list of members.") + + (if (not comp) + (progn + (princ " Semantic does not know what +possible completions there are for \"") + (princ prefixitem) + (princ "\". Examine the known +members below for more.")) + (princ " Semantic knows of some +possible completions for \"") + (princ prefixitem) + (princ "\"."))) + ) + ;; end cond + ) + + (princ "\n") + (semantic-analyzer-debug-show-completions comp) + + (princ "\nKnown members of ") + (princ (semantic-tag-name dt)) + (princ ":\n") + (dolist (M (semantic-tag-type-members dt)) + (princ " ") + ;;(princ (semantic-format-tag-prototype M)) + (semantic-analyzer-debug-insert-tag M) + (princ "\n")) + + ;; This doesn't refer to in-type completions. + ;;(semantic-analyzer-debug-global-miss-text prefixitem) + + ;; More explanation + (when desired-type + (princ "\nWhen there are known members that would make good completion +candidates that are not in the completion list, then the most likely +cause is a type constraint. Semantic has determined that there is a +type constraint looking for the type ") + (if (semantic-tag-p desired-type) + (semantic-analyzer-debug-insert-tag desired-type) + (princ (format "%S" desired-type))) + (princ ".")) + )) + (semantic-analyzer-debug-add-buttons) + + )) + + +(defun semantic-analyzer-debug-test-local-context () + "Test the local context parsed from the file." + (let* ((prefixandbounds (semantic-ctxt-current-symbol-and-bounds (point))) + (prefix (car prefixandbounds)) + (bounds (nth 2 prefixandbounds)) + ) + (when (and (or (not prefixandbounds) + (not prefix) + (not bounds)) + ) + (with-output-to-temp-buffer (help-buffer) + (with-current-buffer standard-output + (princ "Local Context Parser Failed. + +If this is unexpected, then there is likely a bug in the Semantic +local context parser. + +Consider debugging the function ") + (let ((lcf (fetch-overload 'semantic-ctxt-current-symbol-and-bounds))) + (if lcf + (princ (symbol-name lcf)) + (princ "semantic-ctxt-current-symbol-and-bounds, +or implementing a version specific to ") + (princ (symbol-name major-mode)) + ) + (princ ".\n")) + (semantic-analyzer-debug-add-buttons) + t))) + )) + +;;; General Inserters with help +;; +(defun semantic-analyzer-debug-show-completions (comp) + "Show the completion list COMP." + (if (not comp) + (princ "\nNo known possible completions.\n") + + (princ "\nPossible completions are:\n") + (dolist (C comp) + (princ " ") + (cond ((stringp C) + (princ C) + ) + ((semantic-tag-p C) + (semantic-analyzer-debug-insert-tag C))) + (princ "\n")) + (princ "\n"))) + +(defvar semantic-dependency-system-include-path) + +(defun semantic-analyzer-debug-insert-include-summary (table) + "Display a summary of includes for the semanticdb TABLE." + (require 'semantic/dep) + (semantic-fetch-tags) + (let ((inc (semantic-find-tags-by-class 'include table)) + ;;(path (semanticdb-find-test-translate-path-no-loading)) + (unk + (save-excursion + (set-buffer (semanticdb-get-buffer table)) + semanticdb-find-lost-includes)) + (ip + (save-excursion + (set-buffer (semanticdb-get-buffer table)) + semantic-dependency-system-include-path)) + (edeobj + (save-excursion + (set-buffer (semanticdb-get-buffer table)) + (and (boundp 'ede-object) + ede-object))) + (edeproj + (save-excursion + (set-buffer (semanticdb-get-buffer table)) + (and (boundp 'ede-object-project) + ede-object-project)))) + + (princ "\n\nInclude Path Summary:") + (when edeobj + (princ "\n\nThis file's project include search is handled by the EDE object:\n") + (princ " Buffer Target: ") + (princ (object-print edeobj)) + (princ "\n") + (when (not (eq edeobj edeproj)) + (princ " Buffer Project: ") + (princ (object-print edeproj)) + (princ "\n")) + (when edeproj + (let ((loc (ede-get-locator-object edeproj))) + (princ " Backup Locator: ") + (princ (object-print loc)) + (princ "\n"))) + ) + + (princ "\n\nThe system include path is:\n") + (dolist (dir ip) + (princ " ") + (princ dir) + (princ "\n")) + + (princ "\n\nInclude Summary: ") + (princ (semanticdb-full-filename table)) + (princ "\n\n") + (princ (format "%s contains %d includes.\n" + (file-name-nondirectory + (semanticdb-full-filename table)) + (length inc))) + (let ((ok 0) + (unknown 0) + (unparsed 0) + (all 0)) + (dolist (i inc) + (let* ((fileinner (semantic-dependency-tag-file i)) + (tableinner (when fileinner + (semanticdb-file-table-object fileinner t)))) + (cond ((not fileinner) + (setq unknown (1+ unknown))) + ((number-or-marker-p (oref tableinner pointmax)) + (setq ok (1+ ok))) + (t + (setq unparsed (1+ unparsed)))))) + (setq all (+ ok unknown unparsed)) + (when (not (= 0 all)) + (princ (format " Unknown Includes: %d\n" unknown)) + (princ (format " Unparsed Includes: %d\n" unparsed)) + (princ (format " Parsed Includes: %d\n" ok))) + ) + + ;; Unknowns... + (if unk + (progn + (princ "\nA likely cause of an unfound tag is missing include files.") + (semantic-analyzer-debug-insert-tag-list + "The following includes were not found" unk) + + (princ "\nYou can fix the include path for ") + (princ (symbol-name (oref table major-mode))) + (princ " by using this function: + +M-x semantic-customize-system-include-path RET + +which customizes the mode specific variable for the mode-local +variable `semantic-dependency-system-include-path'.") + ) + + (princ "\n No unknown includes.\n")) + )) + +(defun semantic-analyzer-debug-describe-scope (ctxt &optional classconstraint) + "Describe the scope in CTXT for finding a global symbol. +Optional argument CLASSCONSTRAINT says to output to tags of that class." + (let* ((scope (oref ctxt :scope)) + (parents (oref scope parents)) + (cc (or classconstraint (oref ctxt prefixclass))) + ) + (princ "\nLocal Scope Information:") + (princ "\n * Tag Class Constraint against SCOPE: ") + (princ (format "%S" classconstraint)) + + (if parents + (semantic-analyzer-debug-insert-tag-list + " >> Known parent types with possible in scope symbols" + parents) + (princ "\n * No known parents in current scope.")) + + (let ((si (semantic-analyze-tags-of-class-list + (oref scope scope) cc)) + (lv (semantic-analyze-tags-of-class-list + (oref scope localvar) cc)) + ) + (if si + (semantic-analyzer-debug-insert-tag-list + " >> Known symbols within the current scope" + si) + (princ "\n * No known symbols currently in scope.")) + + (if lv + (semantic-analyzer-debug-insert-tag-list + " >> Known symbols that are declared locally" + lv) + (princ "\n * No known symbols declared locally.")) + ) + ) + ) + +(defun semantic-analyzer-debug-global-miss-text (name-in) + "Use 'princ' to show text describing not finding symbol NAME-IN. +NAME is the name of the unfound symbol." + (let ((name (cond ((stringp name-in) + name-in) + ((semantic-tag-p name-in) + (semantic-format-tag-name name-in)) + (t (format "%S" name-in))))) + (when (not (string= name "")) + (princ "\nIf ") + (princ name) + (princ " is a local variable, argument, or symbol in some +namespace or class exposed via scoping statements, then it should +appear in the scope. + +Debugging the scope can be done with: + M-x semantic-calculate-scope RET + +If the prefix is a global symbol, in an included file, then +your search path may be incomplete. +")))) + +;;; Utils +;; +(defun semantic-analyzer-debug-insert-tag-list (text taglist) + "Prefixing with TEXT, dump TAGLIST in a help buffer." + (princ "\n") (princ text) (princ ":\n") + + (dolist (M taglist) + (princ " ") + ;;(princ (semantic-format-tag-prototype M)) + (semantic-analyzer-debug-insert-tag M) + (princ "\n")) + ) + +(defun semantic-analyzer-debug-insert-tag (tag &optional parent) + "Display a TAG by name, with possible jumpitude. +PARENT is a possible parent (by nesting) tag." + (let ((str (semantic-format-tag-prototype tag parent))) + (if (and (semantic-tag-with-position-p tag) + (semantic-tag-file-name tag)) + (insert-button str + 'mouse-face 'custom-button-pressed-face + 'tag tag + 'action + `(lambda (button) + (let ((buff nil) + (pnt nil)) + (save-excursion + (semantic-go-to-tag + (button-get button 'tag)) + (setq buff (current-buffer)) + (setq pnt (point))) + (if (get-buffer-window buff) + (select-window (get-buffer-window buff)) + (pop-to-buffer buff t)) + (goto-char pnt) + (pulse-line-hook-function))) + ) + (princ "\"") + (princ str) + (princ "\"")) + )) + +(defvar semantic-analyzer-debug-orig nil + "The originating buffer for a help button.") + +(defun semantic-analyzer-debug-add-buttons () + "Add push-buttons to the *Help* buffer. +Look for key expressions, and add push-buttons near them." + (let ((orig-buffer (make-marker))) + (set-marker orig-buffer (point) (current-buffer)) + (save-excursion + ;; Get a buffer ready. + (set-buffer "*Help*") + (toggle-read-only -1) + (goto-char (point-min)) + (set (make-local-variable 'semantic-analyzer-debug-orig) orig-buffer) + ;; First, add do-in buttons to recommendations. + (while (re-search-forward "^\\s-*M-x \\(\\(\\w\\|\\s_\\)+\\) " nil t) + (let ((fcn (match-string 1))) + (when (not (fboundp (intern-soft fcn))) + (error "Help Err: Can't find %s" fcn)) + (end-of-line) + (insert " ") + (insert-button "[ Do It ]" + 'mouse-face 'custom-button-pressed-face + 'do-fcn fcn + 'action `(lambda (arg) + (let ((M semantic-analyzer-debug-orig)) + (set-buffer (marker-buffer M)) + (goto-char M)) + (call-interactively (quote ,(intern-soft fcn)))) + ) + )) + ;; Do something else? + + ;; Clean up the mess + (toggle-read-only 1) + (set-buffer-modified-p nil) + ))) + +(provide 'semantic/analyze/debug) + +;;; semantic/analyze/debug.el ends here diff --git a/lisp/cedet/semantic/analyze/fcn.el b/lisp/cedet/semantic/analyze/fcn.el new file mode 100644 index 00000000000..c86a79a226d --- /dev/null +++ b/lisp/cedet/semantic/analyze/fcn.el @@ -0,0 +1,337 @@ +;;; semantic/analyze/fcn.el --- Analyzer support functions. + +;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Analyzer support functions. + +;;; Code: + +(require 'semantic) +(eval-when-compile (require 'semantic/find)) + +(declare-function semanticdb-typecache-merge-streams "semantic/db-typecache") +(declare-function semantic-scope-find name "semantic/scope") +(declare-function semantic-scope-set-typecache "semantic/scope") +(declare-function semantic-scope-tag-get-scope "semantic/scope") + +;;; Small Mode Specific Options +;; +;; These queries allow a major mode to help the analyzer make decisions. +;; +(define-overloadable-function semantic-analyze-tag-prototype-p (tag) + "Non-nil if TAG is a prototype." + ) + +(defun semantic-analyze-tag-prototype-p-default (tag) + "Non-nil if TAG is a prototype." + (let ((p (semantic-tag-get-attribute tag :prototype-flag))) + (cond + ;; Trust the parser author. + (p p) + ;; Empty types might be a prototype. + ((eq (semantic-tag-class tag) 'type) + (not (semantic-tag-type-members tag))) + ;; No other heuristics. + (t nil)) + )) + +;;------------------------------------------------------------ + +(define-overloadable-function semantic-analyze-split-name (name) + "Split a tag NAME into a sequence. +Sometimes NAMES are gathered from the parser that are compounded, +such as in C++ where foo::bar means: + \"The class BAR in the namespace FOO.\" +Return the string NAME for no change, or a list if it needs to be split.") + +(defun semantic-analyze-split-name-default (name) + "Don't split up NAME by default." + name) + +(define-overloadable-function semantic-analyze-unsplit-name (namelist) + "Assemble a NAMELIST into a string representing a compound name. +Return the string representing the compound name.") + +(defun semantic-analyze-unsplit-name-default (namelist) + "Concatenate the names in NAMELIST with a . between." + (mapconcat 'identity namelist ".")) + +;;; SELECTING +;; +;; If you narrow things down to a list of tags that all mean +;; the same thing, how to you pick one? Select or merge. +;; + +(defun semantic-analyze-select-best-tag (sequence &optional tagclass) + "For a SEQUENCE of tags, all with good names, pick the best one. +If SEQUENCE is made up of namespaces, merge the namespaces together. +If SEQUENCE has several prototypes, find the non-prototype. +If SEQUENCE has some items w/ no type information, find the one with a type. +If SEQUENCE is all prototypes, or has no prototypes, get the first one. +Optional TAGCLASS indicates to restrict the return to only +tags of TAGCLASS." + + ;; If there is a srew up and we get just one tag.. massage over it. + (when (semantic-tag-p sequence) + (setq sequence (list sequence))) + + ;; Filter out anything not of TAGCLASS + (when tagclass + (setq sequence (semantic-find-tags-by-class tagclass sequence))) + + (if (< (length sequence) 2) + ;; If the remaining sequence is 1 tag or less, just return it + ;; and skip the rest of this mumbo-jumbo. + (car sequence) + + ;; 1) + ;; This step will eliminate a vast majority of the types, + ;; in addition to merging namespaces together. + ;; + ;; 2) + ;; It will also remove prototypes. + (require 'semantic/db-typecache) + (setq sequence (semanticdb-typecache-merge-streams sequence nil)) + + (if (< (length sequence) 2) + ;; If the remaining sequence after the merge is 1 tag or less, + ;; just return it and skip the rest of this mumbo-jumbo. + (car sequence) + + (let ((best nil) + (notypeinfo nil) + ) + (while (and (not best) sequence) + + ;; 3) select a non-prototype. + (if (not (semantic-tag-type (car sequence))) + (setq notypeinfo (car sequence)) + + (setq best (car sequence)) + ) + + (setq sequence (cdr sequence))) + + ;; Select the best, or at least the prototype. + (or best notypeinfo))))) + +;;; Tag Finding +;; +;; Mechanism for lookup up tags by name. +;; +(defun semantic-analyze-find-tags-by-prefix (prefix) + ;; @todo - only used in semantic-complete. Find something better? + "Attempt to find a tag with PREFIX. +This is a wrapper on top of semanticdb, and semantic search functions. +Almost all searches use the same arguments." + (if (and (fboundp 'semanticdb-minor-mode-p) + (semanticdb-minor-mode-p)) + ;; Search the database & concatenate all matches together. + (semanticdb-strip-find-results + (semanticdb-find-tags-for-completion prefix) + 'name) + ;; Search just this file because there is no DB available. + (semantic-find-tags-for-completion + prefix (current-buffer)))) + +;;; Finding Datatypes +;; + +(define-overloadable-function semantic-analyze-dereference-metatype (type scope &optional type-declaration) + ;; todo - move into typecahe!! + "Return a concrete type tag based on input TYPE tag. +A concrete type is an actual declaration of a memory description, +such as a structure, or class. A meta type is an alias, +or a typedef in C or C++. If TYPE is concrete, it +is returned. If it is a meta type, it will return the concrete +type defined by TYPE. +The default behavior always returns TYPE. +Override functions need not return a real semantic tag. +Just a name, or short tag will be ok. It will be expanded here. +SCOPE is the scope object with additional items in which to search for names." + (catch 'default-behavior + (let* ((ans-tuple (:override + ;; Nothing fancy, just return type by default. + (throw 'default-behavior (list type type-declaration)))) + (ans-type (car ans-tuple)) + (ans-type-declaration (cadr ans-tuple))) + (list (semantic-analyze-dereference-metatype-1 ans-type scope) ans-type-declaration)))) + +;; Finding a data type by name within a project. +;; +(defun semantic-analyze-type-to-name (type) + "Get the name of TAG's type. +The TYPE field in a tag can be nil (return nil) +or a string, or a non-positional tag." + (cond ((semantic-tag-p type) + (semantic-tag-name type)) + ((stringp type) + type) + ((listp type) + (car type)) + (t nil))) + +(defun semantic-analyze-tag-type (tag &optional scope nometaderef) + "Return the semantic tag for a type within the type of TAG. +TAG can be a variable, function or other type of tag. +The behavior of TAG's type is defined by `semantic-analyze-type'. +Optional SCOPE represents a calculated scope in which the +types might be found. This can be nil. +If NOMETADEREF, then do not dereference metatypes. This is +used by the analyzer debugger." + (semantic-analyze-type (semantic-tag-type tag) scope nometaderef)) + +(defun semantic-analyze-type (type-declaration &optional scope nometaderef) + "Return the semantic tag for TYPE-DECLARATION. +TAG can be a variable, function or other type of tag. +The type of tag (such as a class or struct) is a name. +Lookup this name in database, and return all slots/fields +within that types field. Also handles anonymous types. +Optional SCOPE represents a calculated scope in which the +types might be found. This can be nil. +If NOMETADEREF, then do not dereference metatypes. This is +used by the analyzer debugger." + (require 'semantic/scope) + (let ((name nil) + (typetag nil) + ) + + ;; Is it an anonymous type? + (if (and type-declaration + (semantic-tag-p type-declaration) + (semantic-tag-of-class-p type-declaration 'type) + (not (semantic-analyze-tag-prototype-p type-declaration)) + ) + ;; We have an anonymous type for TAG with children. + ;; Use this type directly. + (if nometaderef + type-declaration + (semantic-analyze-dereference-metatype-stack + type-declaration scope type-declaration)) + + ;; Not an anonymous type. Look up the name of this type + ;; elsewhere, and report back. + (setq name (semantic-analyze-type-to-name type-declaration)) + + (if (and name (not (string= name ""))) + (progn + ;; Find a type of that name in scope. + (setq typetag (and scope (semantic-scope-find name 'type scope))) + ;; If no typetag, try the typecache + (when (not typetag) + (setq typetag (semanticdb-typecache-find name)))) + + ;; No name to look stuff up with. + (error "Semantic tag %S has no type information" + (semantic-tag-name type-declaration))) + + ;; Handle lists of tags. + (when (and (consp typetag) (semantic-tag-p (car typetag))) + (setq typetag (semantic-analyze-select-best-tag typetag 'type)) + ) + + ;; We now have a tag associated with the type. We need to deref it. + ;; + ;; If we were asked not to (ie - debugger) push the typecache anyway. + (if nometaderef + typetag + (unwind-protect + (progn + (semantic-scope-set-typecache + scope (semantic-scope-tag-get-scope typetag)) + (semantic-analyze-dereference-metatype-stack typetag scope type-declaration) + ) + (semantic-scope-set-typecache scope nil) + ))))) + +(defun semantic-analyze-dereference-metatype-stack (type scope &optional type-declaration) + "Dereference metatypes repeatedly until we hit a real TYPE. +Uses `semantic-analyze-dereference-metatype'. +Argument SCOPE is the scope object with additional items in which to search. +Optional argument TYPE-DECLARATION is how TYPE was found referenced." + (let ((lasttype type) + (lasttypedeclaration type-declaration) + (nexttype (semantic-analyze-dereference-metatype type scope type-declaration)) + (idx 0)) + (catch 'metatype-recursion + (while (and nexttype (not (eq (car nexttype) lasttype))) + (setq lasttype (car nexttype) + lasttypedeclaration (cadr nexttype)) + (setq nexttype (semantic-analyze-dereference-metatype lasttype scope lasttypedeclaration)) + (setq idx (1+ idx)) + (when (> idx 20) (message "Possible metatype recursion for %S" + (semantic-tag-name lasttype)) + (throw 'metatype-recursion nil)) + )) + lasttype)) + +;; @ TODO - the typecache can also return a stack of scope names. + +(defun semantic-analyze-dereference-metatype-1 (ans scope) + "Do extra work after dereferencing a metatype. +ANS is the answer from the the language specific query. +SCOPE is the current scope." + (require 'semantic/scope) + ;; If ANS is a string, or if ANS is a short tag, we + ;; need to do some more work to look it up. + (if (stringp ans) + ;; The metatype is just a string... look it up. + (or (and scope (car-safe + ;; @todo - should this be `find the best one'? + (semantic-scope-find ans 'type scope))) + (let ((tcsans nil)) + (prog1 + (setq tcsans + (semanticdb-typecache-find ans)) + ;; While going through the metatype, if we have + ;; a scope, push our new cache in. + (when scope + (semantic-scope-set-typecache + scope (semantic-scope-tag-get-scope tcsans)) + )) + )) + (when (and (semantic-tag-p ans) + (eq (semantic-tag-class ans) 'type)) + ;; We have a tag. + (if (semantic-analyze-tag-prototype-p ans) + ;; It is a prototype.. find the real one. + (or (and scope + (car-safe + (semantic-scope-find (semantic-tag-name ans) + 'type scope))) + (let ((tcsans nil)) + (prog1 + (setq tcsans + (semanticdb-typecache-find (semantic-tag-name ans))) + ;; While going through the metatype, if we have + ;; a scope, push our new cache in. + (when scope + (semantic-scope-set-typecache + scope (semantic-scope-tag-get-scope tcsans)) + )))) + ;; We have a tag, and it is not a prototype. + ans)) + )) + +(provide 'semantic/analyze/fcn) + +;;; semantic/analyze/fcn.el ends here diff --git a/lisp/cedet/semantic/analyze/refs.el b/lisp/cedet/semantic/analyze/refs.el new file mode 100644 index 00000000000..8e2f2120d69 --- /dev/null +++ b/lisp/cedet/semantic/analyze/refs.el @@ -0,0 +1,332 @@ +;;; semantic/analyze/refs.el --- Analysis of the references between tags. + +;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Analyze the references between tags. +;; +;; The original purpose of these analysis is to provide a way to jump +;; between a prototype and implementation. +;; +;; Finding all prototype/impl matches is hard because you have to search +;; through the entire set of allowed databases to capture all possible +;; refs. The core analysis class stores basic starting point, and then +;; entire raw search data, which is expensive to calculate. +;; +;; Once the raw data is available, queries for impl, prototype, or +;; perhaps other things become cheap. + +(require 'semantic) +(require 'semantic/analyze) +(require 'semantic/db-find) +(eval-when-compile (require 'semantic/find)) + +(declare-function data-debug-new-buffer "data-debug") +(declare-function data-debug-insert-object-slots "eieio-datadebug") +(declare-function semantic-momentary-highlight-tag "semantic/decorate") + +;;; Code: +(defclass semantic-analyze-references () + ((tag :initarg :tag + :type semantic-tag + :documentation + "The starting TAG we are providing references analysis for.") + (tagdb :initarg :tagdb + :documentation + "The database that tag can be found in.") + (scope :initarg :scope + :documentation "A Scope object.") + (rawsearchdata :initarg :rawsearchdata + :documentation + "The raw search data for TAG's name across all databases.") + ;; Note: Should I cache queried data here? I expect that searching + ;; through rawsearchdata will be super-fast, so why bother? + ) + "Class containing data from a semantic analysis.") + +(define-overloadable-function semantic-analyze-tag-references (tag &optional db) + "Analyze the references for TAG. +Returns a class with information about TAG. + +Optional argument DB is a database. It will be used to help +locate TAG. + +Use `semantic-analyze-current-tag' to debug this fcn.") + +(defun semantic-analyze-tag-references-default (tag &optional db) + "Analyze the references for TAG. +Returns a class with information about TAG. + +Optional argument DB is a database. It will be used to help +locate TAG. + +Use `semantic-analyze-current-tag' to debug this fcn." + (when (not (semantic-tag-p tag)) (signal 'wrong-type-argument (list 'semantic-tag-p tag))) + (let ((allhits nil) + (scope nil) + ) + (save-excursion + (semantic-go-to-tag tag db) + (setq scope (semantic-calculate-scope)) + + (setq allhits (semantic--analyze-refs-full-lookup tag scope)) + + (semantic-analyze-references (semantic-tag-name tag) + :tag tag + :tagdb db + :scope scope + :rawsearchdata allhits) + ))) + +;;; METHODS +;; +;; These accessor methods will calculate the useful bits from the context, and cache values +;; into the context. +(defmethod semantic-analyze-refs-impl ((refs semantic-analyze-references) &optional in-buffer) + "Return the implementations derived in the reference analyzer REFS. +Optional argument IN-BUFFER indicates that the returned tag should be in an active buffer." + (let ((allhits (oref refs rawsearchdata)) + (impl nil) + ) + (semanticdb-find-result-mapc + (lambda (T DB) + "Examine T in the database DB, and sont it." + (let* ((ans (semanticdb-normalize-one-tag DB T)) + (aT (cdr ans)) + (aDB (car ans)) + ) + (when (not (semantic-tag-prototype-p aT)) + (when in-buffer (save-excursion (semantic-go-to-tag aT aDB))) + (push aT impl)))) + allhits) + impl)) + +(defmethod semantic-analyze-refs-proto ((refs semantic-analyze-references) &optional in-buffer) + "Return the prototypes derived in the reference analyzer REFS. +Optional argument IN-BUFFER indicates that the returned tag should be in an active buffer." + (let ((allhits (oref refs rawsearchdata)) + (proto nil)) + (semanticdb-find-result-mapc + (lambda (T DB) + "Examine T in the database DB, and sort it." + (let* ((ans (semanticdb-normalize-one-tag DB T)) + (aT (cdr ans)) + (aDB (car ans)) + ) + (when (semantic-tag-prototype-p aT) + (when in-buffer (save-excursion (semantic-go-to-tag aT aDB))) + (push aT proto)))) + allhits) + proto)) + +;;; LOOKUP +;; +(defun semantic--analyze-refs-full-lookup (tag scope) + "Perform a full lookup for all occurances of TAG in the current project. +TAG should be the tag currently under point. +PARENT is the list of tags that are parents to TAG by +containment, as opposed to reference." + (if (not (oref scope parents)) + ;; If this tag has some named parent, but is not + (semantic--analyze-refs-full-lookup-simple tag) + + ;; We have some sort of lineage we need to consider when we do + ;; our side lookup of tags. + (semantic--analyze-refs-full-lookup-with-parents tag scope) + )) + +(defun semantic--analyze-refs-find-child-in-find-results (find-results name class) + "Find in FIND-RESULT a tag NAME which is a child of a tag in FIND-RESULTS. +CLASS is the class of the tag that ought to be returned." + (let ((ans nil) + (subans nil)) + ;; Loop over each segment of the find results. + (dolist (FDB find-results) + (setq subans nil) + ;; Loop over each tag in the find results. + (dolist (T (cdr FDB)) + ;; For each tag, get the children. + (let* ((chil (semantic-tag-type-members T)) + (match (semantic-find-tags-by-name name chil))) + ;; Go over the matches, looking for matching tag class. + (dolist (M match) + (when (semantic-tag-of-class-p M class) + (push M subans))))) + ;; Store current matches into a new find results. + (when subans + (push (cons (car FDB) subans) ans)) + ) + ans)) + +(defun semantic--analyze-refs-find-tags-with-parent (find-results parents) + "Find in FIND-RESULTS all tags with PARNTS. +NAME is the name of the tag needing finding. +PARENTS is a list of names." + (let ((ans nil)) + (semanticdb-find-result-mapc + (lambda (tag db) + (let* ((p (semantic-tag-named-parent tag)) + (ps (when (stringp p) + (semantic-analyze-split-name p)))) + (when (stringp ps) (setq ps (list ps))) + (when (and ps (equal ps parents)) + ;; We could optimize this, but it seems unlikely. + (push (list db tag) ans)) + )) + find-results) + ans)) + +(defun semantic--analyze-refs-full-lookup-with-parents (tag scope) + "Perform a lookup for all occurances of TAG based on TAG's SCOPE. +TAG should be the tag currently under point." + (let* ((classmatch (semantic-tag-class tag)) + (plist (mapcar (lambda (T) (semantic-tag-name T)) (oref scope parents))) + ;; The first item in the parent list + (name (car plist)) + ;; Stuff from the simple list. + (simple (semantic--analyze-refs-full-lookup-simple tag t)) + ;; Find all hits for the first parent name. + (brute (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-by-name-method table name tags) + ) + nil nil t)) + ;; Prime the answer. + (answer (semantic--analyze-refs-find-tags-with-parent simple plist)) + ) + ;; First parent is already search to initialize "brute". + (setq plist (cdr plist)) + ;; Go through the list of parents, and try to find matches. + ;; As we cycle through plist, for each level look for NAME, + ;; and compare the named-parent, and also dive into the next item of + ;; plist. + (while (and plist brute) + + ;; Find direct matches + (let* ((direct (semantic--analyze-refs-find-child-in-find-results + brute (semantic-tag-name tag) classmatch)) + (pdirect (semantic--analyze-refs-find-tags-with-parent + direct plist))) + (setq answer (append pdirect answer))) + + ;; The next set of search items. + (setq brute (semantic--analyze-refs-find-child-in-find-results + brute (car plist) 'type)) + + (setq plist (cdr plist))) + + ;; Brute now has the children from the very last match. + (let* ((direct (semantic--analyze-refs-find-child-in-find-results + brute (semantic-tag-name tag) classmatch)) + ) + (setq answer (append direct answer))) + + answer)) + +(defun semantic--analyze-refs-full-lookup-simple (tag &optional noerror) + "Perform a simple lookup for occurances of TAG in the current project. +TAG should be the tag currently under point. +Optional NOERROR means don't throw errors on failure to find something. +This only compares the tag name, and does not infer any matches in namespaces, +or parts of some other data structure. +Only works for tags in the global namespace." + (let* ((name (semantic-tag-name tag)) + (brute (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-by-name-method table name tags) + ) + nil nil t)) + ) + + (when (and (not brute) (not noerror)) + ;; An error, because tag under point ought to be found. + (error "Cannot find any references to %s in wide search" name)) + + (let* ((classmatch (semantic-tag-class tag)) + (RES + (semanticdb-find-tags-collector + (lambda (table tags) + (semantic-find-tags-by-class classmatch tags) + ;; @todo - Add parent check also. + ) + brute nil))) + + (when (and (not RES) (not noerror)) + (error "Cannot find any definitions for %s in wide search" + (semantic-tag-name tag))) + + ;; Return the matching tags and databases. + RES))) + + +;;; USER COMMANDS +;; +;;;###autoload +(defun semantic-analyze-current-tag () + "Analyze the tag under point." + (interactive) + (let* ((tag (semantic-current-tag)) + (start (current-time)) + (sac (semantic-analyze-tag-references tag)) + (end (current-time)) + ) + (message "Analysis took %.2f seconds." (semantic-elapsed-time start end)) + (if sac + (progn + (require 'eieio-datadebug) + (data-debug-new-buffer "*Analyzer Reference ADEBUG*") + (data-debug-insert-object-slots sac "]")) + (message "No Context to analyze here.")))) + +;;;###autoload +(defun semantic-analyze-proto-impl-toggle () + "Toggle between the implementation, and a prototype of tag under point." + (interactive) + (require 'semantic/decorate) + (semantic-fetch-tags) + (let* ((tag (semantic-current-tag)) + (sar (if tag + (semantic-analyze-tag-references tag) + (error "Point must be in a declaration"))) + (target (if (semantic-tag-prototype-p tag) + (car (semantic-analyze-refs-impl sar t)) + (car (semantic-analyze-refs-proto sar t)))) + ) + + (when (not target) + (error "Could not find suitable %s" + (if (semantic-tag-prototype-p tag) "implementation" "prototype"))) + + (push-mark) + (semantic-go-to-tag target) + (switch-to-buffer (current-buffer)) + (semantic-momentary-highlight-tag target)) + ) + +(provide 'semantic/analyze/refs) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/analyze/refs" +;; End: + +;;; semantic/analyze/refs.el ends here diff --git a/lisp/cedet/semantic/bovine.el b/lisp/cedet/semantic/bovine.el new file mode 100644 index 00000000000..d11fc16e07c --- /dev/null +++ b/lisp/cedet/semantic/bovine.el @@ -0,0 +1,297 @@ +;;; semantic/bovine.el --- LL Parser/Analyzer core. + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Semantix 1.x uses an LL parser named the "bovinator". This parser +;; had several conveniences in it which made for parsing tags out of +;; languages with list characters easy. This parser lives on as one +;; of many available parsers for semantic the tool. +;; +;; This parser should be used when the language is simple, such as +;; makefiles or other data-declaritive langauges. + +;;; Code: +(require 'semantic) + +(declare-function semantic-create-bovine-debug-error-frame + "semantic/bovine/debug") +(declare-function semantic-bovine-debug-create-frame + "semantic/bovine/debug") +(declare-function semantic-debug-break "semantic/debug") + +;;; Variables +;; +(defvar semantic-bovinate-nonterminal-check-obarray nil + "Obarray of streams already parsed for nonterminal symbols. +Use this to detect infinite recursion during a parse.") +(make-variable-buffer-local 'semantic-bovinate-nonterminal-check-obarray) + + + +;; These are functions that can be called from within a bovine table. +;; Most of these have code auto-generated from other construct in the +;; bovine input grammar. +(defmacro semantic-lambda (&rest return-val) + "Create a lambda expression to return a list including RETURN-VAL. +The return list is a lambda expression to be used in a bovine table." + `(lambda (vals start end) + (append ,@return-val (list start end)))) + +;;; Semantic Bovination +;; +;; Take a semantic token stream, and convert it using the bovinator. +;; The bovinator takes a state table, and converts the token stream +;; into a new semantic stream defined by the bovination table. +;; +(defsubst semantic-bovinate-symbol-nonterminal-p (sym table) + "Return non-nil if SYM is in TABLE, indicating it is NONTERMINAL." + ;; sym is always a sym, so assq should be ok. + (if (assq sym table) t nil)) + +(defmacro semantic-bovinate-nonterminal-db-nt () + "Return the current nonterminal symbol. +Part of the grammar source debugger. Depends on the existing +environment of `semantic-bovinate-stream'." + `(if nt-stack + (car (aref (car nt-stack) 2)) + nonterminal)) + +(defun semantic-bovinate-nonterminal-check (stream nonterminal) + "Check if STREAM not already parsed for NONTERMINAL. +If so abort because an infinite recursive parse is suspected." + (or (vectorp semantic-bovinate-nonterminal-check-obarray) + (setq semantic-bovinate-nonterminal-check-obarray + (make-vector 13 nil))) + (let* ((nt (symbol-name nonterminal)) + (vs (symbol-value + (intern-soft + nt semantic-bovinate-nonterminal-check-obarray)))) + (if (memq stream vs) + ;; Always enter debugger to see the backtrace + (let ((debug-on-signal t) + (debug-on-error t)) + (setq semantic-bovinate-nonterminal-check-obarray nil) + (error "Infinite recursive parse suspected on %s" nt)) + (set (intern nt semantic-bovinate-nonterminal-check-obarray) + (cons stream vs))))) + +;;;###autoload +(defun semantic-bovinate-stream (stream &optional nonterminal) + "Bovinate STREAM, starting at the first NONTERMINAL rule. +Use `bovine-toplevel' if NONTERMINAL is not provided. +This is the core routine for converting a stream into a table. +Return the list (STREAM SEMANTIC-STREAM) where STREAM are those +elements of STREAM that have not been used. SEMANTIC-STREAM is the +list of semantic tokens found." + (if (not nonterminal) + (setq nonterminal 'bovine-toplevel)) + + ;; Try to detect infinite recursive parse when doing a full reparse. + (or semantic--buffer-cache + (semantic-bovinate-nonterminal-check stream nonterminal)) + + (let* ((table semantic--parse-table) + (matchlist (cdr (assq nonterminal table))) + (starting-stream stream) + (nt-loop t) ;non-terminal loop condition + nt-popup ;non-nil if return from nt recursion + nt-stack ;non-terminal recursion stack + s ;Temp Stream Tracker + lse ;Local Semantic Element + lte ;Local matchlist element + tev ;Matchlist entry values from buffer + val ;Value found in buffer. + cvl ;collected values list. + out ;Output + end ;End of match + result + ) + (condition-case debug-condition + (while nt-loop + (catch 'push-non-terminal + (setq nt-popup nil + end (semantic-lex-token-end (car stream))) + (while (or nt-loop nt-popup) + (setq nt-loop nil + out nil) + (while (or nt-popup matchlist) + (if nt-popup + ;; End of a non-terminal recursion + (setq nt-popup nil) + ;; New matching process + (setq s stream ;init s from stream. + cvl nil ;re-init the collected value list. + lte (car matchlist) ;Get the local matchlist entry. + ) + (if (or (byte-code-function-p (car lte)) + (listp (car lte))) + ;; In this case, we have an EMPTY match! Make + ;; stuff up. + (setq cvl (list nil)))) + + (while (and lte + (not (byte-code-function-p (car lte))) + (not (listp (car lte)))) + + ;; GRAMMAR SOURCE DEBUGGING! + (if (and (boundp 'semantic-debug-enabled) + semantic-debug-enabled) + (let* ((db-nt (semantic-bovinate-nonterminal-db-nt)) + (db-ml (cdr (assq db-nt table))) + (db-mlen (length db-ml)) + (db-midx (- db-mlen (length matchlist))) + (db-tlen (length (nth db-midx db-ml))) + (db-tidx (- db-tlen (length lte))) + (frame (progn + (require 'semantic/bovine/debug) + (semantic-bovine-debug-create-frame + db-nt db-midx db-tidx cvl (car s)))) + (cmd (semantic-debug-break frame)) + ) + (cond ((eq 'fail cmd) (setq lte '(trash 0 . 0))) + ((eq 'quit cmd) (signal 'quit "Abort")) + ((eq 'abort cmd) (error "Abort")) + ;; support more commands here. + + ))) + ;; END GRAMMAR SOURCE DEBUGGING! + + (cond + ;; We have a nonterminal symbol. Recurse inline. + ((setq nt-loop (assq (car lte) table)) + + (setq + ;; push state into the nt-stack + nt-stack (cons (vector matchlist cvl lte stream end + ) + nt-stack) + ;; new non-terminal matchlist + matchlist (cdr nt-loop) + ;; new non-terminal stream + stream s) + + (throw 'push-non-terminal t) + + ) + ;; Default case + (t + (setq lse (car s) ;Get the local stream element + s (cdr s)) ;update stream. + ;; Do the compare + (if (eq (car lte) (semantic-lex-token-class lse)) ;syntactic match + (let ((valdot (semantic-lex-token-bounds lse))) + (setq val (semantic-lex-token-text lse)) + (setq lte (cdr lte)) + (if (stringp (car lte)) + (progn + (setq tev (car lte) + lte (cdr lte)) + (if (string-match tev val) + (setq cvl (cons + (if (memq (semantic-lex-token-class lse) + '(comment semantic-list)) + valdot val) + cvl)) ;append this value + (setq lte nil cvl nil))) ;clear the entry (exit) + (setq cvl (cons + (if (memq (semantic-lex-token-class lse) + '(comment semantic-list)) + valdot val) cvl))) ;append unchecked value. + (setq end (semantic-lex-token-end lse)) + ) + (setq lte nil cvl nil)) ;No more matches, exit + ))) + (if (not cvl) ;lte=nil; there was no match. + (setq matchlist (cdr matchlist)) ;Move to next matchlist entry + (let ((start (semantic-lex-token-start (car stream)))) + (setq out (cond + ((car lte) + (funcall (car lte) ;call matchlist fn on values + (nreverse cvl) start end)) + ((and (= (length cvl) 1) + (listp (car cvl)) + (not (numberp (car (car cvl))))) + (append (car cvl) (list start end))) + (t + ;;(append (nreverse cvl) (list start end)))) + ;; MAYBE THE FOLLOWING NEEDS LESS CONS + ;; CELLS THAN THE ABOVE? + (nreverse (cons end (cons start cvl))))) + matchlist nil) ;;generate exit condition + (if (not end) + (setq out nil))) + ;; Nothin? + )) + (setq result + (if (eq s starting-stream) + (list (cdr s) nil) + (list s out))) + (if nt-stack + ;; pop previous state from the nt-stack + (let ((state (car nt-stack))) + + (setq nt-popup t + ;; pop actual parser state + matchlist (aref state 0) + cvl (aref state 1) + lte (aref state 2) + stream (aref state 3) + end (aref state 4) + ;; update the stack + nt-stack (cdr nt-stack)) + + (if out + (let ((len (length out)) + (strip (nreverse (cdr (cdr (reverse out)))))) + (setq end (nth (1- len) out) ;reset end to the end of exp + cvl (cons strip cvl) ;prepend value of exp + lte (cdr lte)) ;update the local table entry + ) + ;; No value means that we need to terminate this + ;; match. + (setq lte nil cvl nil)) ;No match, exit + ))))) + (error + ;; On error just move forward the stream of lexical tokens + (setq result (list (cdr starting-stream) nil)) + (when (and (boundp 'semantic-debug-enabled) + semantic-debug-enabled) + (require 'semantic/bovine/debug) + (let ((frame (semantic-create-bovine-debug-error-frame + debug-condition))) + (semantic-debug-break frame))))) + result)) + +;; Make it the default parser +;;;###autoload +(defalias 'semantic-parse-stream-default 'semantic-bovinate-stream) + +(provide 'semantic/bovine) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/bovine" +;; End: + +;;; semantic/bovine.el ends here diff --git a/lisp/cedet/semantic/bovine/c-by.el b/lisp/cedet/semantic/bovine/c-by.el new file mode 100644 index 00000000000..e6be8a6822e --- /dev/null +++ b/lisp/cedet/semantic/bovine/c-by.el @@ -0,0 +1,2196 @@ +;;; semantic/bovine/c-by.el --- Generated parser support file + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 2008, 2009 Free Software Foundation, Inc. + +;; 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: +;; +;; This file was generated from the grammar file semantic/bovine/c.by +;; in the CEDET repository. + +;;; Code: + +(require 'semantic/lex) +(eval-when-compile (require 'semantic/bovine)) + +(declare-function semantic-c-reconstitute-token "semantic/bovine/c") +(declare-function semantic-c-reconstitute-template "semantic/bovine/c") +(declare-function semantic-expand-c-tag "semantic/bovine/c") + +(defconst semantic-c-by--keyword-table + (semantic-lex-make-keyword-table + '(("extern" . EXTERN) + ("static" . STATIC) + ("const" . CONST) + ("volatile" . VOLATILE) + ("register" . REGISTER) + ("signed" . SIGNED) + ("unsigned" . UNSIGNED) + ("inline" . INLINE) + ("virtual" . VIRTUAL) + ("mutable" . MUTABLE) + ("struct" . STRUCT) + ("union" . UNION) + ("enum" . ENUM) + ("typedef" . TYPEDEF) + ("class" . CLASS) + ("typename" . TYPENAME) + ("namespace" . NAMESPACE) + ("using" . USING) + ("new" . NEW) + ("delete" . DELETE) + ("template" . TEMPLATE) + ("throw" . THROW) + ("reentrant" . REENTRANT) + ("try" . TRY) + ("catch" . CATCH) + ("operator" . OPERATOR) + ("public" . PUBLIC) + ("private" . PRIVATE) + ("protected" . PROTECTED) + ("friend" . FRIEND) + ("if" . IF) + ("else" . ELSE) + ("do" . DO) + ("while" . WHILE) + ("for" . FOR) + ("switch" . SWITCH) + ("case" . CASE) + ("default" . DEFAULT) + ("return" . RETURN) + ("break" . BREAK) + ("continue" . CONTINUE) + ("sizeof" . SIZEOF) + ("void" . VOID) + ("char" . CHAR) + ("wchar_t" . WCHAR) + ("short" . SHORT) + ("int" . INT) + ("long" . LONG) + ("float" . FLOAT) + ("double" . DOUBLE) + ("bool" . BOOL) + ("_P" . UNDERP) + ("__P" . UNDERUNDERP)) + '(("__P" summary "Common macro to eliminate prototype compatibility on some compilers") + ("_P" summary "Common macro to eliminate prototype compatibility on some compilers") + ("bool" summary "Primitive boolean type") + ("double" summary "Primitive floating-point type (double-precision 64-bit IEEE 754)") + ("float" summary "Primitive floating-point type (single-precision 32-bit IEEE 754)") + ("long" summary "Integral primitive type (-9223372036854775808 to 9223372036854775807)") + ("int" summary "Integral Primitive Type: (-2147483648 to 2147483647)") + ("short" summary "Integral Primitive Type: (-32768 to 32767)") + ("wchar_t" summary "Wide Character Type") + ("char" summary "Integral Character Type: (0 to 256)") + ("void" summary "Built in typeless type: void") + ("sizeof" summary "Compile time macro: sizeof(<type or variable>) // size in bytes") + ("continue" summary "Non-local continue within a loop (for, do/while): continue;") + ("break" summary "Non-local exit within a loop or switch (for, do/while, switch): break;") + ("return" summary "return <value>;") + ("default" summary "switch (<variable>) { case <constvalue>: code; ... default: code; }") + ("case" summary "switch (<variable>) { case <constvalue>: code; ... default: code; }") + ("switch" summary "switch (<variable>) { case <constvalue>: code; ... default: code; }") + ("for" summary "for(<init>; <condition>; <increment>) { code }") + ("while" summary "do { code } while (<condition>); or while (<condition>) { code };") + ("do" summary " do { code } while (<condition>);") + ("else" summary "if (<condition>) { code } [ else { code } ]") + ("if" summary "if (<condition>) { code } [ else { code } ]") + ("friend" summary "friend class <CLASSNAME>") + ("catch" summary "try { <body> } catch { <catch code> }") + ("try" summary "try { <body> } catch { <catch code> }") + ("reentrant" summary "<type> <methoddef> (<method args>) reentrant ...") + ("throw" summary "<type> <methoddef> (<method args>) throw (<exception>) ...") + ("template" summary "template <class TYPE ...> TYPE_OR_FUNCTION") + ("delete" summary "delete <object>;") + ("new" summary "new <classname>();") + ("using" summary "using <namespace>;") + ("namespace" summary "Namespace Declaration: namespace <name> { ... };") + ("typename" summary "typename is used to handle a qualified name as a typename;") + ("class" summary "Class Declaration: class <name>[:parents] { ... };") + ("typedef" summary "Arbitrary Type Declaration: typedef <typedeclaration> <name>;") + ("enum" summary "Enumeration Type Declaration: enum [name] { ... };") + ("union" summary "Union Type Declaration: union [name] { ... };") + ("struct" summary "Structure Type Declaration: struct [name] { ... };") + ("mutable" summary "Member Declaration Modifier: mutable <type> <name> ...") + ("virtual" summary "Method Modifier: virtual <type> <name>(...) ...") + ("inline" summary "Function Modifier: inline <return type> <name>(...) {...};") + ("unsigned" summary "Numeric Type Modifier: unsigned <numeric type> <name> ...") + ("signed" summary "Numeric Type Modifier: signed <numeric type> <name> ...") + ("register" summary "Declaration Modifier: register <type> <name> ...") + ("volatile" summary "Declaration Modifier: volatile <type> <name> ...") + ("const" summary "Declaration Modifier: const <type> <name> ...") + ("static" summary "Declaration Modifier: static <type> <name> ...") + ("extern" summary "Declaration Modifier: extern <type> <name> ..."))) + "Table of language keywords.") + +(defconst semantic-c-by--token-table + (semantic-lex-make-type-table + '(("semantic-list" + (BRACKETS . "\\[\\]") + (PARENS . "()") + (VOID_BLCK . "^(void)$") + (BRACE_BLCK . "^{") + (PAREN_BLCK . "^(") + (BRACK_BLCK . "\\[.*\\]$")) + ("close-paren" + (RBRACE . "}") + (RPAREN . ")")) + ("open-paren" + (LBRACE . "{") + (LPAREN . "(")) + ("symbol" + (RESTRICT . "\\<\\(__\\)?restrict\\>")) + ("number" + (ZERO . "^0$")) + ("string" + (CPP . "\"C\\+\\+\"") + (C . "\"C\"")) + ("punctuation" + (OR . "\\`[|]\\'") + (HAT . "\\`\\^\\'") + (MOD . "\\`[%]\\'") + (TILDE . "\\`[~]\\'") + (COMA . "\\`[,]\\'") + (GREATER . "\\`[>]\\'") + (LESS . "\\`[<]\\'") + (EQUAL . "\\`[=]\\'") + (BANG . "\\`[!]\\'") + (MINUS . "\\`[-]\\'") + (PLUS . "\\`[+]\\'") + (DIVIDE . "\\`[/]\\'") + (AMPERSAND . "\\`[&]\\'") + (STAR . "\\`[*]\\'") + (SEMICOLON . "\\`[;]\\'") + (COLON . "\\`[:]\\'") + (PERIOD . "\\`[.]\\'") + (HASH . "\\`[#]\\'"))) + 'nil) + "Table of lexical tokens.") + +(defconst semantic-c-by--parse-table + `( + (bovine-toplevel + (declaration) + ) ;; end bovine-toplevel + + (bovine-inner-scope + (codeblock) + ) ;; end bovine-inner-scope + + (declaration + (macro) + (type) + (define) + (var-or-fun) + (extern-c) + (template) + (using) + ) ;; end declaration + + (codeblock + (define) + (codeblock-var-or-fun) + (type) + (using) + ) ;; end codeblock + + (extern-c-contents + (open-paren + ,(semantic-lambda + (list nil)) + ) + (declaration) + (close-paren + ,(semantic-lambda + (list nil)) + ) + ) ;; end extern-c-contents + + (extern-c + (EXTERN + string + "\"C\"" + semantic-list + ,(semantic-lambda + (semantic-tag + "C" + 'extern :members + (semantic-parse-region + (car + (nth 2 vals)) + (cdr + (nth 2 vals)) + 'extern-c-contents + 1))) + ) + (EXTERN + string + "\"C\\+\\+\"" + semantic-list + ,(semantic-lambda + (semantic-tag + "C" + 'extern :members + (semantic-parse-region + (car + (nth 2 vals)) + (cdr + (nth 2 vals)) + 'extern-c-contents + 1))) + ) + (EXTERN + string + "\"C\"" + ,(semantic-lambda + (list nil)) + ) + (EXTERN + string + "\"C\\+\\+\"" + ,(semantic-lambda + (list nil)) + ) + ) ;; end extern-c + + (macro + (spp-macro-def + ,(semantic-lambda + (semantic-tag-new-variable + (nth 0 vals) nil nil :constant-flag t)) + ) + (spp-system-include + ,(semantic-lambda + (semantic-tag-new-include + (nth 0 vals) t)) + ) + (spp-include + ,(semantic-lambda + (semantic-tag-new-include + (nth 0 vals) nil)) + ) + ) ;; end macro + + (define + (spp-macro-def + ,(semantic-lambda + (semantic-tag-new-variable + (nth 0 vals) nil nil :constant-flag t)) + ) + (spp-macro-undef + ,(semantic-lambda + (list nil)) + ) + ) ;; end define + + (unionparts + (semantic-list + ,(semantic-lambda + (semantic-parse-region + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'classsubparts + 1)) + ) + ) ;; end unionparts + + (opt-symbol + (symbol) + ( ;;EMPTY + ) + ) ;; end opt-symbol + + (classsubparts + (open-paren + "{" + ,(semantic-lambda + (list nil)) + ) + (close-paren + "}" + ,(semantic-lambda + (list nil)) + ) + (class-protection + opt-symbol + punctuation + "\\`[:]\\'" + ,(semantic-lambda + (semantic-tag + (car + (nth 0 vals)) + 'label)) + ) + (var-or-fun) + (FRIEND + func-decl + ,(semantic-lambda + (semantic-tag + (car + (nth 1 vals)) + 'friend)) + ) + (FRIEND + CLASS + symbol + ,(semantic-lambda + (semantic-tag + (nth 2 vals) + 'friend)) + ) + (type) + (define) + (template) + ( ;;EMPTY + ) + ) ;; end classsubparts + + (opt-class-parents + (punctuation + "\\`[:]\\'" + class-parents + opt-template-specifier + ,(semantic-lambda + (list + (nth 1 vals))) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end opt-class-parents + + (one-class-parent + (opt-class-protection + opt-class-declmods + namespace-symbol + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 2 vals)) + "class" nil nil :protection + (car + (nth 0 vals)))) + ) + (opt-class-declmods + opt-class-protection + namespace-symbol + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 2 vals)) + "class" nil nil :protection + (car + (nth 1 vals)))) + ) + ) ;; end one-class-parent + + (class-parents + (one-class-parent + punctuation + "\\`[,]\\'" + class-parents + ,(semantic-lambda + (cons + (nth 0 vals) + (nth 2 vals))) + ) + (one-class-parent + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end class-parents + + (opt-class-declmods + (class-declmods + opt-class-declmods + ,(semantic-lambda + (list nil)) + ) + ( ;;EMPTY + ) + ) ;; end opt-class-declmods + + (class-declmods + (VIRTUAL) + ) ;; end class-declmods + + (class-protection + (PUBLIC) + (PRIVATE) + (PROTECTED) + ) ;; end class-protection + + (opt-class-protection + (class-protection + ,(semantic-lambda + (nth 0 vals)) + ) + ( ;;EMPTY + ,(semantic-lambda + (list + "unspecified")) + ) + ) ;; end opt-class-protection + + (namespaceparts + (semantic-list + ,(semantic-lambda + (semantic-parse-region + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'namespacesubparts + 1)) + ) + ) ;; end namespaceparts + + (namespacesubparts + (open-paren + "{" + ,(semantic-lambda + (list nil)) + ) + (close-paren + "}" + ,(semantic-lambda + (list nil)) + ) + (type) + (var-or-fun) + (define) + (class-protection + punctuation + "\\`[:]\\'" + ,(semantic-lambda + (semantic-tag + (car + (nth 0 vals)) + 'label)) + ) + (template) + (using) + ( ;;EMPTY + ) + ) ;; end namespacesubparts + + (enumparts + (semantic-list + ,(semantic-lambda + (semantic-parse-region + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'enumsubparts + 1)) + ) + ) ;; end enumparts + + (enumsubparts + (symbol + opt-assign + ,(semantic-lambda + (semantic-tag-new-variable + (nth 0 vals) + "int" + (car + (nth 1 vals)) :constant-flag t)) + ) + (open-paren + "{" + ,(semantic-lambda + (list nil)) + ) + (close-paren + "}" + ,(semantic-lambda + (list nil)) + ) + (punctuation + "\\`[,]\\'" + ,(semantic-lambda + (list nil)) + ) + ) ;; end enumsubparts + + (opt-name + (symbol) + ( ;;EMPTY + ,(semantic-lambda + (list + "")) + ) + ) ;; end opt-name + + (typesimple + (struct-or-class + opt-class + opt-name + opt-template-specifier + opt-class-parents + semantic-list + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 2 vals)) + (car + (nth 0 vals)) + (let + ( + (semantic-c-classname + (cons + (car + (nth 2 vals)) + (car + (nth 0 vals))))) + (semantic-parse-region + (car + (nth 5 vals)) + (cdr + (nth 5 vals)) + 'classsubparts + 1)) + (nth 4 vals) :template-specifier + (nth 3 vals) :parent + (car + (nth 1 vals)))) + ) + (struct-or-class + opt-class + opt-name + opt-template-specifier + opt-class-parents + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 2 vals)) + (car + (nth 0 vals)) nil + (nth 4 vals) :template-specifier + (nth 3 vals) :prototype t :parent + (car + (nth 1 vals)))) + ) + (UNION + opt-class + opt-name + unionparts + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 2 vals)) + (nth 0 vals) + (nth 3 vals) nil :parent + (car + (nth 1 vals)))) + ) + (ENUM + opt-class + opt-name + enumparts + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 2 vals)) + (nth 0 vals) + (nth 3 vals) nil :parent + (car + (nth 1 vals)))) + ) + (TYPEDEF + declmods + typeformbase + cv-declmods + typedef-symbol-list + ,(semantic-lambda + (semantic-tag-new-type + (nth 4 vals) + (nth 0 vals) nil + (list + (nth 2 vals)))) + ) + ) ;; end typesimple + + (typedef-symbol-list + (typedefname + punctuation + "\\`[,]\\'" + typedef-symbol-list + ,(semantic-lambda + (cons + (nth 0 vals) + (nth 2 vals))) + ) + (typedefname + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end typedef-symbol-list + + (typedefname + (opt-stars + symbol + opt-bits + opt-array + ,(semantic-lambda + (list + (nth 0 vals) + (nth 1 vals))) + ) + ) ;; end typedefname + + (struct-or-class + (STRUCT) + (CLASS) + ) ;; end struct-or-class + + (type + (typesimple + punctuation + "\\`[;]\\'" + ,(semantic-lambda + (nth 0 vals)) + ) + (NAMESPACE + symbol + namespaceparts + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + (nth 0 vals) + (nth 2 vals) nil)) + ) + (NAMESPACE + namespaceparts + ,(semantic-lambda + (semantic-tag-new-type + "unnamed" + (nth 0 vals) + (nth 1 vals) nil)) + ) + (NAMESPACE + symbol + punctuation + "\\`[=]\\'" + typeformbase + punctuation + "\\`[;]\\'" + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + (nth 0 vals) + (list + (semantic-tag-new-type + (car + (nth 3 vals)) + (nth 0 vals) nil nil)) nil :kind + 'alias)) + ) + ) ;; end type + + (using + (USING + usingname + punctuation + "\\`[;]\\'" + ,(semantic-lambda + (semantic-tag + (car + (nth 1 vals)) + 'using :type + (nth 1 vals))) + ) + ) ;; end using + + (usingname + (typeformbase + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 0 vals)) + "class" nil nil :prototype t)) + ) + (NAMESPACE + typeformbase + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 1 vals)) + "namespace" nil nil :prototype t)) + ) + ) ;; end usingname + + (template + (TEMPLATE + template-specifier + opt-friend + template-definition + ,(semantic-lambda + (semantic-c-reconstitute-template + (nth 3 vals) + (nth 1 vals))) + ) + ) ;; end template + + (opt-friend + (FRIEND) + ( ;;EMPTY + ) + ) ;; end opt-friend + + (opt-template-specifier + (template-specifier + ,(semantic-lambda + (nth 0 vals)) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end opt-template-specifier + + (template-specifier + (punctuation + "\\`[<]\\'" + template-specifier-types + punctuation + "\\`[>]\\'" + ,(semantic-lambda + (nth 1 vals)) + ) + ) ;; end template-specifier + + (template-specifier-types + (template-var + template-specifier-type-list + ,(semantic-lambda + (cons + (nth 0 vals) + (nth 1 vals))) + ) + ( ;;EMPTY + ) + ) ;; end template-specifier-types + + (template-specifier-type-list + (punctuation + "\\`[,]\\'" + template-specifier-types + ,(semantic-lambda + (nth 1 vals)) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end template-specifier-type-list + + (template-var + (template-type + opt-template-equal + ,(semantic-lambda + (cons + (car + (nth 0 vals)) + (cdr + (nth 0 vals)))) + ) + (string + ,(semantic-lambda + (list + (nth 0 vals))) + ) + (number + ,(semantic-lambda + (list + (nth 0 vals))) + ) + (opt-stars + opt-ref + namespace-symbol + ,(semantic-lambda + (nth 2 vals)) + ) + (semantic-list + ,(semantic-lambda + (list + (nth 0 vals))) + ) + (SIZEOF + semantic-list + ,(semantic-lambda + (list + (nth 1 vals))) + ) + ) ;; end template-var + + (opt-template-equal + (punctuation + "\\`[=]\\'" + symbol + punctuation + "\\`[<]\\'" + template-specifier-types + punctuation + "\\`[>]\\'" + ,(semantic-lambda + (list + (nth 1 vals))) + ) + (punctuation + "\\`[=]\\'" + symbol + ,(semantic-lambda + (list + (nth 1 vals))) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end opt-template-equal + + (template-type + (CLASS + symbol + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + "class" nil nil)) + ) + (STRUCT + symbol + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + "struct" nil nil)) + ) + (TYPENAME + symbol + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + "class" nil nil)) + ) + (declmods + typeformbase + cv-declmods + opt-stars + opt-ref + variablearg-opt-name + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 1 vals)) nil nil nil :constant-flag + (if + (member + "const" + (append + (nth 0 vals) + (nth 2 vals))) t nil) :typemodifiers + (delete + "const" + (append + (nth 0 vals) + (nth 2 vals))) :reference + (car + (nth 4 vals)) :pointer + (car + (nth 3 vals)))) + ) + ) ;; end template-type + + (template-definition + (type + ,(semantic-lambda + (nth 0 vals)) + ) + (var-or-fun + ,(semantic-lambda + (nth 0 vals)) + ) + ) ;; end template-definition + + (opt-stars + (punctuation + "\\`[*]\\'" + opt-starmod + opt-stars + ,(semantic-lambda + (list + (1+ + (car + (nth 2 vals))))) + ) + ( ;;EMPTY + ,(semantic-lambda + (list + 0)) + ) + ) ;; end opt-stars + + (opt-starmod + (STARMOD + opt-starmod + ,(semantic-lambda + (cons + (car + (nth 0 vals)) + (nth 1 vals))) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end opt-starmod + + (STARMOD + (CONST) + ) ;; end STARMOD + + (declmods + (DECLMOD + declmods + ,(semantic-lambda + (cons + (car + (nth 0 vals)) + (nth 1 vals))) + ) + (DECLMOD + ,(semantic-lambda + (nth 0 vals)) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end declmods + + (DECLMOD + (EXTERN) + (STATIC) + (CVDECLMOD) + (INLINE) + (REGISTER) + (FRIEND) + (TYPENAME) + (METADECLMOD) + (VIRTUAL) + ) ;; end DECLMOD + + (metadeclmod + (METADECLMOD + ,(semantic-lambda) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end metadeclmod + + (CVDECLMOD + (CONST) + (VOLATILE) + ) ;; end CVDECLMOD + + (cv-declmods + (CVDECLMOD + cv-declmods + ,(semantic-lambda + (cons + (car + (nth 0 vals)) + (nth 1 vals))) + ) + (CVDECLMOD + ,(semantic-lambda + (nth 0 vals)) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end cv-declmods + + (METADECLMOD + (VIRTUAL) + (MUTABLE) + ) ;; end METADECLMOD + + (opt-ref + (punctuation + "\\`[&]\\'" + ,(semantic-lambda + (list + 1)) + ) + ( ;;EMPTY + ,(semantic-lambda + (list + 0)) + ) + ) ;; end opt-ref + + (typeformbase + (typesimple + ,(semantic-lambda + (nth 0 vals)) + ) + (STRUCT + symbol + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + (nth 0 vals) nil nil)) + ) + (UNION + symbol + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + (nth 0 vals) nil nil)) + ) + (ENUM + symbol + ,(semantic-lambda + (semantic-tag-new-type + (nth 1 vals) + (nth 0 vals) nil nil)) + ) + (builtintype + ,(semantic-lambda + (nth 0 vals)) + ) + (symbol + template-specifier + ,(semantic-lambda + (semantic-tag-new-type + (nth 0 vals) + "class" nil nil :template-specifier + (nth 1 vals))) + ) + (namespace-symbol-for-typeformbase + opt-template-specifier + ,(semantic-lambda + (semantic-tag-new-type + (car + (nth 0 vals)) + "class" nil nil :template-specifier + (nth 1 vals))) + ) + (symbol + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end typeformbase + + (signedmod + (UNSIGNED) + (SIGNED) + ) ;; end signedmod + + (builtintype-types + (VOID) + (CHAR) + (WCHAR) + (SHORT + INT + ,(semantic-lambda + (list + (concat + (nth 0 vals) + " " + (nth 1 vals)))) + ) + (SHORT) + (INT) + (LONG + INT + ,(semantic-lambda + (list + (concat + (nth 0 vals) + " " + (nth 1 vals)))) + ) + (FLOAT) + (DOUBLE) + (BOOL) + (LONG + DOUBLE + ,(semantic-lambda + (list + (concat + (nth 0 vals) + " " + (nth 1 vals)))) + ) + (LONG + LONG + ,(semantic-lambda + (list + (concat + (nth 0 vals) + " " + (nth 1 vals)))) + ) + (LONG) + ) ;; end builtintype-types + + (builtintype + (signedmod + builtintype-types + ,(semantic-lambda + (list + (concat + (car + (nth 0 vals)) + " " + (car + (nth 1 vals))))) + ) + (builtintype-types + ,(semantic-lambda + (nth 0 vals)) + ) + (signedmod + ,(semantic-lambda + (list + (concat + (car + (nth 0 vals)) + " int"))) + ) + ) ;; end builtintype + + (codeblock-var-or-fun + (declmods + typeformbase + declmods + opt-ref + var-or-func-decl + ,(semantic-lambda + (semantic-c-reconstitute-token + (nth 4 vals) + (nth 0 vals) + (nth 1 vals))) + ) + ) ;; end codeblock-var-or-fun + + (var-or-fun + (codeblock-var-or-fun + ,(semantic-lambda + (nth 0 vals)) + ) + (declmods + var-or-func-decl + ,(semantic-lambda + (semantic-c-reconstitute-token + (nth 1 vals) + (nth 0 vals) nil)) + ) + ) ;; end var-or-fun + + (var-or-func-decl + (func-decl + ,(semantic-lambda + (nth 0 vals)) + ) + (var-decl + ,(semantic-lambda + (nth 0 vals)) + ) + ) ;; end var-or-func-decl + + (func-decl + (opt-stars + opt-class + opt-destructor + functionname + opt-template-specifier + opt-under-p + arg-list + opt-post-fcn-modifiers + opt-throw + opt-initializers + fun-or-proto-end + ,(semantic-lambda + (nth 3 vals) + (list + 'function + (nth 1 vals) + (nth 2 vals) + (nth 6 vals) + (nth 8 vals) + (nth 7 vals)) + (nth 0 vals) + (nth 10 vals) + (nth 4 vals)) + ) + (opt-stars + opt-class + opt-destructor + functionname + opt-template-specifier + opt-under-p + opt-post-fcn-modifiers + opt-throw + opt-initializers + fun-try-end + ,(semantic-lambda + (nth 3 vals) + (list + 'function + (nth 1 vals) + (nth 2 vals) nil + (nth 7 vals) + (nth 6 vals)) + (nth 0 vals) + (nth 9 vals) + (nth 4 vals)) + ) + ) ;; end func-decl + + (var-decl + (varnamelist + punctuation + "\\`[;]\\'" + ,(semantic-lambda + (list + (nth 0 vals) + 'variable)) + ) + ) ;; end var-decl + + (opt-under-p + (UNDERP + ,(semantic-lambda + (list nil)) + ) + (UNDERUNDERP + ,(semantic-lambda + (list nil)) + ) + ( ;;EMPTY + ) + ) ;; end opt-under-p + + (opt-initializers + (punctuation + "\\`[:]\\'" + namespace-symbol + semantic-list + opt-initializers) + (punctuation + "\\`[,]\\'" + namespace-symbol + semantic-list + opt-initializers) + ( ;;EMPTY + ) + ) ;; end opt-initializers + + (opt-post-fcn-modifiers + (post-fcn-modifiers + opt-post-fcn-modifiers + ,(semantic-lambda + (cons + (nth 0 vals) + (nth 1 vals))) + ) + ( ;;EMPTY + ,(semantic-lambda + (list nil)) + ) + ) ;; end opt-post-fcn-modifiers + + (post-fcn-modifiers + (REENTRANT) + (CONST) + ) ;; end post-fcn-modifiers + + (opt-throw + (THROW + semantic-list + ,(lambda (vals start end) + (semantic-bovinate-from-nonterminal + (car + (nth 1 vals)) + (cdr + (nth 1 vals)) + 'throw-exception-list)) + ) + ( ;;EMPTY + ) + ) ;; end opt-throw + + (throw-exception-list + (namespace-symbol + punctuation + "\\`[,]\\'" + throw-exception-list + ,(semantic-lambda + (cons + (car + (nth 0 vals)) + (nth 2 vals))) + ) + (namespace-symbol + close-paren + ")" + ,(semantic-lambda + (nth 0 vals)) + ) + (symbol + close-paren + ")" + ,(semantic-lambda + (list + (nth 0 vals))) + ) + (open-paren + "(" + throw-exception-list + ,(semantic-lambda + (nth 1 vals)) + ) + (close-paren + ")" + ,(semantic-lambda) + ) + ) ;; end throw-exception-list + + (opt-bits + (punctuation + "\\`[:]\\'" + number + ,(semantic-lambda + (list + (nth 1 vals))) + ) + ( ;;EMPTY + ,(semantic-lambda + (list nil)) + ) + ) ;; end opt-bits + + (opt-array + (semantic-list + "\\[.*\\]$" + opt-array + ,(semantic-lambda + (list + (cons + 1 + (car + (nth 1 vals))))) + ) + ( ;;EMPTY + ,(semantic-lambda + (list nil)) + ) + ) ;; end opt-array + + (opt-assign + (punctuation + "\\`[=]\\'" + expression + ,(semantic-lambda + (list + (nth 1 vals))) + ) + ( ;;EMPTY + ,(semantic-lambda + (list nil)) + ) + ) ;; end opt-assign + + (opt-restrict + (symbol + "\\<\\(__\\)?restrict\\>") + ( ;;EMPTY + ) + ) ;; end opt-restrict + + (varname + (opt-stars + opt-restrict + namespace-symbol + opt-bits + opt-array + opt-assign + ,(semantic-lambda + (nth 2 vals) + (nth 0 vals) + (nth 3 vals) + (nth 4 vals) + (nth 5 vals)) + ) + ) ;; end varname + + (variablearg + (declmods + typeformbase + cv-declmods + opt-ref + variablearg-opt-name + ,(semantic-lambda + (semantic-tag-new-variable + (list + (nth 4 vals)) + (nth 1 vals) nil :constant-flag + (if + (member + "const" + (append + (nth 0 vals) + (nth 2 vals))) t nil) :typemodifiers + (delete + "const" + (append + (nth 0 vals) + (nth 2 vals))) :reference + (car + (nth 3 vals)))) + ) + ) ;; end variablearg + + (variablearg-opt-name + (varname + ,(semantic-lambda + (nth 0 vals)) + ) + (opt-stars + ,(semantic-lambda + (list + "") + (nth 0 vals) + (list nil nil nil)) + ) + ) ;; end variablearg-opt-name + + (varnamelist + (opt-ref + varname + punctuation + "\\`[,]\\'" + varnamelist + ,(semantic-lambda + (cons + (nth 1 vals) + (nth 3 vals))) + ) + (opt-ref + varname + ,(semantic-lambda + (list + (nth 1 vals))) + ) + ) ;; end varnamelist + + (namespace-symbol + (symbol + opt-template-specifier + punctuation + "\\`[:]\\'" + punctuation + "\\`[:]\\'" + namespace-symbol + ,(semantic-lambda + (list + (concat + (nth 0 vals) + "::" + (car + (nth 4 vals))))) + ) + (symbol + opt-template-specifier + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end namespace-symbol + + (namespace-symbol-for-typeformbase + (symbol + opt-template-specifier + punctuation + "\\`[:]\\'" + punctuation + "\\`[:]\\'" + namespace-symbol-for-typeformbase + ,(semantic-lambda + (list + (concat + (nth 0 vals) + "::" + (car + (nth 4 vals))))) + ) + (symbol + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end namespace-symbol-for-typeformbase + + (namespace-opt-class + (symbol + punctuation + "\\`[:]\\'" + punctuation + "\\`[:]\\'" + namespace-opt-class + ,(semantic-lambda + (list + (concat + (nth 0 vals) + "::" + (car + (nth 3 vals))))) + ) + (symbol + opt-template-specifier + punctuation + "\\`[:]\\'" + punctuation + "\\`[:]\\'" + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end namespace-opt-class + + (opt-class + (namespace-opt-class + ,(semantic-lambda + (nth 0 vals)) + ) + ( ;;EMPTY + ,(semantic-lambda + (list nil)) + ) + ) ;; end opt-class + + (opt-destructor + (punctuation + "\\`[~]\\'" + ,(semantic-lambda + (list t)) + ) + ( ;;EMPTY + ,(semantic-lambda + (list nil)) + ) + ) ;; end opt-destructor + + (arg-list + (semantic-list + "^(" + knr-arguments + ,(semantic-lambda + (nth 1 vals)) + ) + (semantic-list + "^(" + ,(semantic-lambda + (semantic-parse-region + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'arg-sub-list + 1)) + ) + (semantic-list + "^(void)$" + ,(semantic-lambda) + ) + ) ;; end arg-list + + (knr-varnamelist + (varname + punctuation + "\\`[,]\\'" + knr-varnamelist + ,(semantic-lambda + (cons + (nth 0 vals) + (nth 2 vals))) + ) + (varname + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end knr-varnamelist + + (knr-one-variable-decl + (declmods + typeformbase + cv-declmods + knr-varnamelist + ,(semantic-lambda + (semantic-tag-new-variable + (nreverse + (nth 3 vals)) + (nth 1 vals) nil :constant-flag + (if + (member + "const" + (append + (nth 2 vals))) t nil) :typemodifiers + (delete + "const" + (nth 2 vals)))) + ) + ) ;; end knr-one-variable-decl + + (knr-arguments + (knr-one-variable-decl + punctuation + "\\`[;]\\'" + knr-arguments + ,(semantic-lambda + (append + (semantic-expand-c-tag + (nth 0 vals)) + (nth 2 vals))) + ) + (knr-one-variable-decl + punctuation + "\\`[;]\\'" + ,(semantic-lambda + (semantic-expand-c-tag + (nth 0 vals))) + ) + ) ;; end knr-arguments + + (arg-sub-list + (variablearg + ,(semantic-lambda + (nth 0 vals)) + ) + (punctuation + "\\`[.]\\'" + punctuation + "\\`[.]\\'" + punctuation + "\\`[.]\\'" + close-paren + ")" + ,(semantic-lambda + (semantic-tag-new-variable + "..." + "vararg" nil)) + ) + (punctuation + "\\`[,]\\'" + ,(semantic-lambda + (list nil)) + ) + (open-paren + "(" + ,(semantic-lambda + (list nil)) + ) + (close-paren + ")" + ,(semantic-lambda + (list nil)) + ) + ) ;; end arg-sub-list + + (operatorsym + (punctuation + "\\`[<]\\'" + punctuation + "\\`[<]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "<<=")) + ) + (punctuation + "\\`[>]\\'" + punctuation + "\\`[>]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + ">>=")) + ) + (punctuation + "\\`[<]\\'" + punctuation + "\\`[<]\\'" + ,(semantic-lambda + (list + "<<")) + ) + (punctuation + "\\`[>]\\'" + punctuation + "\\`[>]\\'" + ,(semantic-lambda + (list + ">>")) + ) + (punctuation + "\\`[=]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "==")) + ) + (punctuation + "\\`[<]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "<=")) + ) + (punctuation + "\\`[>]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + ">=")) + ) + (punctuation + "\\`[!]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "!=")) + ) + (punctuation + "\\`[+]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "+=")) + ) + (punctuation + "\\`[-]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "-=")) + ) + (punctuation + "\\`[*]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "*=")) + ) + (punctuation + "\\`[/]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "/=")) + ) + (punctuation + "\\`[%]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "%=")) + ) + (punctuation + "\\`[&]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "&=")) + ) + (punctuation + "\\`[|]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "|=")) + ) + (punctuation + "\\`[-]\\'" + punctuation + "\\`[>]\\'" + punctuation + "\\`[*]\\'" + ,(semantic-lambda + (list + "->*")) + ) + (punctuation + "\\`[-]\\'" + punctuation + "\\`[>]\\'" + ,(semantic-lambda + (list + "->")) + ) + (semantic-list + "()" + ,(semantic-lambda + (list + "()")) + ) + (semantic-list + "\\[\\]" + ,(semantic-lambda + (list + "[]")) + ) + (punctuation + "\\`[<]\\'") + (punctuation + "\\`[>]\\'") + (punctuation + "\\`[*]\\'") + (punctuation + "\\`[+]\\'" + punctuation + "\\`[+]\\'" + ,(semantic-lambda + (list + "++")) + ) + (punctuation + "\\`[+]\\'") + (punctuation + "\\`[-]\\'" + punctuation + "\\`[-]\\'" + ,(semantic-lambda + (list + "--")) + ) + (punctuation + "\\`[-]\\'") + (punctuation + "\\`[&]\\'" + punctuation + "\\`[&]\\'" + ,(semantic-lambda + (list + "&&")) + ) + (punctuation + "\\`[&]\\'") + (punctuation + "\\`[|]\\'" + punctuation + "\\`[|]\\'" + ,(semantic-lambda + (list + "||")) + ) + (punctuation + "\\`[|]\\'") + (punctuation + "\\`[/]\\'") + (punctuation + "\\`[=]\\'") + (punctuation + "\\`[!]\\'") + (punctuation + "\\`[~]\\'") + (punctuation + "\\`[%]\\'") + (punctuation + "\\`[,]\\'") + (punctuation + "\\`\\^\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda + (list + "^=")) + ) + (punctuation + "\\`\\^\\'") + ) ;; end operatorsym + + (functionname + (OPERATOR + operatorsym + ,(semantic-lambda + (nth 1 vals)) + ) + (semantic-list + ,(lambda (vals start end) + (semantic-bovinate-from-nonterminal + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'function-pointer)) + ) + (symbol + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end functionname + + (function-pointer + (open-paren + "(" + punctuation + "\\`[*]\\'" + symbol + close-paren + ")" + ,(semantic-lambda + (list + (concat + "*" + (nth 2 vals)))) + ) + ) ;; end function-pointer + + (fun-or-proto-end + (punctuation + "\\`[;]\\'" + ,(semantic-lambda + (list t)) + ) + (semantic-list + ,(semantic-lambda + (list nil)) + ) + (punctuation + "\\`[=]\\'" + number + "^0$" + punctuation + "\\`[;]\\'" + ,(semantic-lambda + (list ':pure-virtual-flag)) + ) + (fun-try-end + ,(semantic-lambda + (list nil)) + ) + ) ;; end fun-or-proto-end + + (fun-try-end + (TRY + opt-initializers + semantic-list + "^{" + fun-try-several-catches + ,(semantic-lambda + (list nil)) + ) + ) ;; end fun-try-end + + (fun-try-several-catches + (CATCH + semantic-list + "^(" + semantic-list + "^{" + fun-try-several-catches + ,(semantic-lambda) + ) + (CATCH + semantic-list + "^{" + fun-try-several-catches + ,(semantic-lambda) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end fun-try-several-catches + + (type-cast + (semantic-list + ,(lambda (vals start end) + (semantic-bovinate-from-nonterminal + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'type-cast-list)) + ) + ) ;; end type-cast + + (type-cast-list + (open-paren + typeformbase + close-paren) + ) ;; end type-cast-list + + (opt-stuff-after-symbol + (semantic-list + "^(") + (semantic-list + "\\[.*\\]$") + ( ;;EMPTY + ) + ) ;; end opt-stuff-after-symbol + + (multi-stage-dereference + (namespace-symbol + opt-stuff-after-symbol + punctuation + "\\`[.]\\'" + multi-stage-dereference) + (namespace-symbol + opt-stuff-after-symbol + punctuation + "\\`[-]\\'" + punctuation + "\\`[>]\\'" + multi-stage-dereference) + (namespace-symbol + opt-stuff-after-symbol) + ) ;; end multi-stage-dereference + + (string-seq + (string + string-seq + ,(semantic-lambda + (list + (concat + (nth 0 vals) + (car + (nth 1 vals))))) + ) + (string + ,(semantic-lambda + (list + (nth 0 vals))) + ) + ) ;; end string-seq + + (expr-start + (punctuation + "\\`[-]\\'") + (punctuation + "\\`[+]\\'") + (punctuation + "\\`[*]\\'") + (punctuation + "\\`[&]\\'") + ) ;; end expr-start + + (expression + (number + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (multi-stage-dereference + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (NEW + multi-stage-dereference + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (NEW + builtintype-types + semantic-list + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (namespace-symbol + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (string-seq + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (type-cast + expression + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (semantic-list + expression + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (semantic-list + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + (expr-start + expression + ,(semantic-lambda + (list + (identity start) + (identity end))) + ) + ) ;; end expression + ) + "Parser table.") + +(defun semantic-c-by--install-parser () + "Setup the Semantic Parser." + (setq semantic--parse-table semantic-c-by--parse-table + semantic-debug-parser-source "c.by" + semantic-debug-parser-class 'semantic-bovine-debug-parser + semantic-flex-keywords-obarray semantic-c-by--keyword-table + semantic-equivalent-major-modes '(c-mode c++-mode) + )) + +;;; Epilogue +;; + +(provide 'semantic/bovine/c-by) + +;;; semantic/bovine/c-by.el ends here diff --git a/lisp/cedet/semantic/bovine/c.el b/lisp/cedet/semantic/bovine/c.el new file mode 100644 index 00000000000..b9077a2ef0b --- /dev/null +++ b/lisp/cedet/semantic/bovine/c.el @@ -0,0 +1,1736 @@ +;;; semantic/bovine/c.el --- Semantic details for C + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Support for the C/C++ bovine parser for Semantic. +;; +;; @todo - can I support c++-font-lock-extra-types ? + +(require 'semantic) +(require 'semantic/analyze) +(require 'semantic/bovine/gcc) +(require 'semantic/idle) +(require 'semantic/lex-spp) +(require 'semantic/bovine/c-by) + +(eval-when-compile + (require 'semantic/find)) + +(declare-function semantic-brute-find-tag-by-attribute "semantic/find") +(declare-function semanticdb-minor-mode-p "semantic/db-mode") +(declare-function semanticdb-needs-refresh-p "semantic/db") +(declare-function c-forward-conditional "cc-cmds") +(declare-function ede-system-include-path "ede") + +;;; Compatibility +;; +(eval-when-compile (require 'cc-mode)) + +(if (fboundp 'c-end-of-macro) + (eval-and-compile + (defalias 'semantic-c-end-of-macro 'c-end-of-macro)) + ;; From cc-mode 5.30 + (defun semantic-c-end-of-macro () + "Go to the end of a preprocessor directive. +More accurately, move point to the end of the closest following line +that doesn't end with a line continuation backslash. + +This function does not do any hidden buffer changes." + (while (progn + (end-of-line) + (when (and (eq (char-before) ?\\) + (not (eobp))) + (forward-char) + t)))) + ) + +;;; Code: +(define-child-mode c++-mode c-mode + "`c++-mode' uses the same parser as `c-mode'.") + + +;;; Include Paths +;; +(defcustom-mode-local-semantic-dependency-system-include-path + c-mode semantic-c-dependency-system-include-path + '("/usr/include") + "The system include path used by the C langauge.") + +(defcustom semantic-default-c-path nil + "Default set of include paths for C code. +Used by `semantic-dep' to define an include path. +NOTE: In process of obsoleting this." + :group 'c + :group 'semantic + :type '(repeat (string :tag "Path"))) + +(defvar-mode-local c-mode semantic-dependency-include-path + semantic-default-c-path + "System path to search for include files.") + +;;; Compile Options +;; +;; Compiler options need to show up after path setup, but before +;; the preprocessor section. + +(when (member system-type '(gnu gnu/linux darwin cygwin)) + (semantic-gcc-setup)) + +;;; Pre-processor maps +;; +;;; Lexical analysis +(defvar semantic-lex-c-preprocessor-symbol-map-builtin + '( ("__THROW" . "") + ("__const" . "const") + ("__restrict" . "") + ("__declspec" . ((spp-arg-list ("foo") 1 . 2))) + ("__attribute__" . ((spp-arg-list ("foo") 1 . 2))) + ) + "List of symbols to include by default.") + +(defvar semantic-c-in-reset-preprocessor-table nil + "Non-nil while resetting the preprocessor symbol map. +Used to prevent a reset while trying to parse files that are +part of the preprocessor map.") + +(defvar semantic-lex-c-preprocessor-symbol-file) +(defvar semantic-lex-c-preprocessor-symbol-map) + +(defun semantic-c-reset-preprocessor-symbol-map () + "Reset the C preprocessor symbol map based on all input variables." + (when (featurep 'semantic/bovine/c) + (let ((filemap nil) + ) + (when (and (not semantic-c-in-reset-preprocessor-table) + (featurep 'semantic/db-mode) + (semanticdb-minor-mode-p)) + (let ( ;; Don't use external parsers. We need the internal one. + (semanticdb-out-of-buffer-create-table-fcn nil) + ;; Don't recurse while parsing these files the first time. + (semantic-c-in-reset-preprocessor-table t) + ) + (dolist (sf semantic-lex-c-preprocessor-symbol-file) + ;; Global map entries + (let* ((table (semanticdb-file-table-object sf t))) + (when table + (when (semanticdb-needs-refresh-p table) + (condition-case nil + ;; Call with FORCE, as the file is very likely to + ;; not be in a buffer. + (semanticdb-refresh-table table t) + (error (message "Error updating tables for %S" + (object-name table))))) + (setq filemap (append filemap (oref table lexical-table))) + ) + )))) + + (setq-mode-local c-mode + semantic-lex-spp-macro-symbol-obarray + (semantic-lex-make-spp-table + (append semantic-lex-c-preprocessor-symbol-map-builtin + semantic-lex-c-preprocessor-symbol-map + filemap)) + ) + ))) + +(defcustom semantic-lex-c-preprocessor-symbol-map nil + "Table of C Preprocessor keywords used by the Semantic C lexer. +Each entry is a cons cell like this: + ( \"KEYWORD\" . \"REPLACEMENT\" ) +Where KEYWORD is the macro that gets replaced in the lexical phase, +and REPLACEMENT is a string that is inserted in it's place. Empty string +implies that the lexical analyzer will discard KEYWORD when it is encountered. + +Alternately, it can be of the form: + ( \"KEYWORD\" ( LEXSYM1 \"str\" 1 1 ) ... ( LEXSYMN \"str\" 1 1 ) ) +where LEXSYM is a symbol that would normally be produced by the +lexical analyzer, such as `symbol' or `string'. The string in the +second position is the text that makes up the replacement. This is +the way to have multiple lexical symbols in a replacement. Using the +first way to specify text like \"foo::bar\" would not work, because : +is a sepearate lexical symbol. + +A quick way to see what you would need to insert is to place a +definition such as: + +#define MYSYM foo::bar + +into a C file, and do this: + \\[semantic-lex-spp-describe] + +The output table will describe the symbols needed." + :group 'c + :type '(repeat (cons (string :tag "Keyword") + (sexp :tag "Replacement"))) + :set (lambda (sym value) + (set-default sym value) + (condition-case nil + (semantic-c-reset-preprocessor-symbol-map) + (error nil)) + ) + ) + +(defcustom semantic-lex-c-preprocessor-symbol-file nil + "List of C/C++ files that contain preprocessor macros for the C lexer. +Each entry is a filename and each file is parsed, and those macros +are included in every C/C++ file parsed by semantic. +You can use this variable instead of `semantic-lex-c-preprocessor-symbol-map' +to store your global macros in a more natural way." + :group 'c + :type '(repeat (file :tag "File")) + :set (lambda (sym value) + (set-default sym value) + (condition-case nil + (semantic-c-reset-preprocessor-symbol-map) + (error nil)) + ) + ) + +(defcustom semantic-c-member-of-autocast 't + "Non-nil means classes with a '->' operator will cast to it's return type. + +For Examples: + + class Foo { + Bar *operator->(); + } + + Foo foo; + +if `semantic-c-member-of-autocast' is non-nil : + foo->[here completion will list method of Bar] + +if `semantic-c-member-of-autocast' is nil : + foo->[here completion will list method of Foo]" + :group 'c + :type 'boolean) + +(define-lex-spp-macro-declaration-analyzer semantic-lex-cpp-define + "A #define of a symbol with some value. +Record the symbol in the semantic preprocessor. +Return the the defined symbol as a special spp lex token." + "^\\s-*#\\s-*define\\s-+\\(\\(\\sw\\|\\s_\\)+\\)" 1 + (goto-char (match-end 0)) + (skip-chars-forward " \t") + (if (eolp) + nil + (let* ((name (buffer-substring-no-properties + (match-beginning 1) (match-end 1))) + (with-args (save-excursion + (goto-char (match-end 0)) + (looking-at "("))) + (semantic-lex-spp-replacements-enabled nil) + ;; Temporarilly override the lexer to include + ;; special items needed inside a macro + (semantic-lex-analyzer #'semantic-cpp-lexer) + (raw-stream + (semantic-lex-spp-stream-for-macro (save-excursion + (semantic-c-end-of-macro) + (point)))) + ) + + ;; Only do argument checking if the paren was immediatly after + ;; the macro name. + (if with-args + (semantic-lex-spp-first-token-arg-list (car raw-stream))) + + ;; Magical spp variable for end point. + (setq semantic-lex-end-point (point)) + + ;; Handled nested macro streams. + (semantic-lex-spp-merge-streams raw-stream) + ))) + +(define-lex-spp-macro-undeclaration-analyzer semantic-lex-cpp-undef + "A #undef of a symbol. +Remove the symbol from the semantic preprocessor. +Return the the defined symbol as a special spp lex token." + "^\\s-*#\\s-*undef\\s-+\\(\\(\\sw\\|\\s_\\)+\\)" 1) + + +;;; Conditional Skipping +;; +(defcustom semantic-c-obey-conditional-section-parsing-flag t + "*Non-nil means to interpret preprocessor #if sections. +This implies that some blocks of code will not be parsed based on the +values of the conditions in the #if blocks." + :group 'c + :type 'boolean) + +(defun semantic-c-skip-conditional-section () + "Skip one section of a conditional. +Moves forward to a matching #elif, #else, or #endif. +Moves completely over balanced #if blocks." + (require 'cc-cmds) + (let ((done nil)) + ;; (if (looking-at "^\\s-*#if") + ;; (semantic-lex-spp-push-if (point)) + (end-of-line) + (while (and semantic-c-obey-conditional-section-parsing-flag + (and (not done) + (re-search-forward + "^\\s-*#\\s-*\\(if\\(n?def\\)?\\|el\\(if\\|se\\)\\|endif\\)\\>" + nil t))) + (goto-char (match-beginning 0)) + (cond + ((looking-at "^\\s-*#\\s-*if") + ;; We found a nested if. Skip it. + (c-forward-conditional 1)) + ((looking-at "^\\s-*#\\s-*elif") + ;; We need to let the preprocessor analize this one. + (beginning-of-line) + (setq done t) + ) + ((looking-at "^\\s-*#\\s-*\\(endif\\|else\\)\\>") + ;; We are at the end. Pop our state. + ;; (semantic-lex-spp-pop-if) + ;; Note: We include ELSE and ENDIF the same. If skip some previous + ;; section, then we should do the else by default, making it much + ;; like the endif. + (end-of-line) + (forward-char 1) + (setq done t)) + (t + ;; We found an elif. Stop here. + (setq done t)))))) + +(define-lex-regex-analyzer semantic-lex-c-if + "Code blocks wrapped up in #if, or #ifdef. +Uses known macro tables in SPP to determine what block to skip." + "^\\s-*#\\s-*\\(if\\|ifndef\\|ifdef\\|elif\\)\\s-+\\(!?defined(\\|\\)\\s-*\\(\\(\\sw\\|\\s_\\)+\\)\\(\\s-*)\\)?\\s-*$" + (semantic-c-do-lex-if)) + +(defun semantic-c-do-lex-if () + "Handle lexical CPP if statements." + (let* ((sym (buffer-substring-no-properties + (match-beginning 3) (match-end 3))) + (defstr (buffer-substring-no-properties + (match-beginning 2) (match-end 2))) + (defined (string= defstr "defined(")) + (notdefined (string= defstr "!defined(")) + (ift (buffer-substring-no-properties + (match-beginning 1) (match-end 1))) + (ifdef (or (string= ift "ifdef") + (and (string= ift "if") defined) + (and (string= ift "elif") defined) + )) + (ifndef (or (string= ift "ifndef") + (and (string= ift "if") notdefined) + (and (string= ift "elif") notdefined) + )) + ) + (if (or (and (or (string= ift "if") (string= ift "elif")) + (string= sym "0")) + (and ifdef (not (semantic-lex-spp-symbol-p sym))) + (and ifndef (semantic-lex-spp-symbol-p sym))) + ;; The if indecates to skip this preprocessor section + (let ((pt nil)) + ;; (message "%s %s yes" ift sym) + (beginning-of-line) + (setq pt (point)) + ;;(c-forward-conditional 1) + ;; This skips only a section of a conditional. Once that section + ;; is opened, encountering any new #else or related conditional + ;; should be skipped. + (semantic-c-skip-conditional-section) + (setq semantic-lex-end-point (point)) + (semantic-push-parser-warning (format "Skip #%s %s" ift sym) + pt (point)) +;; (semantic-lex-push-token +;; (semantic-lex-token 'c-preprocessor-skip pt (point))) + nil) + ;; Else, don't ignore it, but do handle the internals. + ;;(message "%s %s no" ift sym) + (end-of-line) + (setq semantic-lex-end-point (point)) + nil))) + +(define-lex-regex-analyzer semantic-lex-c-macro-else + "Ignore an #else block. +We won't see the #else due to the macro skip section block +unless we are actively parsing an open #if statement. In that +case, we must skip it since it is the ELSE part." + "^\\s-*#\\s-*\\(else\\)" + (let ((pt (point))) + (semantic-c-skip-conditional-section) + (setq semantic-lex-end-point (point)) + (semantic-push-parser-warning "Skip #else" pt (point)) +;; (semantic-lex-push-token +;; (semantic-lex-token 'c-preprocessor-skip pt (point))) + nil)) + +(define-lex-regex-analyzer semantic-lex-c-macrobits + "Ignore various forms of #if/#else/#endif conditionals." + "^\\s-*#\\s-*\\(if\\(n?def\\)?\\|endif\\|elif\\|else\\)" + (semantic-c-end-of-macro) + (setq semantic-lex-end-point (point)) + nil) + +(define-lex-spp-include-analyzer semantic-lex-c-include-system + "Identify include strings, and return special tokens." + "^\\s-*#\\s-*include\\s-*<\\([^ \t\n>]+\\)>" 0 + ;; Hit 1 is the name of the include. + (goto-char (match-end 0)) + (setq semantic-lex-end-point (point)) + (cons (buffer-substring-no-properties (match-beginning 1) + (match-end 1)) + 'system)) + +(define-lex-spp-include-analyzer semantic-lex-c-include + "Identify include strings, and return special tokens." + "^\\s-*#\\s-*include\\s-*\"\\([^ \t\n>]+\\)\"" 0 + ;; Hit 1 is the name of the include. + (goto-char (match-end 0)) + (setq semantic-lex-end-point (point)) + (cons (buffer-substring-no-properties (match-beginning 1) + (match-end 1)) + nil)) + + +(define-lex-regex-analyzer semantic-lex-c-ignore-ending-backslash + "Skip backslash ending a line. +Go to the next line." + "\\\\\\s-*\n" + (setq semantic-lex-end-point (match-end 0))) + +(define-lex-regex-analyzer semantic-lex-c-namespace-begin-macro + "Handle G++'s namespace macros which the pre-processor can't handle." + "\\(_GLIBCXX_BEGIN_NAMESPACE\\)(\\s-*\\(\\(?:\\w\\|\\s_\\)+\\)\\s-*)" + (let* ((nsend (match-end 1)) + (sym-start (match-beginning 2)) + (sym-end (match-end 2)) + (ms (buffer-substring-no-properties sym-start sym-end))) + ;; Push the namespace keyword. + (semantic-lex-push-token + (semantic-lex-token 'NAMESPACE (match-beginning 0) nsend "namespace")) + ;; Push the name. + (semantic-lex-push-token + (semantic-lex-token 'symbol sym-start sym-end ms)) + ) + (goto-char (match-end 0)) + (let ((start (point)) + (end 0)) + ;; If we can't find a matching end, then create the fake list. + (when (re-search-forward "_GLIBCXX_END_NAMESPACE" nil t) + (setq end (point)) + (semantic-lex-push-token + (semantic-lex-token 'semantic-list start end + (list 'prefix-fake))))) + (setq semantic-lex-end-point (point))) + +(defcustom semantic-lex-c-nested-namespace-ignore-second t + "Should _GLIBCXX_BEGIN_NESTED_NAMESPACE ignore the second namespace? +It is really there, but if a majority of uses is to squeeze out +the second namespace in use, then it should not be included. + +If you are having problems with smart completion and STL templates, +it may that this is set incorrectly. After changing the value +of this flag, you will need to delete any semanticdb cache files +that may have been incorrectly parsed." + :group 'semantic + :type 'boolean) + +(define-lex-regex-analyzer semantic-lex-c-VC++-begin-std-namespace + "Handle VC++'s definition of the std namespace." + "\\(_STD_BEGIN\\)" + (semantic-lex-push-token + (semantic-lex-token 'NAMESPACE (match-beginning 0) (match-end 0) "namespace")) + (semantic-lex-push-token + (semantic-lex-token 'symbol (match-beginning 0) (match-end 0) "std")) + (goto-char (match-end 0)) + (let ((start (point)) + (end 0)) + (when (re-search-forward "_STD_END" nil t) + (setq end (point)) + (semantic-lex-push-token + (semantic-lex-token 'semantic-list start end + (list 'prefix-fake))))) + (setq semantic-lex-end-point (point))) + +(define-lex-regex-analyzer semantic-lex-c-VC++-end-std-namespace + "Handle VC++'s definition of the std namespace." + "\\(_STD_END\\)" + (goto-char (match-end 0)) + (setq semantic-lex-end-point (point))) + +(define-lex-regex-analyzer semantic-lex-c-namespace-begin-nested-macro + "Handle G++'s namespace macros which the pre-processor can't handle." + "\\(_GLIBCXX_BEGIN_NESTED_NAMESPACE\\)(\\s-*\\(\\(?:\\w\\|\\s_\\)+\\)\\s-*,\\s-*\\(\\(?:\\w\\|\\s_\\)+\\)\\s-*)" + (goto-char (match-end 0)) + (let* ((nsend (match-end 1)) + (sym-start (match-beginning 2)) + (sym-end (match-end 2)) + (ms (buffer-substring-no-properties sym-start sym-end)) + (sym2-start (match-beginning 3)) + (sym2-end (match-end 3)) + (ms2 (buffer-substring-no-properties sym2-start sym2-end))) + ;; Push the namespace keyword. + (semantic-lex-push-token + (semantic-lex-token 'NAMESPACE (match-beginning 0) nsend "namespace")) + ;; Push the name. + (semantic-lex-push-token + (semantic-lex-token 'symbol sym-start sym-end ms)) + + (goto-char (match-end 0)) + (let ((start (point)) + (end 0)) + ;; If we can't find a matching end, then create the fake list. + (when (re-search-forward "_GLIBCXX_END_NESTED_NAMESPACE" nil t) + (setq end (point)) + (if semantic-lex-c-nested-namespace-ignore-second + ;; The same as _GLIBCXX_BEGIN_NAMESPACE + (semantic-lex-push-token + (semantic-lex-token 'semantic-list start end + (list 'prefix-fake))) + ;; Do both the top and second level namespace + (semantic-lex-push-token + (semantic-lex-token 'semantic-list start end + ;; We'll depend on a quick hack + (list 'prefix-fake-plus + (semantic-lex-token 'NAMESPACE + sym-end sym2-start + "namespace") + (semantic-lex-token 'symbol + sym2-start sym2-end + ms2) + (semantic-lex-token 'semantic-list start end + (list 'prefix-fake))) + ))) + ))) + (setq semantic-lex-end-point (point))) + +(define-lex-regex-analyzer semantic-lex-c-namespace-end-macro + "Handle G++'s namespace macros which the pre-processor can't handle." + "_GLIBCXX_END_\\(NESTED_\\)?NAMESPACE" + (goto-char (match-end 0)) + (setq semantic-lex-end-point (point))) + +(define-lex-regex-analyzer semantic-lex-c-string + "Detect and create a C string token." + "L?\\(\\s\"\\)" + ;; Zing to the end of this string. + (semantic-lex-push-token + (semantic-lex-token + 'string (point) + (save-excursion + ;; Skip L prefix if present. + (goto-char (match-beginning 1)) + (semantic-lex-unterminated-syntax-protection 'string + (forward-sexp 1) + (point)) + )))) + +(define-lex-regex-analyzer semantic-c-lex-ignore-newline + "Detect and ignore newline tokens. +Use this ONLY if newlines are not whitespace characters (such as when +they are comment end characters)." + ;; Just like semantic-lex-ignore-newline, but also ignores + ;; trailing \. + "\\s-*\\\\?\\s-*\\(\n\\|\\s>\\)" + (setq semantic-lex-end-point (match-end 0))) + + +(define-lex semantic-c-lexer + "Lexical Analyzer for C code. +Use semantic-cpp-lexer for parsing text inside a CPP macro." + ;; C preprocessor features + semantic-lex-cpp-define + semantic-lex-cpp-undef + semantic-lex-c-if + semantic-lex-c-macro-else + semantic-lex-c-macrobits + semantic-lex-c-include + semantic-lex-c-include-system + semantic-lex-c-ignore-ending-backslash + ;; Whitespace handling + semantic-lex-ignore-whitespace + semantic-c-lex-ignore-newline + ;; Non-preprocessor features + semantic-lex-number + ;; Must detect C strings before symbols because of possible L prefix! + semantic-lex-c-string + ;; Custom handlers for some macros come before the macro replacement analyzer. + semantic-lex-c-namespace-begin-macro + semantic-lex-c-namespace-begin-nested-macro + semantic-lex-c-namespace-end-macro + semantic-lex-c-VC++-begin-std-namespace + semantic-lex-c-VC++-end-std-namespace + ;; Handle macros, symbols, and keywords + semantic-lex-spp-replace-or-symbol-or-keyword + semantic-lex-charquote + semantic-lex-paren-or-list + semantic-lex-close-paren + semantic-lex-ignore-comments + semantic-lex-punctuation + semantic-lex-default-action) + +(define-lex-simple-regex-analyzer semantic-lex-cpp-hashhash + "Match ## inside a CPP macro as special." + "##" 'spp-concat) + +(define-lex semantic-cpp-lexer + "Lexical Analyzer for CPP macros in C code." + ;; CPP special + semantic-lex-cpp-hashhash + ;; C preprocessor features + semantic-lex-cpp-define + semantic-lex-cpp-undef + semantic-lex-c-if + semantic-lex-c-macro-else + semantic-lex-c-macrobits + semantic-lex-c-include + semantic-lex-c-include-system + semantic-lex-c-ignore-ending-backslash + ;; Whitespace handling + semantic-lex-ignore-whitespace + semantic-c-lex-ignore-newline + ;; Non-preprocessor features + semantic-lex-number + ;; Must detect C strings before symbols because of possible L prefix! + semantic-lex-c-string + ;; Parsing inside a macro means that we don't do macro replacement. + ;; semantic-lex-spp-replace-or-symbol-or-keyword + semantic-lex-symbol-or-keyword + semantic-lex-charquote + semantic-lex-paren-or-list + semantic-lex-close-paren + semantic-lex-ignore-comments + semantic-lex-punctuation + semantic-lex-default-action) + +(define-mode-local-override semantic-parse-region c-mode + (start end &optional nonterminal depth returnonerror) + "Calls 'semantic-parse-region-default', except in a macro expansion. +MACRO expansion mode is handled through the nature of Emacs's non-lexical +binding of variables. +START, END, NONTERMINAL, DEPTH, and RETURNONERRORS are the same +as for the parent." + (if (and (boundp 'lse) (or (/= start 1) (/= end (point-max)))) + (let* ((last-lexical-token lse) + (llt-class (semantic-lex-token-class last-lexical-token)) + (llt-fakebits (car (cdr last-lexical-token))) + (macroexpand (stringp (car (cdr last-lexical-token))))) + (if macroexpand + (progn + ;; It is a macro expansion. Do something special. + ;;(message "MOOSE %S %S, %S : %S" start end nonterminal lse) + (semantic-c-parse-lexical-token + lse nonterminal depth returnonerror) + ) + ;; Not a macro expansion, but perhaps a funny semantic-list + ;; is at the start? Remove the depth if our semantic list is not + ;; made of list tokens. + (if (and depth (= depth 1) + (eq llt-class 'semantic-list) + (not (null llt-fakebits)) + (consp llt-fakebits) + (symbolp (car llt-fakebits)) + ) + (progn + (setq depth 0) + + ;; This is a copy of semantic-parse-region-default where we + ;; are doing something special with the lexication of the + ;; contents of the semantic-list token. Stuff not used by C + ;; removed. + (let ((tokstream + (if (and (consp llt-fakebits) + (eq (car llt-fakebits) 'prefix-fake-plus)) + ;; If our semantic-list is special, then only stick in the + ;; fake tokens. + (cdr llt-fakebits) + ;; Lex up the region with a depth of 0 + (semantic-lex start end 0)))) + + ;; Do the parse + (nreverse + (semantic-repeat-parse-whole-stream tokstream + nonterminal + returnonerror)) + + )) + + ;; It was not a macro expansion, nor a special semantic-list. + ;; Do old thing. + (semantic-parse-region-default start end + nonterminal depth + returnonerror) + ))) + ;; Do the parse + (semantic-parse-region-default start end nonterminal + depth returnonerror) + )) + +(defvar semantic-c-parse-token-hack-depth 0 + "Current depth of recursive calls to `semantic-c-parse-lexical-token'") + +(defun semantic-c-parse-lexical-token (lexicaltoken nonterminal depth + returnonerror) + "Do a region parse on the contents of LEXICALTOKEN. +Presumably, this token has a string in it from a macro. +The text of the token is inserted into a different buffer, and +parsed there. +Argument NONTERMINAL, DEPTH, and RETURNONERROR are passed into +the regular parser." + (let* ((semantic-c-parse-token-hack-depth (1+ semantic-c-parse-token-hack-depth)) + (buf (get-buffer-create (format " *C parse hack %d*" + semantic-c-parse-token-hack-depth))) + (mode major-mode) + (spp-syms semantic-lex-spp-dynamic-macro-symbol-obarray) + (stream nil) + (start (semantic-lex-token-start lexicaltoken)) + (end (semantic-lex-token-end lexicaltoken)) + (symtext (semantic-lex-token-text lexicaltoken)) + (macros (get-text-property 0 'macros symtext)) + ) + (save-excursion + (set-buffer buf) + (erase-buffer) + (when (not (eq major-mode mode)) + (save-match-data + + ;; Protect against user hooks throwing errors. + (condition-case nil + (funcall mode) + (error nil)) + + ;; Hack in mode-local + (activate-mode-local-bindings) + ;; CHEATER! The following 3 lines are from + ;; `semantic-new-buffer-fcn', but we don't want to turn + ;; on all the other annoying modes for this little task. + (setq semantic-new-buffer-fcn-was-run t) + (semantic-lex-init) + (semantic-clear-toplevel-cache) + (remove-hook 'semantic-lex-reset-hooks 'semantic-lex-spp-reset-hook + t) + )) + ;; Get the macro symbol table right. + (setq semantic-lex-spp-dynamic-macro-symbol-obarray spp-syms) + ;; (message "%S" macros) + (dolist (sym macros) + (semantic-lex-spp-symbol-set (car sym) (cdr sym))) + + (insert symtext) + + (setq stream + (semantic-parse-region-default + (point-min) (point-max) nonterminal depth returnonerror)) + + ;; Clean up macro symbols + (dolist (sym macros) + (semantic-lex-spp-symbol-remove (car sym))) + + ;; Convert the text of the stream. + (dolist (tag stream) + ;; Only do two levels here 'cause I'm lazy. + (semantic--tag-set-overlay tag (list start end)) + (dolist (stag (semantic-tag-components-with-overlays tag)) + (semantic--tag-set-overlay stag (list start end)) + )) + ) + stream)) + +(defun semantic-expand-c-tag (tag) + "Expand TAG into a list of equivalent tags, or nil." + (let ((return-list nil) + ) + ;; Expand an EXTERN C first. + (when (eq (semantic-tag-class tag) 'extern) + (let* ((mb (semantic-tag-get-attribute tag :members)) + (ret mb)) + (while mb + (let ((mods (semantic-tag-get-attribute (car mb) :typemodifiers))) + (setq mods (cons "extern" (cons "\"C\"" mods))) + (semantic-tag-put-attribute (car mb) :typemodifiers mods)) + (setq mb (cdr mb))) + (setq return-list ret))) + + ;; Function or variables that have a :type that is some complex + ;; thing, extract it, and replace it with a reference. + ;; + ;; Thus, struct A { int a; } B; + ;; + ;; will create 2 toplevel tags, one is type A, and the other variable B + ;; where the :type of B is just a type tag A that is a prototype, and + ;; the actual struct info of A is it's own toplevel tag. + (when (or (semantic-tag-of-class-p tag 'function) + (semantic-tag-of-class-p tag 'variable)) + (let* ((basetype (semantic-tag-type tag)) + (typeref nil) + (tname (when (consp basetype) + (semantic-tag-name basetype)))) + ;; Make tname be a string. + (when (consp tname) (setq tname (car (car tname)))) + ;; Is the basetype a full type with a name of its own? + (when (and basetype (semantic-tag-p basetype) + (not (semantic-tag-prototype-p basetype)) + tname + (not (string= tname ""))) + ;; a type tag referencing the type we are extracting. + (setq typeref (semantic-tag-new-type + (semantic-tag-name basetype) + (semantic-tag-type basetype) + nil nil + :prototype t)) + ;; Convert original tag to only have a reference. + (setq tag (semantic-tag-copy tag)) + (semantic-tag-put-attribute tag :type typeref) + ;; Convert basetype to have the location information. + (semantic--tag-copy-properties tag basetype) + (semantic--tag-set-overlay basetype + (semantic-tag-overlay tag)) + ;; Store the base tag as part of the return list. + (setq return-list (cons basetype return-list))))) + + ;; Name of the tag is a list, so expand it. Tag lists occur + ;; for variables like this: int var1, var2, var3; + ;; + ;; This will expand that to 3 tags that happen to share the + ;; same overlay information. + (if (consp (semantic-tag-name tag)) + (let ((rl (semantic-expand-c-tag-namelist tag))) + (cond + ;; If this returns nothing, then return nil overall + ;; because that will restore the old TAG input. + ((not rl) (setq return-list nil)) + ;; If we have a return, append it to the existing list + ;; of returns. + ((consp rl) + (setq return-list (append rl return-list))) + )) + ;; If we didn't have a list, but the return-list is non-empty, + ;; that means we still need to take our existing tag, and glom + ;; it onto our extracted type. + (if (consp return-list) + (setq return-list (cons tag return-list))) + ) + + ;; Default, don't change the tag means returning nil. + return-list)) + +(defun semantic-expand-c-tag-namelist (tag) + "Expand TAG whose name is a list into a list of tags, or nil." + (cond ((semantic-tag-of-class-p tag 'variable) + ;; The name part comes back in the form of: + ;; ( NAME NUMSTARS BITS ARRAY ASSIGN ) + (let ((vl nil) + (basety (semantic-tag-type tag)) + (ty "") + (mods (semantic-tag-get-attribute tag :typemodifiers)) + (suffix "") + (lst (semantic-tag-name tag)) + (default nil) + (cur nil)) + ;; Open up each name in the name list. + (while lst + (setq suffix "" ty "") + (setq cur (car lst)) + (if (nth 2 cur) + (setq suffix (concat ":" (nth 2 cur)))) + (if (= (length basety) 1) + (setq ty (car basety)) + (setq ty basety)) + (setq default (nth 4 cur)) + (setq vl (cons + (semantic-tag-new-variable + (car cur) ;name + ty ;type + (if default + (buffer-substring-no-properties + (car default) (car (cdr default)))) + :constant-flag (semantic-tag-variable-constant-p tag) + :suffix suffix + :typemodifiers mods + :dereference (length (nth 3 cur)) + :pointer (nth 1 cur) + :reference (semantic-tag-get-attribute tag :reference) + :documentation (semantic-tag-docstring tag) ;doc + ) + vl)) + (semantic--tag-copy-properties tag (car vl)) + (semantic--tag-set-overlay (car vl) + (semantic-tag-overlay tag)) + (setq lst (cdr lst))) + ;; Return the list + (nreverse vl))) + ((semantic-tag-of-class-p tag 'type) + ;; We may someday want to add an extra check for a type + ;; of type "typedef". + ;; Each elt of NAME is ( STARS NAME ) + (let ((vl nil) + (names (semantic-tag-name tag))) + (while names + (setq vl (cons (semantic-tag-new-type + (nth 1 (car names)) ; name + "typedef" + (semantic-tag-type-members tag) + ;; parent is just tbe name of what + ;; is passed down as a tag. + (list + (semantic-tag-name + (semantic-tag-type-superclasses tag))) + :pointer + (let ((stars (car (car (car names))))) + (if (= stars 0) nil stars)) + ;; This specifies what the typedef + ;; is expanded out as. Just the + ;; name shows up as a parent of this + ;; typedef. + :typedef + (semantic-tag-get-attribute tag :superclasses) + ;;(semantic-tag-type-superclasses tag) + :documentation + (semantic-tag-docstring tag)) + vl)) + (semantic--tag-copy-properties tag (car vl)) + (semantic--tag-set-overlay (car vl) + (semantic-tag-overlay tag)) + (setq names (cdr names))) + vl)) + ((and (listp (car tag)) + (semantic-tag-of-class-p (car tag) 'variable)) + ;; Argument lists come in this way. Append all the expansions! + (let ((vl nil)) + (while tag + (setq vl (append (semantic-tag-components (car vl)) + vl) + tag (cdr tag))) + vl)) + (t nil))) + +(defvar-mode-local c-mode semantic-tag-expand-function 'semantic-expand-c-tag + "Function used to expand tags generated in the C bovine parser.") + +(defvar semantic-c-classname nil + "At parse time, assign a class or struct name text here. +It is picked up by `semantic-c-reconstitute-token' to determine +if something is a constructor. Value should be: + ( TYPENAME . TYPEOFTYPE) +where typename is the name of the type, and typeoftype is \"class\" +or \"struct\".") + +(defun semantic-c-reconstitute-token (tokenpart declmods typedecl) + "Reconstitute a token TOKENPART with DECLMODS and TYPEDECL. +This is so we don't have to match the same starting text several times. +Optional argument STAR and REF indicate the number of * and & in the typedef." + (when (and (listp typedecl) + (= 1 (length typedecl)) + (stringp (car typedecl))) + (setq typedecl (car typedecl))) + (cond ((eq (nth 1 tokenpart) 'variable) + (semantic-tag-new-variable + (car tokenpart) + (or typedecl "int") ;type + nil ;default value (filled with expand) + :constant-flag (if (member "const" declmods) t nil) + :typemodifiers (delete "const" declmods) + ) + ) + ((eq (nth 1 tokenpart) 'function) + ;; We should look at part 4 (the arglist) here, and throw an + ;; error of some sort if it contains parser errors so that we + ;; don't parser function calls, but that is a little beyond what + ;; is available for data here. + (let* ((constructor + (and (or (and semantic-c-classname + (string= (car semantic-c-classname) + (car tokenpart))) + (and (stringp (car (nth 2 tokenpart))) + (string= (car (nth 2 tokenpart)) (car tokenpart))) + ) + (not (car (nth 3 tokenpart))))) + (fcnpointer (string-match "^\\*" (car tokenpart))) + (fnname (if fcnpointer + (substring (car tokenpart) 1) + (car tokenpart))) + (operator (if (string-match "[a-zA-Z]" fnname) + nil + t)) + ) + (if fcnpointer + ;; Function pointers are really variables. + (semantic-tag-new-variable + fnname + typedecl + nil + ;; It is a function pointer + :functionpointer-flag t + ) + ;; The function + (semantic-tag-new-function + fnname + (or typedecl ;type + (cond ((car (nth 3 tokenpart) ) + "void") ; Destructors have no return? + (constructor + ;; Constructors return an object. + (semantic-tag-new-type + ;; name + (or (car semantic-c-classname) + (car (nth 2 tokenpart))) + ;; type + (or (cdr semantic-c-classname) + "class") + ;; members + nil + ;; parents + nil + )) + (t "int"))) + (nth 4 tokenpart) ;arglist + :constant-flag (if (member "const" declmods) t nil) + :typemodifiers (delete "const" declmods) + :parent (car (nth 2 tokenpart)) + :destructor-flag (if (car (nth 3 tokenpart) ) t) + :constructor-flag (if constructor t) + :pointer (nth 7 tokenpart) + :operator-flag operator + ;; Even though it is "throw" in C++, we use + ;; `throws' as a common name for things that toss + ;; exceptions about. + :throws (nth 5 tokenpart) + ;; Reemtrant is a C++ thingy. Add it here + :reentrant-flag (if (member "reentrant" (nth 6 tokenpart)) t) + ;; A function post-const is funky. Try stuff + :methodconst-flag (if (member "const" (nth 6 tokenpart)) t) + ;; prototypes are functions w/ no body + :prototype-flag (if (nth 8 tokenpart) t) + ;; Pure virtual + :pure-virtual-flag (if (eq (nth 8 tokenpart) :pure-virtual-flag) t) + ;; Template specifier. + :template-specifier (nth 9 tokenpart) + ))) + ) + )) + +(defun semantic-c-reconstitute-template (tag specifier) + "Reconstitute the token TAG with the template SPECIFIER." + (semantic-tag-put-attribute tag :template (or specifier "")) + tag) + + +;;; Override methods & Variables +;; +(define-mode-local-override semantic-format-tag-name + c-mode (tag &optional parent color) + "Convert TAG to a string that is the print name for TAG. +Optional PARENT and COLOR are ignored." + (let ((name (semantic-format-tag-name-default tag parent color)) + (fnptr (semantic-tag-get-attribute tag :functionpointer-flag)) + ) + (if (not fnptr) + name + (concat "(*" name ")")) + )) + +(define-mode-local-override semantic-format-tag-canonical-name + c-mode (tag &optional parent color) + "Create a cannonical name for TAG. +PARENT specifies a parent class. +COLOR indicates that the text should be type colorized. +Enhances the base class to search for the entire parent +tree to make the name accurate." + (semantic-format-tag-canonical-name-default tag parent color) + ) + +(define-mode-local-override semantic-format-tag-type c-mode (tag color) + "Convert the data type of TAG to a string usable in tag formatting. +Adds pointer and reference symbols to the default. +Argument COLOR adds color to the text." + (let* ((type (semantic-tag-type tag)) + (defaulttype nil) + (point (semantic-tag-get-attribute tag :pointer)) + (ref (semantic-tag-get-attribute tag :reference)) + ) + (if (semantic-tag-p type) + (let ((typetype (semantic-tag-type type)) + (typename (semantic-tag-name type))) + ;; Create the string that expresses the type + (if (string= typetype "class") + (setq defaulttype typename) + (setq defaulttype (concat typetype " " typename)))) + (setq defaulttype (semantic-format-tag-type-default tag color))) + + ;; Colorize + (when color + (setq defaulttype (semantic--format-colorize-text defaulttype 'type))) + + ;; Add refs, ptrs, etc + (if ref (setq ref "&")) + (if point (setq point (make-string point ?*)) "") + (when type + (concat defaulttype ref point)) + )) + +(define-mode-local-override semantic-find-tags-by-scope-protection + c-mode (scopeprotection parent &optional table) + "Override the usual search for protection. +We can be more effective than the default by scanning through once, +and collecting tags based on the labels we see along the way." + (if (not table) (setq table (semantic-tag-type-members parent))) + (if (null scopeprotection) + table + (let ((ans nil) + (curprot 1) + (targetprot (cond ((eq scopeprotection 'public) + 1) + ((eq scopeprotection 'protected) + 2) + (t 3) + )) + (alist '(("public" . 1) + ("protected" . 2) + ("private" . 3))) + ) + (dolist (tag table) + (cond + ((semantic-tag-of-class-p tag 'label) + (setq curprot (cdr (assoc (semantic-tag-name tag) alist))) + ) + ((>= targetprot curprot) + (setq ans (cons tag ans))) + )) + ans))) + +(define-mode-local-override semantic-tag-protection + c-mode (tag &optional parent) + "Return the protection of TAG in PARENT. +Override function for `semantic-tag-protection'." + (let ((mods (semantic-tag-modifiers tag)) + (prot nil)) + ;; Check the modifiers for protection if we are not a child + ;; of some class type. + (when (or (not parent) (not (eq (semantic-tag-class parent) 'type))) + (while (and (not prot) mods) + (if (stringp (car mods)) + (let ((s (car mods))) + ;; A few silly defaults to get things started. + (cond ((or (string= s "extern") + (string= s "export")) + 'public) + ((string= s "static") + 'private)))) + (setq mods (cdr mods)))) + ;; If we have a typed parent, look for :public style labels. + (when (and parent (eq (semantic-tag-class parent) 'type)) + (let ((pp (semantic-tag-type-members parent))) + (while (and pp (not (semantic-equivalent-tag-p (car pp) tag))) + (when (eq (semantic-tag-class (car pp)) 'label) + (setq prot + (cond ((string= (semantic-tag-name (car pp)) "public") + 'public) + ((string= (semantic-tag-name (car pp)) "private") + 'private) + ((string= (semantic-tag-name (car pp)) "protected") + 'protected))) + ) + (setq pp (cdr pp))))) + (when (and (not prot) (eq (semantic-tag-class parent) 'type)) + (setq prot + (cond ((string= (semantic-tag-type parent) "class") 'private) + ((string= (semantic-tag-type parent) "struct") 'public) + (t 'unknown)))) + (or prot + (if (and parent (semantic-tag-of-class-p parent 'type)) + 'public + nil)))) + +(define-mode-local-override semantic-tag-components c-mode (tag) + "Return components for TAG." + (if (and (eq (semantic-tag-class tag) 'type) + (string= (semantic-tag-type tag) "typedef")) + ;; A typedef can contain a parent who has positional children, + ;; but that parent will not have a position. Do this funny hack + ;; to make sure we can apply overlays properly. + (let ((sc (semantic-tag-get-attribute tag :typedef))) + (when (semantic-tag-p sc) (semantic-tag-components sc))) + (semantic-tag-components-default tag))) + +(defun semantic-c-tag-template (tag) + "Return the template specification for TAG, or nil." + (semantic-tag-get-attribute tag :template)) + +(defun semantic-c-tag-template-specifier (tag) + "Return the template specifier specification for TAG, or nil." + (semantic-tag-get-attribute tag :template-specifier)) + +(defun semantic-c-template-string-body (templatespec) + "Convert TEMPLATESPEC into a string. +This might be a string, or a list of tokens." + (cond ((stringp templatespec) + templatespec) + ((semantic-tag-p templatespec) + (semantic-format-tag-abbreviate templatespec)) + ((listp templatespec) + (mapconcat 'semantic-format-tag-abbreviate templatespec ", ")))) + +(defun semantic-c-template-string (token &optional parent color) + "Return a string representing the TEMPLATE attribute of TOKEN. +This string is prefixed with a space, or is the empty string. +Argument PARENT specifies a parent type. +Argument COLOR specifies that the string should be colorized." + (let ((t2 (semantic-c-tag-template-specifier token)) + (t1 (semantic-c-tag-template token)) + ;; @todo - Need to account for a parent that is a template + (pt1 (if parent (semantic-c-tag-template parent))) + (pt2 (if parent (semantic-c-tag-template-specifier parent))) + ) + (cond (t2 ;; we have a template with specifier + (concat " <" + ;; Fill in the parts here + (semantic-c-template-string-body t2) + ">")) + (t1 ;; we have a template without specifier + " <>") + (t + "")))) + +(define-mode-local-override semantic-format-tag-concise-prototype + c-mode (token &optional parent color) + "Return an abbreviated string describing TOKEN for C and C++. +Optional PARENT and COLOR as specified with +`semantic-format-tag-abbreviate-default'." + ;; If we have special template things, append. + (concat (semantic-format-tag-concise-prototype-default token parent color) + (semantic-c-template-string token parent color))) + +(define-mode-local-override semantic-format-tag-uml-prototype + c-mode (token &optional parent color) + "Return an uml string describing TOKEN for C and C++. +Optional PARENT and COLOR as specified with +`semantic-abbreviate-tag-default'." + ;; If we have special template things, append. + (concat (semantic-format-tag-uml-prototype-default token parent color) + (semantic-c-template-string token parent color))) + +(define-mode-local-override semantic-tag-abstract-p + c-mode (tag &optional parent) + "Return non-nil if TAG is considered abstract. +PARENT is tag's parent. +In C, a method is abstract if it is `virtual', which is already +handled. A class is abstract iff it's destructor is virtual." + (cond + ((eq (semantic-tag-class tag) 'type) + (require 'semantic/find) + (or (semantic-brute-find-tag-by-attribute :pure-virtual-flag + (semantic-tag-components tag) + ) + (let* ((ds (semantic-brute-find-tag-by-attribute + :destructor-flag + (semantic-tag-components tag) + )) + (cs (semantic-brute-find-tag-by-attribute + :constructor-flag + (semantic-tag-components tag) + ))) + (and ds (member "virtual" (semantic-tag-modifiers (car ds))) + cs (eq 'protected (semantic-tag-protection (car cs) tag)) + ) + ))) + ((eq (semantic-tag-class tag) 'function) + (or (semantic-tag-get-attribute tag :pure-virtual-flag) + (member "virtual" (semantic-tag-modifiers tag)))) + (t (semantic-tag-abstract-p-default tag parent)))) + +(defun semantic-c-dereference-typedef (type scope &optional type-declaration) + "If TYPE is a typedef, get TYPE's type by name or tag, and return. +SCOPE is not used, and TYPE-DECLARATION is used only if TYPE is not a typedef." + (if (and (eq (semantic-tag-class type) 'type) + (string= (semantic-tag-type type) "typedef")) + (let ((dt (semantic-tag-get-attribute type :typedef))) + (cond ((and (semantic-tag-p dt) + (not (semantic-analyze-tag-prototype-p dt))) + ;; In this case, DT was declared directly. We need + ;; to clone DT and apply a filename to it. + (let* ((fname (semantic-tag-file-name type)) + (def (semantic-tag-copy dt nil fname))) + (list def def))) + ((stringp dt) (list dt (semantic-tag dt 'type))) + ((consp dt) (list (car dt) dt)))) + + (list type type-declaration))) + +(defun semantic-c--instantiate-template (tag def-list spec-list) + "Replace TAG name according to template specification. +DEF-LIST is the template information. +SPEC-LIST is the template specifier of the datatype instantiated." + (when (and (car def-list) (car spec-list)) + + (when (and (string= (semantic-tag-type (car def-list)) "class") + (string= (semantic-tag-name tag) (semantic-tag-name (car def-list)))) + (semantic-tag-set-name tag (semantic-tag-name (car spec-list)))) + + (semantic-c--instantiate-template tag (cdr def-list) (cdr spec-list)))) + +(defun semantic-c--template-name-1 (spec-list) + "return a string used to compute template class name based on SPEC-LIST +for ref<Foo,Bar> it will return 'Foo,Bar'." + (when (car spec-list) + (let* ((endpart (semantic-c--template-name-1 (cdr spec-list))) + (separator (and endpart ","))) + (concat (semantic-tag-name (car spec-list)) separator endpart)))) + +(defun semantic-c--template-name (type spec-list) + "Return a template class name for TYPE based on SPEC-LIST. +For a type `ref' with a template specifier of (Foo Bar) it will +return 'ref<Foo,Bar>'." + (concat (semantic-tag-name type) + "<" (semantic-c--template-name-1 (cdr spec-list)) ">")) + +(defun semantic-c-dereference-template (type scope &optional type-declaration) + "Dereference any template specifieres in TYPE within SCOPE. +If TYPE is a template, return a TYPE copy with the templates types +instantiated as specified in TYPE-DECLARATION." + (when (semantic-tag-p type-declaration) + (let ((def-list (semantic-tag-get-attribute type :template)) + (spec-list (semantic-tag-get-attribute type-declaration :template-specifier))) + (when (and def-list spec-list) + (setq type (semantic-tag-deep-copy-one-tag + type + (lambda (tag) + (when (semantic-tag-of-class-p tag 'type) + (semantic-c--instantiate-template + tag def-list spec-list)) + tag) + )) + (semantic-tag-set-name type (semantic-c--template-name type spec-list)) + (semantic-tag-put-attribute type :template nil) + (semantic-tag-set-faux type)))) + (list type type-declaration)) + +;;; Patch here by "Raf" for instantiating templates. +(defun semantic-c-dereference-member-of (type scope &optional type-declaration) + "Dereference through the `->' operator of TYPE. +Uses the return type of the '->' operator if it is contained in TYPE. +SCOPE is the current local scope to perform searches in. +TYPE-DECLARATION is passed through." + (if semantic-c-member-of-autocast + (let ((operator (car (semantic-find-tags-by-name "->" (semantic-analyze-scoped-type-parts type))))) + (if operator + (list (semantic-tag-get-attribute operator :type) (semantic-tag-get-attribute operator :type)) + (list type type-declaration))) + (list type type-declaration))) + +;; David Engster: The following three functions deal with namespace +;; aliases and types which are member of a namespace through a using +;; statement. For examples, see the file semantic/tests/testusing.cpp, +;; tests 5 and following. + +(defun semantic-c-dereference-namespace (type scope &optional type-declaration) + "Dereference namespace which might hold an 'alias' for TYPE. +Such an alias can be created through 'using' statements in a +namespace declaration. This function checks the namespaces in +SCOPE for such statements." + (let ((scopetypes (oref scope scopetypes)) + typename currentns tmp usingname result namespaces) + (when (and (semantic-tag-p type-declaration) + (or (null type) (semantic-tag-prototype-p type))) + (setq typename (semantic-analyze-split-name (semantic-tag-name type-declaration))) + ;; If we already have that TYPE in SCOPE, we do nothing + (unless (semantic-deep-find-tags-by-name (or (car-safe typename) typename) scopetypes) + (if (stringp typename) + ;; The type isn't fully qualified, so we have to search in all namespaces in SCOPE. + (setq namespaces (semantic-find-tags-by-type "namespace" scopetypes)) + ;; This is a fully qualified name, so we only have to search one namespace. + (setq namespaces (semanticdb-typecache-find (car typename))) + ;; Make sure it's really a namespace. + (if (string= (semantic-tag-type namespaces) "namespace") + (setq namespaces (list namespaces)) + (setq namespaces nil))) + (setq result nil) + ;; Iterate over all the namespaces we have to check. + (while (and namespaces + (null result)) + (setq currentns (car namespaces)) + ;; Check if this is namespace is an alias and dereference it if necessary. + (setq result (semantic-c-dereference-namespace-alias type-declaration currentns)) + (unless result + ;; Otherwise, check if we can reach the type through 'using' statements. + (setq result + (semantic-c-check-type-namespace-using type-declaration currentns))) + (setq namespaces (cdr namespaces))))) + (if result + ;; we have found the original type + (list result result) + (list type type-declaration)))) + +(defun semantic-c-dereference-namespace-alias (type namespace) + "Dereference TYPE in NAMESPACE, given that NAMESPACE is an alias. +Checks if NAMESPACE is an alias and if so, returns a new type +with a fully qualified name in the original namespace. Returns +nil if NAMESPACE is not an alias." + (when (eq (semantic-tag-get-attribute namespace :kind) 'alias) + (let ((typename (semantic-analyze-split-name (semantic-tag-name type))) + ns nstype originaltype newtype) + ;; Make typename unqualified + (if (listp typename) + (setq typename (last typename)) + (setq typename (list typename))) + (when + (and + ;; Get original namespace and make sure TYPE exists there. + (setq ns (semantic-tag-name + (car (semantic-tag-get-attribute namespace :members)))) + (setq nstype (semanticdb-typecache-find ns)) + (setq originaltype (semantic-find-tags-by-name + (car typename) + (semantic-tag-get-attribute nstype :members)))) + ;; Construct new type with name in original namespace. + (setq ns (semantic-analyze-split-name ns)) + (setq newtype + (semantic-tag-clone + (car originaltype) + (semantic-analyze-unsplit-name + (if (listp ns) + (append ns typename) + (append (list ns) typename))))))))) + +;; This searches a type in a namespace, following through all using +;; statements. +(defun semantic-c-check-type-namespace-using (type namespace) + "Check if TYPE is accessible in NAMESPACE through a using statement. +Returns the original type from the namespace where it is defined, +or nil if it cannot be found." + (let (usings result usingname usingtype unqualifiedname members shortname tmp) + ;; Get all using statements from NAMESPACE. + (when (and (setq usings (semantic-tag-get-attribute namespace :members)) + (setq usings (semantic-find-tags-by-class 'using usings))) + ;; Get unqualified typename. + (when (listp (setq unqualifiedname (semantic-analyze-split-name + (semantic-tag-name type)))) + (setq unqualifiedname (car (last unqualifiedname)))) + ;; Iterate over all using statements in NAMESPACE. + (while (and usings + (null result)) + (setq usingname (semantic-analyze-split-name + (semantic-tag-name (car usings))) + usingtype (semantic-tag-type (semantic-tag-type (car usings)))) + (cond + ((or (string= usingtype "namespace") + (stringp usingname)) + ;; We are dealing with a 'using [namespace] NAMESPACE;' + ;; Search for TYPE in that namespace + (setq result + (semanticdb-typecache-find usingname)) + (if (and result + (setq members (semantic-tag-get-attribute result :members)) + (setq members (semantic-find-tags-by-name unqualifiedname members))) + ;; TYPE is member of that namespace, so we are finished + (setq result (car members)) + ;; otherwise recursively search in that namespace for an alias + (setq result (semantic-c-check-type-namespace-using type result)) + (when result + (setq result (semantic-tag-type result))))) + ((and (string= usingtype "class") + (listp usingname)) + ;; We are dealing with a 'using TYPE;' + (when (string= unqualifiedname (car (last usingname))) + ;; We have found the correct tag. + (setq result (semantic-tag-type (car usings)))))) + (setq usings (cdr usings)))) + result)) + + +(define-mode-local-override semantic-analyze-dereference-metatype + c-mode (type scope &optional type-declaration) + "Dereference TYPE as described in `semantic-analyze-dereference-metatype'. +Handle typedef, template instantiation, and '->' operator." + (let* ((dereferencer-list '(semantic-c-dereference-typedef + semantic-c-dereference-template + semantic-c-dereference-member-of + semantic-c-dereference-namespace)) + (dereferencer (pop dereferencer-list)) + (type-tuple) + (original-type type)) + (while dereferencer + (setq type-tuple (funcall dereferencer type scope type-declaration) + type (car type-tuple) + type-declaration (cadr type-tuple)) + (if (not (eq type original-type)) + ;; we found a new type so break the dereferencer loop now ! + ;; (we will be recalled with the new type expanded by + ;; semantic-analyze-dereference-metatype-stack). + (setq dereferencer nil) + ;; no new type found try the next dereferencer : + (setq dereferencer (pop dereferencer-list))))) + (list type type-declaration)) + +(define-mode-local-override semantic-analyze-type-constants c-mode (type) + "When TYPE is a tag for an enum, return it's parts. +These are constants which are of type TYPE." + (if (and (eq (semantic-tag-class type) 'type) + (string= (semantic-tag-type type) "enum")) + (semantic-tag-type-members type))) + +(define-mode-local-override semantic-analyze-split-name c-mode (name) + "Split up tag names on colon (:) boundaries." + (let ((ans (split-string name ":"))) + (if (= (length ans) 1) + name + (delete "" ans)))) + +(define-mode-local-override semantic-analyze-unsplit-name c-mode (namelist) + "Assemble the list of names NAMELIST into a namespace name." + (mapconcat 'identity namelist "::")) + +(define-mode-local-override semantic-ctxt-scoped-types c++-mode (&optional point) + "Return a list of tags of CLASS type based on POINT. +DO NOT return the list of tags encompassing point." + (when point (goto-char (point))) + (let ((tagsaroundpoint (semantic-find-tag-by-overlay)) + (tagreturn nil) + (tmp nil)) + ;; In C++, we want to find all the namespaces declared + ;; locally and add them to the list. + (setq tmp (semantic-find-tags-by-class 'type (current-buffer))) + (setq tmp (semantic-find-tags-by-type "namespace" tmp)) + (setq tmp (semantic-find-tags-by-name "unnamed" tmp)) + (setq tagreturn tmp) + ;; We should also find all "using" type statements and + ;; accept those entities in as well. + (setq tmp (semanticdb-find-tags-by-class 'using)) + (let ((idx 0) + (len (semanticdb-find-result-length tmp))) + (while (< idx len) + (setq tagreturn (cons (semantic-tag-type (car (semanticdb-find-result-nth tmp idx))) tagreturn)) + (setq idx (1+ idx))) + ) + ;; Use the encompased types around point to also look for using statements. + ;;(setq tagreturn (cons "bread_name" tagreturn)) + (while (cdr tagsaroundpoint) ; don't search the last one + (setq tmp (semantic-find-tags-by-class 'using (semantic-tag-components (car tagsaroundpoint)))) + (dolist (T tmp) + (setq tagreturn (cons (semantic-tag-type T) tagreturn)) + ) + (setq tagsaroundpoint (cdr tagsaroundpoint)) + ) + ;; If in a function... + (when (and (semantic-tag-of-class-p (car tagsaroundpoint) 'function) + ;; ...search for using statements in the local scope... + (setq tmp (semantic-find-tags-by-class + 'using + (semantic-get-local-variables)))) + ;; ... and add them. + (setq tagreturn + (append tagreturn + (mapcar 'semantic-tag-type tmp)))) + ;; Return the stuff + tagreturn + )) + +(define-mode-local-override semantic-get-local-variables c++-mode () + "Do what `semantic-get-local-variables' does, plus add `this' if needed." + (let* ((origvar (semantic-get-local-variables-default)) + (ct (semantic-current-tag)) + (p (semantic-tag-function-parent ct))) + ;; If we have a function parent, then that implies we can + (if (and p (semantic-tag-of-class-p ct 'function)) + ;; Append a new tag THIS into our space. + (cons (semantic-tag-new-variable "this" p nil) + origvar) + ;; No parent, just return the usual + origvar) + )) + +(define-mode-local-override semantic-idle-summary-current-symbol-info + c-mode () + "Handle the SPP keywords, then use the default mechanism." + (let* ((sym (car (semantic-ctxt-current-thing))) + (spp-sym (semantic-lex-spp-symbol sym))) + (if spp-sym + (let* ((txt (concat "Macro: " sym)) + (sv (symbol-value spp-sym)) + (arg (semantic-lex-spp-macro-with-args sv)) + ) + (when arg + (setq txt (concat txt (format "%S" arg))) + (setq sv (cdr sv))) + + ;; This is optional, and potentially fraught w/ errors. + (condition-case nil + (dolist (lt sv) + (setq txt (concat txt " " (semantic-lex-token-text lt)))) + (error (setq txt (concat txt " #error in summary fcn")))) + + txt) + (semantic-idle-summary-current-symbol-info-default)))) + +(defvar-mode-local c-mode semantic-orphaned-member-metaparent-type "struct" + "When lost memberes are found in the class hierarchy generator, use a struct.") + +(defvar-mode-local c-mode semantic-symbol->name-assoc-list + '((type . "Types") + (variable . "Variables") + (function . "Functions") + (include . "Includes") + ) + "List of tag classes, and strings to describe them.") + +(defvar-mode-local c-mode semantic-symbol->name-assoc-list-for-type-parts + '((type . "Types") + (variable . "Attributes") + (function . "Methods") + (label . "Labels") + ) + "List of tag classes in a datatype decl, and strings to describe them.") + +(defvar-mode-local c-mode imenu-create-index-function 'semantic-create-imenu-index + "Imenu index function for C.") + +(defvar-mode-local c-mode semantic-type-relation-separator-character + '("." "->" "::") + "Separator characters between something of a given type, and a field.") + +(defvar-mode-local c-mode semantic-command-separation-character ";" + "Commen separation character for C") + +(defvar-mode-local c-mode senator-step-at-tag-classes '(function variable) + "Tag classes where senator will stop at the end.") + +;;;###autoload +(defun semantic-default-c-setup () + "Set up a buffer for semantic parsing of the C language." + (semantic-c-by--install-parser) + (setq semantic-lex-syntax-modifications '((?> ".") + (?< ".") + ) + ) + + (setq semantic-lex-analyzer #'semantic-c-lexer) + (add-hook 'semantic-lex-reset-hooks 'semantic-lex-spp-reset-hook nil t) + ) + +;;;###autoload +(defun semantic-c-add-preprocessor-symbol (sym replacement) + "Add a preprocessor symbol SYM with a REPLACEMENT value." + (interactive "sSymbol: \nsReplacement: ") + (let ((SA (assoc sym semantic-lex-c-preprocessor-symbol-map))) + (if SA + ;; Replace if there is one. + (setcdr SA replacement) + ;; Otherwise, append + (setq semantic-lex-c-preprocessor-symbol-map + (cons (cons sym replacement) + semantic-lex-c-preprocessor-symbol-map)))) + + (semantic-c-reset-preprocessor-symbol-map) + ) + +;;; SETUP QUERY +;; +(defun semantic-c-describe-environment () + "Describe the Semantic features of the current C environment." + (interactive) + (if (not (or (eq major-mode 'c-mode) (eq major-mode 'c++-mode))) + (error "Not useful to query C mode in %s mode" major-mode)) + (let ((gcc (when (boundp 'semantic-gcc-setup-data) + semantic-gcc-setup-data)) + ) + (semantic-fetch-tags) + + (with-output-to-temp-buffer "*Semantic C Environment*" + (when gcc + (princ "Calculated GCC Parameters:") + (dolist (P gcc) + (princ "\n ") + (princ (car P)) + (princ " = ") + (princ (cdr P)) + ) + ) + + (princ "\n\nInclude Path Summary:\n") + (when (and (boundp 'ede-object) ede-object) + (princ "\n This file's project include is handled by:\n") + (princ " ") + (princ (object-print ede-object)) + (princ "\n with the system path:\n") + (dolist (dir (ede-system-include-path ede-object)) + (princ " ") + (princ dir) + (princ "\n")) + ) + + (when semantic-dependency-include-path + (princ "\n This file's generic include path is:\n") + (dolist (dir semantic-dependency-include-path) + (princ " ") + (princ dir) + (princ "\n"))) + + (when semantic-dependency-system-include-path + (princ "\n This file's system include path is:\n") + (dolist (dir semantic-dependency-system-include-path) + (princ " ") + (princ dir) + (princ "\n"))) + + (princ "\n\nMacro Summary:\n") + (when semantic-lex-c-preprocessor-symbol-file + (princ "\n Your CPP table is primed from these files:\n") + (dolist (file semantic-lex-c-preprocessor-symbol-file) + (princ " ") + (princ file) + (princ "\n") + (princ " in table: ") + (princ (object-print (semanticdb-file-table-object file))) + (princ "\n") + )) + + (when semantic-lex-c-preprocessor-symbol-map-builtin + (princ "\n Built-in symbol map:\n") + (dolist (S semantic-lex-c-preprocessor-symbol-map-builtin) + (princ " ") + (princ (car S)) + (princ " = ") + (princ (cdr S)) + (princ "\n") + )) + + (when semantic-lex-c-preprocessor-symbol-map + (princ "\n User symbol map:\n") + (dolist (S semantic-lex-c-preprocessor-symbol-map) + (princ " ") + (princ (car S)) + (princ " = ") + (princ (cdr S)) + (princ "\n") + )) + + (princ "\n\n Use: M-x semantic-lex-spp-describe RET\n") + (princ "\n to see the complete macro table.\n") + + ))) + +(provide 'semantic/bovine/c) + +(semantic-c-reset-preprocessor-symbol-map) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/bovine/c" +;; End: + +;;; semantic/bovine/c.el ends here diff --git a/lisp/cedet/semantic/bovine/debug.el b/lisp/cedet/semantic/bovine/debug.el new file mode 100644 index 00000000000..cd54bf4ce07 --- /dev/null +++ b/lisp/cedet/semantic/bovine/debug.el @@ -0,0 +1,147 @@ +;;; semantic/bovine/debug.el --- Debugger support for bovinator + +;;; Copyright (C) 2003 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Implementation of the semantic debug support framework for the +;; bovine parser. +;; + +(require 'semantic/debug) +(require 'semantic/find) + +;;; Code: + +;;; Support a frame for the Bovinator +;; +(defclass semantic-bovine-debug-frame (semantic-debug-frame) + ((nonterm :initarg :nonterm + :type symbol + :documentation + "The name of the semantic nonterminal for this frame.") + (rule :initarg :rule + :type number + :documentation + "The index into NONTERM's rule list. 0 based.") + (match :initarg :match + :type number + :documentation + "The index into NONTERM's RULE's match. 0 based..") + (collection :initarg :collection + :type list + :documentation + "List of things matched so far.") + (lextoken :initarg :lextoken + :type list + :documentation + "A Token created by `semantic-lex-token'. +This is the lexical token being matched by the parser.") + ) + "Debugger frame representation for the bovinator.") + +(defun semantic-bovine-debug-create-frame (nonterm rule match collection + lextoken) + "Create one bovine frame. +NONTERM is the name of a rule we are currently parsing. +RULE is the index into the list of rules in NONTERM. +MATCH is the index into the list of matches in RULE. +For example: + this: that + | other thing + | here + ; +The NONTERM is THIS. +The RULE is for \"thing\" is 1. +The MATCH for \"thing\" is 1. +COLLECTION is a list of `things' that have been matched so far. +LEXTOKEN, is a token returned by the lexer which is being matched." + (let ((frame (semantic-bovine-debug-frame "frame" + :nonterm nonterm + :rule rule + :match match + :collection collection + :lextoken lextoken))) + (semantic-debug-set-frame semantic-debug-current-interface + frame) + frame)) + +(defmethod semantic-debug-frame-highlight ((frame semantic-debug-frame)) + "Highlight one parser frame." + (let* ((nonterm (oref frame nonterm)) + (pb (oref semantic-debug-current-interface parser-buffer)) + (start (semantic-brute-find-tag-by-class 'start pb)) + ) + ;; Make sure we get a good rule name, and that it is a string + (if (and (eq nonterm 'bovine-toplevel) start) + (setq nonterm (semantic-tag-name (car start))) + (setq nonterm (symbol-name nonterm))) + + (semantic-debug-highlight-rule semantic-debug-current-interface + nonterm + (oref frame rule) + (oref frame match)) + (semantic-debug-highlight-lexical-token semantic-debug-current-interface + (oref frame lextoken)) + )) + +(defmethod semantic-debug-frame-info ((frame semantic-debug-frame)) + "Display info about this one parser frame." + (message "%S" (oref frame collection)) + ) + +;;; Lisp error thrown frame. +;; +(defclass semantic-bovine-debug-error-frame (semantic-debug-frame) + ((condition :initarg :condition + :documentation + "An error condition caught in an action.") + ) + "Debugger frame representaion of a lisp error thrown during parsing.") + +(defun semantic-create-bovine-debug-error-frame (condition) + "Create an error frame for bovine debugger. +Argument CONDITION is the thrown error condition." + (let ((frame (semantic-bovine-debug-error-frame "frame" + :condition condition))) + (semantic-debug-set-frame semantic-debug-current-interface + frame) + frame)) + +(defmethod semantic-debug-frame-highlight ((frame semantic-bovine-debug-error-frame)) + "Highlight a frame from an action." + ;; How do I get the location of the action in the source buffer? + ) + +(defmethod semantic-debug-frame-info ((frame semantic-bovine-debug-error-frame)) + "Display info about the error thrown." + (message "Error: %S" (oref frame condition))) + +;;; Parser support for the debugger +;; +(defclass semantic-bovine-debug-parser (semantic-debug-parser) + ( + ) + "Represents a parser and its state.") + + +(provide 'semantic/bovine/debug) + +;;; semantic/bovine/debug.el ends here diff --git a/lisp/cedet/semantic/bovine/el.el b/lisp/cedet/semantic/bovine/el.el new file mode 100644 index 00000000000..596e29228f9 --- /dev/null +++ b/lisp/cedet/semantic/bovine/el.el @@ -0,0 +1,966 @@ +;;; semantic/bovine/el.el --- Semantic details for Emacs Lisp + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, +;;; 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Use the Semantic Bovinator for Emacs Lisp + +(require 'semantic) +(require 'semantic/bovine) +(require 'find-func) + +(require 'semantic/ctxt) +(require 'semantic/format) +(require 'thingatpt) + +;;; Code: + +;;; Lexer +;; +(define-lex semantic-emacs-lisp-lexer + "A simple lexical analyzer for Emacs Lisp. +This lexer ignores comments and whitespace, and will return +syntax as specified by the syntax table." + semantic-lex-ignore-whitespace + semantic-lex-ignore-newline + semantic-lex-number + semantic-lex-symbol-or-keyword + semantic-lex-charquote + semantic-lex-paren-or-list + semantic-lex-close-paren + semantic-lex-string + semantic-lex-ignore-comments + semantic-lex-punctuation + semantic-lex-default-action) + +;;; Parser +;; +(defvar semantic--elisp-parse-table + `((bovine-toplevel + (semantic-list + ,(lambda (vals start end) + (let ((tag (semantic-elisp-use-read (car vals)))) + (cond + ((and (listp tag) (semantic-tag-p (car tag))) + ;; We got a list of tags back. This list is + ;; returned here in the correct order, but this + ;; list gets reversed later, putting the correctly ordered + ;; items into reverse order later. + (nreverse tag)) + ((semantic--tag-expanded-p tag) + ;; At this point, if `semantic-elisp-use-read' returned an + ;; already expanded tag (from definitions parsed inside an + ;; eval and compile wrapper), just pass it! + tag) + (t + ;; We got the basics of a single tag. + (append tag (list start end)))))))) + ) + "Top level bovination table for elisp.") + +(defun semantic-elisp-desymbolify (arglist) + "Convert symbols to strings for ARGLIST." + (let ((out nil)) + (while arglist + (setq out + (cons + (if (symbolp (car arglist)) + (symbol-name (car arglist)) + (if (and (listp (car arglist)) + (symbolp (car (car arglist)))) + (symbol-name (car (car arglist))) + (format "%S" (car arglist)))) + out) + arglist (cdr arglist))) + (nreverse out))) + +(defun semantic-elisp-desymbolify-args (arglist) + "Convert symbols to strings for ARGLIST." + (let ((in (semantic-elisp-desymbolify arglist)) + (out nil)) + (dolist (T in) + (when (not (string-match "^&" T)) + (push T out))) + (nreverse out))) + +(defun semantic-elisp-clos-slot-property-string (slot property) + "For SLOT, a string representing PROPERTY." + (let ((p (member property slot))) + (if (not p) + nil + (setq p (cdr p)) + (cond + ((stringp (car p)) + (car p)) + ((or (symbolp (car p)) + (listp (car p)) + (numberp (car p))) + (format "%S" (car p))) + (t nil))))) + +(defun semantic-elisp-clos-args-to-semantic (partlist) + "Convert a list of CLOS class slot PARTLIST to `variable' tags." + (let (vars part v) + (while partlist + (setq part (car partlist) + partlist (cdr partlist) + v (semantic-tag-new-variable + (symbol-name (car part)) + (semantic-elisp-clos-slot-property-string part :type) + (semantic-elisp-clos-slot-property-string part :initform) + ;; Attributes + :protection (semantic-elisp-clos-slot-property-string + part :protection) + :static-flag (equal (semantic-elisp-clos-slot-property-string + part :allocation) + ":class") + :documentation (semantic-elisp-clos-slot-property-string + part :documentation)) + vars (cons v vars))) + (nreverse vars))) + +(defun semantic-elisp-form-to-doc-string (form) + "After reading a form FORM, covert it to a doc string. +For Emacs Lisp, sometimes that string is non-existant. +Sometimes it is a form which is evaluated at compile time, permitting +compound strings." + (cond ((stringp form) form) + ((and (listp form) (eq (car form) 'concat) + (stringp (nth 1 form))) + (nth 1 form)) + (t nil))) + +(defvar semantic-elisp-store-documentation-in-tag nil + "*When non-nil, store documentation strings in the created tags.") + +(defun semantic-elisp-do-doc (str) + "Return STR as a documentation string IF they are enabled." + (when semantic-elisp-store-documentation-in-tag + (semantic-elisp-form-to-doc-string str))) + +(defmacro semantic-elisp-setup-form-parser (parser &rest symbols) + "Install the function PARSER as the form parser for SYMBOLS. +SYMBOLS is a list of symbols identifying the forms to parse. +PARSER is called on every forms whose first element (car FORM) is +found in SYMBOLS. It is passed the parameters FORM, START, END, +where: + +- FORM is an Elisp form read from the current buffer. +- START and END are the beginning and end location of the + corresponding data in the current buffer." + (let ((sym (make-symbol "sym"))) + `(dolist (,sym ',symbols) + (put ,sym 'semantic-elisp-form-parser #',parser)))) +(put 'semantic-elisp-setup-form-parser 'lisp-indent-function 1) + +(defmacro semantic-elisp-reuse-form-parser (symbol &rest symbols) + "Reuse the form parser of SYMBOL for forms identified by SYMBOLS. +See also `semantic-elisp-setup-form-parser'." + (let ((parser (make-symbol "parser")) + (sym (make-symbol "sym"))) + `(let ((,parser (get ',symbol 'semantic-elisp-form-parser))) + (or ,parser + (signal 'wrong-type-argument + '(semantic-elisp-form-parser ,symbol))) + (dolist (,sym ',symbols) + (put ,sym 'semantic-elisp-form-parser ,parser))))) + +(defun semantic-elisp-use-read (sl) + "Use `read' on the semantic list SL. +Return a bovination list to use." + (let* ((start (car sl)) + (end (cdr sl)) + (form (read (buffer-substring-no-properties start end)))) + (cond + ;; If the first elt is a list, then it is some arbitrary code. + ((listp (car form)) + (semantic-tag-new-code "anonymous" nil) + ) + ;; A special form parser is provided, use it. + ((and (car form) (symbolp (car form)) + (get (car form) 'semantic-elisp-form-parser)) + (funcall (get (car form) 'semantic-elisp-form-parser) + form start end)) + ;; Produce a generic code tag by default. + (t + (semantic-tag-new-code (format "%S" (car form)) nil) + )))) + +;;; Form parsers +;; +(semantic-elisp-setup-form-parser + (lambda (form start end) + (semantic-tag-new-function + (symbol-name (nth 2 form)) + nil + '("form" "start" "end") + :form-parser t + )) + semantic-elisp-setup-form-parser) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((tags + (condition-case foo + (semantic-parse-region start end nil 1) + (error (message "MUNGE: %S" foo) + nil)))) + (if (semantic-tag-p (car-safe tags)) + tags + (semantic-tag-new-code (format "%S" (car form)) nil)))) + eval-and-compile + eval-when-compile + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (semantic-tag-new-function + (symbol-name (nth 1 form)) + nil + (semantic-elisp-desymbolify-args (nth 2 form)) + :user-visible-flag (eq (car-safe (nth 4 form)) 'interactive) + :documentation (semantic-elisp-do-doc (nth 3 form)) + :overloadable (or (eq (car form) 'define-overload) + (eq (car form) 'define-overloadable-function)) + )) + defun + defun* + defsubst + defmacro + define-overload ;; @todo - remove after cleaning up semantic. + define-overloadable-function + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((doc (semantic-elisp-form-to-doc-string (nth 3 form)))) + (semantic-tag-new-variable + (symbol-name (nth 1 form)) + nil + (nth 2 form) + :user-visible-flag (and doc + (> (length doc) 0) + (= (aref doc 0) ?*)) + :constant-flag (eq (car form) 'defconst) + :documentation (semantic-elisp-do-doc doc) + ))) + defvar + defconst + defcustom + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((doc (semantic-elisp-form-to-doc-string (nth 3 form)))) + (semantic-tag-new-variable + (symbol-name (nth 1 form)) + "face" + (nth 2 form) + :user-visible-flag (and doc + (> (length doc) 0) + (= (aref doc 0) ?*)) + :documentation (semantic-elisp-do-doc doc) + ))) + defface + ) + + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((doc (semantic-elisp-form-to-doc-string (nth 3 form)))) + (semantic-tag-new-variable + (symbol-name (nth 1 form)) + "image" + (nth 2 form) + :user-visible-flag (and doc + (> (length doc) 0) + (= (aref doc 0) ?*)) + :documentation (semantic-elisp-do-doc doc) + ))) + defimage + defezimage + ) + + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((doc (semantic-elisp-form-to-doc-string (nth 3 form)))) + (semantic-tag + (symbol-name (nth 1 form)) + 'customgroup + :value (nth 2 form) + :user-visible-flag t + :documentation (semantic-elisp-do-doc doc) + ))) + defgroup + ) + + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (semantic-tag-new-function + (symbol-name (cadr (cadr form))) + nil nil + :user-visible-flag (and (nth 4 form) + (not (eq (nth 4 form) 'nil))) + :prototype-flag t + :documentation (semantic-elisp-do-doc (nth 3 form)))) + autoload + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let* ((a2 (nth 2 form)) + (a3 (nth 3 form)) + (args (if (listp a2) a2 a3)) + (doc (nth (if (listp a2) 3 4) form))) + (semantic-tag-new-function + (symbol-name (nth 1 form)) + nil + (if (listp (car args)) + (cons (symbol-name (caar args)) + (semantic-elisp-desymbolify-args (cdr args))) + (semantic-elisp-desymbolify-args (cdr args))) + :parent (if (listp (car args)) (symbol-name (cadr (car args))) nil) + :documentation (semantic-elisp-do-doc doc) + ))) + defmethod + defgeneric + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (semantic-tag-new-function + (symbol-name (nth 1 form)) + nil + (semantic-elisp-desymbolify (nth 2 form)) + )) + defadvice + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((docpart (nthcdr 4 form))) + (semantic-tag-new-type + (symbol-name (nth 1 form)) + "class" + (semantic-elisp-clos-args-to-semantic (nth 3 form)) + (semantic-elisp-desymbolify (nth 2 form)) + :typemodifiers (semantic-elisp-desymbolify + (unless (stringp (car docpart)) docpart)) + :documentation (semantic-elisp-do-doc + (if (stringp (car docpart)) + (car docpart) + (cadr (member :documentation docpart)))) + ))) + defclass + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((slots (nthcdr 2 form))) + ;; Skip doc string if present. + (and (stringp (car slots)) + (setq slots (cdr slots))) + (semantic-tag-new-type + (symbol-name (if (consp (nth 1 form)) + (car (nth 1 form)) + (nth 1 form))) + "struct" + (semantic-elisp-desymbolify slots) + (cons nil nil) + ))) + defstruct + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (semantic-tag-new-function + (symbol-name (nth 1 form)) + nil nil + :lexical-analyzer-flag t + :documentation (semantic-elisp-do-doc (nth 2 form)) + )) + define-lex + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((args (nth 3 form))) + (semantic-tag-new-function + (symbol-name (nth 1 form)) + nil + (and (listp args) (semantic-elisp-desymbolify args)) + :override-function-flag t + :parent (symbol-name (nth 2 form)) + :documentation (semantic-elisp-do-doc (nth 4 form)) + ))) + define-mode-overload-implementation ;; obsoleted + define-mode-local-override + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (semantic-tag-new-variable + (symbol-name (nth 2 form)) + nil + (nth 3 form) ; default value + :override-variable-flag t + :parent (symbol-name (nth 1 form)) + :documentation (semantic-elisp-do-doc (nth 4 form)) + )) + defvar-mode-local + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((name (nth 1 form))) + (semantic-tag-new-include + (symbol-name (if (eq (car-safe name) 'quote) + (nth 1 name) + name)) + nil + :directory (nth 2 form)))) + require + ) + +(semantic-elisp-setup-form-parser + (lambda (form start end) + (let ((name (nth 1 form))) + (semantic-tag-new-package + (symbol-name (if (eq (car-safe name) 'quote) + (nth 1 name) + name)) + (nth 3 form)))) + provide + ) + +;;; Mode setup +;; +(define-mode-local-override semantic-dependency-tag-file + emacs-lisp-mode (tag) + "Find the file BUFFER depends on described by TAG." + (if (fboundp 'find-library-name) + (condition-case nil + ;; Try an Emacs 22 fcn. This throws errors. + (find-library-name (semantic-tag-name tag)) + (error + (message "semantic: connot find source file %s" + (semantic-tag-name tag)))) + ;; No handy function available. (Older Emacsen) + (let* ((lib (locate-library (semantic-tag-name tag))) + (name (if lib (file-name-sans-extension lib) nil)) + (nameel (concat name ".el"))) + (cond + ((and name (file-exists-p nameel)) nameel) + ((and name (file-exists-p (concat name ".el.gz"))) + ;; This is the linux distro case. + (concat name ".el.gz")) + ;; source file does not exists + (name + (message "semantic: cannot find source file %s" (concat name ".el"))) + (t + nil))))) + +;;; DOC Strings +;; +(defun semantic-emacs-lisp-overridable-doc (tag) + "Return the documentation string generated for overloadable functions. +Fetch the item for TAG. Only returns info about what symbols can be +used to perform the override." + (if (and (eq (semantic-tag-class tag) 'function) + (semantic-tag-get-attribute tag :overloadable)) + ;; Calc the doc to use for the overloadable symbols. + (overload-docstring-extension (intern (semantic-tag-name tag))) + "")) + +(defun semantic-emacs-lisp-obsoleted-doc (tag) + "Indicate that TAG is a new name that has obsoleted some old name. +Unfortunately, this requires that the tag in question has been loaded +into Emacs Lisp's memory." + (let ((obsoletethis (intern-soft (semantic-tag-name tag))) + (obsoletor nil)) + ;; This asks if our tag is available in the Emacs name space for querying. + (when obsoletethis + (mapatoms (lambda (a) + (let ((oi (get a 'byte-obsolete-info))) + (if (and oi (eq (car oi) obsoletethis)) + (setq obsoletor a))))) + (if obsoletor + (format "\n@obsolete{%s,%s}" obsoletor (semantic-tag-name tag)) + "")))) + +(define-mode-local-override semantic-documentation-for-tag + emacs-lisp-mode (tag &optional nosnarf) + "Return the documentation string for TAG. +Optional argument NOSNARF is ignored." + (let ((d (semantic-tag-docstring tag))) + (when (not d) + (cond ((semantic-tag-with-position-p tag) + ;; Doc isn't in the tag itself. Lets pull it out of the + ;; sources. + (let ((semantic-elisp-store-documentation-in-tag t)) + (setq tag (with-current-buffer (semantic-tag-buffer tag) + (goto-char (semantic-tag-start tag)) + (semantic-elisp-use-read + ;; concoct a lexical token. + (cons (semantic-tag-start tag) + (semantic-tag-end tag)))) + d (semantic-tag-docstring tag)))) + ;; The tag may be the result of a system search. + ((intern-soft (semantic-tag-name tag)) + (let ((sym (intern-soft (semantic-tag-name tag)))) + ;; Query into the global table o stuff. + (cond ((eq (semantic-tag-class tag) 'function) + (setq d (documentation sym))) + (t + (setq d (documentation-property + sym 'variable-documentation))))) + ;; Label it as system doc.. perhaps just for debugging + ;; purposes. + (if d (setq d (concat "Sytem Doc: \n" d))) + )) + ) + + (when d + (concat + (substitute-command-keys + (if (and (> (length d) 0) (= (aref d 0) ?*)) + (substring d 1) + d)) + (semantic-emacs-lisp-overridable-doc tag) + (semantic-emacs-lisp-obsoleted-doc tag))))) + +;;; Tag Features +;; +(define-mode-local-override semantic-tag-include-filename emacs-lisp-mode + (tag) + "Return the name of the tag with .el appended. +If there is a detail, prepend that directory." + (let ((name (semantic-tag-name tag)) + (detail (semantic-tag-get-attribute tag :directory))) + (concat (expand-file-name name detail) ".el"))) + +(define-mode-local-override semantic-insert-foreign-tag + emacs-lisp-mode (tag) + "Insert TAG at point. +Attempts a simple prototype for calling or using TAG." + (cond ((semantic-tag-of-class-p tag 'function) + (insert "(" (semantic-tag-name tag) " )") + (forward-char -1)) + (t + (insert (semantic-tag-name tag))))) + +(define-mode-local-override semantic-tag-protection + emacs-lisp-mode (tag &optional parent) + "Return the protection of TAG in PARENT. +Override function for `semantic-tag-protection'." + (let ((prot (semantic-tag-get-attribute tag :protection))) + (cond + ;; If a protection is not specified, AND there is a parent + ;; data type, then it is public. + ((and (not prot) parent) 'public) + ((string= prot ":public") 'public) + ((string= prot "public") 'public) + ((string= prot ":private") 'private) + ((string= prot "private") 'private) + ((string= prot ":protected") 'protected) + ((string= prot "protected") 'protected)))) + +(define-mode-local-override semantic-tag-static-p + emacs-lisp-mode (tag &optional parent) + "Return non-nil if TAG is static in PARENT class. +Overrides `semantic-nonterminal-static'." + ;; This can only be true (theoretically) in a class where it is assigned. + (semantic-tag-get-attribute tag :static-flag)) + +;;; Context parsing +;; +;; Emacs lisp is very different from C,C++ which most context parsing +;; functions are written. Support them here. +(define-mode-local-override semantic-up-context emacs-lisp-mode + (&optional point bounds-type) + "Move up one context in an Emacs Lisp function. +A Context in many languages is a block with it's own local variables. +In Emacs, we will move up lists and stop when one starts with one of +the following context specifiers: + `let', `let*', `defun', `with-slots' +Returns non-nil it is not possible to go up a context." + (let ((last-up (semantic-up-context-default))) + (while + (and (not (looking-at + "(\\(let\\*?\\|def\\(un\\|method\\|generic\\|\ +define-mode-overload\\)\ +\\|with-slots\\)")) + (not last-up)) + (setq last-up (semantic-up-context-default))) + last-up)) + + +(define-mode-local-override semantic-ctxt-current-function emacs-lisp-mode + (&optional point same-as-symbol-return) + "Return a string which is the current function being called." + (save-excursion + (if point (goto-char point) (setq point (point))) + ;; (semantic-beginning-of-command) + (if (condition-case nil + (and (save-excursion + (up-list -2) + (looking-at "((")) + (save-excursion + (up-list -3) + (looking-at "(let"))) + (error nil)) + ;; This is really a let statement, not a function. + nil + (let ((fun (condition-case nil + (save-excursion + (up-list -1) + (forward-char 1) + (buffer-substring-no-properties + (point) (progn (forward-sexp 1) + (point)))) + (error nil)) + )) + (when fun + ;; Do not return FUN IFF the cursor is on FUN. + ;; Huh? Thats because if cursor is on fun, it is + ;; the current symbol, and not the current function. + (if (save-excursion + (condition-case nil + (progn (forward-sexp -1) + (and + (looking-at (regexp-quote fun)) + (<= point (+ (point) (length fun)))) + ) + (error t))) + ;; Go up and try again. + same-as-symbol-return + ;; We are ok, so get it. + (list fun)) + )) + ))) + + +(define-mode-local-override semantic-get-local-variables emacs-lisp-mode + (&optional point) + "Return a list of local variables for POINT. +Scan backwards from point at each successive function. For all occurances +of `let' or `let*', grab those variable names." + (let* ((vars nil) + (fn nil)) + (save-excursion + (while (setq fn (car (semantic-ctxt-current-function-emacs-lisp-mode + (point) (list t)))) + (cond + ((eq fn t) + nil) + ((member fn '("let" "let*" "with-slots")) + ;; Snarf variables + (up-list -1) + (forward-char 1) + (forward-symbol 1) + (skip-chars-forward "* \t\n") + (let ((varlst (read (buffer-substring-no-properties + (point) + (save-excursion + (forward-sexp 1) + (point)))))) + (while varlst + (let* ((oneelt (car varlst)) + (name (if (symbolp oneelt) + oneelt + (car oneelt)))) + (setq vars (cons (semantic-tag-new-variable + (symbol-name name) + nil nil) + vars))) + (setq varlst (cdr varlst))) + )) + ((string= fn "lambda") + ;; Snart args... + (up-list -1) + (forward-char 1) + (forward-word 1) + (skip-chars-forward "* \t\n") + (let ((arglst (read (buffer-substring-no-properties + (point) + (save-excursion + (forward-sexp 1) + (point)))))) + (while arglst + (let* ((name (car arglst))) + (when (/= ?& (aref (symbol-name name) 0)) + (setq vars (cons (semantic-tag-new-variable + (symbol-name name) + nil nil) + vars)))) + (setq arglst (cdr arglst))) + )) + ) + (up-list -1))) + (nreverse vars))) + +(define-mode-local-override semantic-end-of-command emacs-lisp-mode + () + "Move cursor to the end of the current command. +In emacs lisp this is easilly defined by parenthisis bounding." + (condition-case nil + (up-list 1) + (error nil))) + +(define-mode-local-override semantic-beginning-of-command emacs-lisp-mode + () + "Move cursor to the beginning of the current command. +In emacs lisp this is easilly defined by parenthisis bounding." + (condition-case nil + (progn + (up-list -1) + (forward-char 1)) + (error nil))) + +(define-mode-local-override semantic-ctxt-current-symbol emacs-lisp-mode + (&optional point) + "List the symbol under point." + (save-excursion + (if point (goto-char point)) + (require 'thingatpt) + (let ((sym (thing-at-point 'symbol))) + (if sym (list sym))) + )) + + +(define-mode-local-override semantic-ctxt-current-assignment emacs-lisp-mode + (&optional point) + "What is the variable being assigned into at POINT?" + (save-excursion + (if point (goto-char point)) + (let ((fn (semantic-ctxt-current-function point)) + (point (point))) + ;; We should never get lists from here. + (if fn (setq fn (car fn))) + (cond + ;; SETQ + ((and fn (or (string= fn "setq") (string= fn "set"))) + (save-excursion + (condition-case nil + (let ((count 0) + (lastodd nil) + (start nil)) + (up-list -1) + (down-list 1) + (forward-sexp 1) + ;; Skip over sexp until we pass point. + (while (< (point) point) + (setq count (1+ count)) + (forward-comment 1) + (setq start (point)) + (forward-sexp 1) + (if (= (% count 2) 1) + (setq lastodd + (buffer-substring-no-properties start (point)))) + ) + (if lastodd (list lastodd)) + ) + (error nil)))) + ;; This obscure thing finds let statements. + ((condition-case nil + (and + (save-excursion + (up-list -2) + (looking-at "((")) + (save-excursion + (up-list -3) + (looking-at "(let"))) + (error nil)) + (save-excursion + (semantic-beginning-of-command) + ;; Use func finding code, since it is the same format. + (semantic-ctxt-current-symbol))) + ;; + ;; DEFAULT- nothing + (t nil)) + ))) + +(define-mode-local-override semantic-ctxt-current-argument emacs-lisp-mode + (&optional point) + "Return the index into the argument the cursor is in, or nil." + (save-excursion + (if point (goto-char point)) + (if (looking-at "\\<\\w") + (forward-char 1)) + (let ((count 0)) + (while (condition-case nil + (progn + (forward-sexp -1) + t) + (error nil)) + (setq count (1+ count))) + (cond ((= count 0) + 0) + (t (1- count)))) + )) + +(define-mode-local-override semantic-ctxt-current-class-list emacs-lisp-mode + (&optional point) + "Return a list of tag classes allowed at POINT. +Emacs Lisp knows much more about the class of the tag needed to perform +completion than some langauges. We distincly know if we are to be +a function name, variable name, or any type of symbol. We could identify +fields and such to, but that is for some other day." + (save-excursion + (if point (goto-char point)) + (setq point (point)) + (condition-case nil + (let ((count 0)) + (up-list -1) + (forward-char 1) + (while (< (point) point) + (setq count (1+ count)) + (forward-sexp 1)) + (if (= count 1) + '(function) + '(variable)) + ) + (error '(variable))) + )) + +;;; Formatting +;; +(define-mode-local-override semantic-format-tag-abbreviate emacs-lisp-mode + (tag &optional parent color) + "Return an abbreviated string describing tag." + (let ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color)) + ) + (cond + ((eq class 'function) + (concat "(" name ")")) + (t + (semantic-format-tag-abbreviate-default tag parent color))))) + +(define-mode-local-override semantic-format-tag-prototype emacs-lisp-mode + (tag &optional parent color) + "Return a prototype string describing tag. +In Emacs Lisp, a prototype for something may start (autoload ...). +This is certainly not expected if this is used to display a summary. +Make up something else. When we go to write something that needs +a real Emacs Lisp protype, we can fix it then." + (let ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color)) + ) + (cond + ((eq class 'function) + (let* ((args (semantic-tag-function-arguments tag)) + (argstr (semantic--format-tag-arguments args + #'identity + color))) + (concat "(" name (if args " " "") + argstr + ")"))) + (t + (semantic-format-tag-prototype-default tag parent color))))) + +(define-mode-local-override semantic-format-tag-concise-prototype emacs-lisp-mode + (tag &optional parent color) + "Return a concise prototype string describing tag. +See `semantic-format-tag-prototype' for Emacs Lisp for more details." + (semantic-format-tag-prototype tag parent color)) + +(define-mode-local-override semantic-format-tag-uml-prototype emacs-lisp-mode + (tag &optional parent color) + "Return a uml prototype string describing tag. +See `semantic-format-tag-prototype' for Emacs Lisp for more details." + (semantic-format-tag-prototype tag parent color)) + +;;; IA Commands +;; +(define-mode-local-override semantic-ia-insert-tag + emacs-lisp-mode (tag) + "Insert TAG into the current buffer based on completion." + ;; This function by David <de_bb@...> is a tweaked version of the original. + (insert (semantic-tag-name tag)) + (let ((tt (semantic-tag-class tag)) + (args (semantic-tag-function-arguments tag))) + (cond ((eq tt 'function) + (if args + (insert " ") + (insert ")"))) + (t nil)))) + +;;; Lexical features and setup +;; +(defvar-mode-local emacs-lisp-mode semantic-lex-analyzer + 'semantic-emacs-lisp-lexer) + +(defvar-mode-local emacs-lisp-mode semantic--parse-table + semantic--elisp-parse-table) + +(defvar-mode-local emacs-lisp-mode semantic-function-argument-separator + " ") + +(defvar-mode-local emacs-lisp-mode semantic-function-argument-separation-character + " ") + +(defvar-mode-local emacs-lisp-mode semantic-symbol->name-assoc-list + '( + (type . "Types") + (variable . "Variables") + (function . "Defuns") + (include . "Requires") + (package . "Provides") + )) + +(defvar-mode-local emacs-lisp-mode imenu-create-index-function + 'semantic-create-imenu-index) + +(defvar-mode-local emacs-lisp-mode semantic-stickyfunc-sticky-classes + '(function type variable) + "Add variables. +ELisp variables can be pretty long, so track this one too.") + +(define-child-mode lisp-mode emacs-lisp-mode + "Make `lisp-mode' inherits mode local behavior from `emacs-lisp-mode'.") + +(defun semantic-default-elisp-setup () + "Setup hook function for Emacs Lisp files and Semantic." + ) + +(add-hook 'emacs-lisp-mode-hook 'semantic-default-elisp-setup) + +;;; LISP MODE +;; +;; @TODO: Lisp supports syntaxes that Emacs Lisp does not. +;; Write a Lisp only parser someday. +;; +;; See this syntax: +;; (defun foo () /#A) +;; +(add-hook 'lisp-mode-hook 'semantic-default-elisp-setup) + +(eval-after-load "semanticdb" + '(require 'semanticdb-el) + ) + +(provide 'semantic/bovine/el) + +;;; semantic/bovine/el.el ends here diff --git a/lisp/cedet/semantic/bovine/gcc.el b/lisp/cedet/semantic/bovine/gcc.el new file mode 100644 index 00000000000..49c65366c2a --- /dev/null +++ b/lisp/cedet/semantic/bovine/gcc.el @@ -0,0 +1,224 @@ +;;; semantic/bovine/gcc.el --- gcc querying special code for the C parser + +;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; GCC stores things in special places. These functions will query +;; GCC, and set up the preprocessor and include paths. + +(require 'semantic/dep) + +(defvar semantic-lex-c-preprocessor-symbol-file) +(defvar semantic-lex-c-preprocessor-symbol-map) +(declare-function semantic-c-reset-preprocessor-symbol-map + "semantic/bovine/gcc") + +;;; Code: + +(defun semantic-gcc-query (gcc-cmd &rest gcc-options) + "Return program output to both standard output and standard error. +GCC-CMD is the program to execute and GCC-OPTIONS are the options +to give to the program." + ;; $ gcc -v + ;; + (let ((buff (get-buffer-create " *gcc-query*")) + (old-lc-messages (getenv "LC_ALL"))) + (save-excursion + (set-buffer buff) + (erase-buffer) + (setenv "LC_ALL" "C") + (condition-case nil + (apply 'call-process gcc-cmd nil (cons buff t) nil gcc-options) + (error ;; Some bogus directory for the first time perhaps? + (let ((default-directory (expand-file-name "~/"))) + (condition-case nil + (apply 'call-process gcc-cmd nil (cons buff t) nil gcc-options) + (error ;; gcc doesn't exist??? + nil))))) + (setenv "LC_ALL" old-lc-messages) + (prog1 + (buffer-string) + (kill-buffer buff) + ) + ))) + +;;(semantic-gcc-get-include-paths "c") +;;(semantic-gcc-get-include-paths "c++") +(defun semantic-gcc-get-include-paths (lang) + "Return include paths as gcc use them for language LANG." + (let* ((gcc-cmd (cond + ((string= lang "c") "gcc") + ((string= lang "c++") "c++") + (t (if (stringp lang) + (error "Unknown lang: %s" lang) + (error "LANG=%S, should be a string" lang))))) + (gcc-output (semantic-gcc-query gcc-cmd "-v" "-E" "-x" lang null-device)) + (lines (split-string gcc-output "\n")) + (include-marks 0) + (inc-mark "#include ") + (inc-mark-len (length "#include ")) + inc-path) + ;;(message "gcc-output=%s" gcc-output) + (dolist (line lines) + (when (> (length line) 1) + (if (= 0 include-marks) + (when (and (> (length line) inc-mark-len) + (string= inc-mark (substring line 0 inc-mark-len))) + (setq include-marks (1+ include-marks))) + (let ((chars (append line nil))) + (when (= 32 (nth 0 chars)) + (let ((path (substring line 1))) + (when (file-accessible-directory-p path) + (when (if (memq system-type '(windows-nt)) + (/= ?/ (nth 1 chars)) + (= ?/ (nth 1 chars))) + (add-to-list 'inc-path + (expand-file-name (substring line 1)) + t))))))))) + inc-path)) + + +(defun semantic-cpp-defs (str) + "Convert CPP output STR into a list of cons cells with defines for C++." + (let ((lines (split-string str "\n")) + (lst nil)) + (dolist (L lines) + (let ((dat (split-string L))) + (when (= (length dat) 3) + (add-to-list 'lst (cons (nth 1 dat) (nth 2 dat)))))) + lst)) + +(defun semantic-gcc-fields (str) + "Convert GCC output STR into an alist of fields." + (let ((fields nil) + (lines (split-string str "\n")) + ) + (dolist (L lines) + ;; For any line, what do we do with it? + (cond ((or (string-match "Configured with\\(:\\)" L) + (string-match "\\(:\\)\\s-*[^ ]*configure " L)) + (let* ((parts (substring L (match-end 1))) + (opts (split-string parts " " t)) + ) + (dolist (O (cdr opts)) + (let* ((data (split-string O "=")) + (sym (intern (car data))) + (val (car (cdr data)))) + (push (cons sym val) fields) + )) + )) + ((string-match "gcc[ -][vV]ersion" L) + (let* ((vline (substring L (match-end 0))) + (parts (split-string vline " "))) + (push (cons 'version (nth 1 parts)) fields))) + ((string-match "Target: " L) + (let ((parts (split-string L " "))) + (push (cons 'target (nth 1 parts)) fields))) + )) + fields)) + +(defvar semantic-gcc-setup-data nil + "The GCC setup data. +This is setup by `semantic-gcc-setup'. +This is an alist, and should include keys of: + 'version - The version of gcc + '--host - The host symbol. (Used in include directories) + '--prefix - Where GCC was installed. +It should also include other symbols GCC was compiled with.") + +;;;###autoload +(defun semantic-gcc-setup () + "Setup Semantic C/C++ parsing based on GCC output." + (interactive) + (let* ((fields (or semantic-gcc-setup-data + (semantic-gcc-fields (semantic-gcc-query "gcc" "-v")))) + (defines (semantic-cpp-defs (semantic-gcc-query "cpp" "-E" "-dM" "-x" "c++" null-device))) + (ver (cdr (assoc 'version fields))) + (host (or (cdr (assoc 'target fields)) + (cdr (assoc '--target fields)) + (cdr (assoc '--host fields)))) + (prefix (cdr (assoc '--prefix fields))) + ;; gcc output supplied paths + (c-include-path (semantic-gcc-get-include-paths "c")) + (c++-include-path (semantic-gcc-get-include-paths "c++"))) + ;; Remember so we don't have to call GCC twice. + (setq semantic-gcc-setup-data fields) + (unless c-include-path + ;; Fallback to guesses + (let* ( ;; gcc include dirs + (gcc-exe (locate-file "gcc" exec-path exec-suffixes 'executable)) + (gcc-root (expand-file-name ".." (file-name-directory gcc-exe))) + (gcc-include (expand-file-name "include" gcc-root)) + (gcc-include-c++ (expand-file-name "c++" gcc-include)) + (gcc-include-c++-ver (expand-file-name ver gcc-include-c++)) + (gcc-include-c++-ver-host (expand-file-name host gcc-include-c++-ver))) + (setq c-include-path + (remove-if-not 'file-accessible-directory-p + (list "/usr/include" gcc-include))) + (setq c++-include-path + (remove-if-not 'file-accessible-directory-p + (list "/usr/include" + gcc-include + gcc-include-c++ + gcc-include-c++-ver + gcc-include-c++-ver-host))))) + + ;;; Fix-me: I think this part might have been a misunderstanding, but I am not sure. + ;; If this option is specified, try it both with and without prefix, and with and without host + ;; (if (assoc '--with-gxx-include-dir fields) + ;; (let ((gxx-include-dir (cdr (assoc '--with-gxx-include-dir fields)))) + ;; (nconc try-paths (list gxx-include-dir + ;; (concat prefix gxx-include-dir) + ;; (concat gxx-include-dir "/" host) + ;; (concat prefix gxx-include-dir "/" host))))) + + ;; Now setup include paths etc + (dolist (D (semantic-gcc-get-include-paths "c")) + (semantic-add-system-include D 'c-mode)) + (dolist (D (semantic-gcc-get-include-paths "c++")) + (semantic-add-system-include D 'c++-mode) + (let ((cppconfig (concat D "/bits/c++config.h"))) + ;; Presumably there will be only one of these files in the try-paths list... + (when (file-readable-p cppconfig) + ;; Add it to the symbol file + (if (boundp 'semantic-lex-c-preprocessor-symbol-file) + ;; Add to the core macro header list + (add-to-list 'semantic-lex-c-preprocessor-symbol-file cppconfig) + ;; Setup the core macro header + (setq semantic-lex-c-preprocessor-symbol-file (list cppconfig))) + ))) + (if (not (boundp 'semantic-lex-c-preprocessor-symbol-map)) + (setq semantic-lex-c-preprocessor-symbol-map nil)) + (dolist (D defines) + (add-to-list 'semantic-lex-c-preprocessor-symbol-map D)) + (when (featurep 'semantic/bovine/c) + (semantic-c-reset-preprocessor-symbol-map)) + nil)) + +(provide 'semantic/bovine/gcc) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/bovine/gcc" +;; End: + +;;; semantic/bovine/gcc.el ends here diff --git a/lisp/cedet/semantic/bovine/make-by.el b/lisp/cedet/semantic/bovine/make-by.el new file mode 100644 index 00000000000..19e35d0682b --- /dev/null +++ b/lisp/cedet/semantic/bovine/make-by.el @@ -0,0 +1,387 @@ +;;; semantic/bovine/make-by.el --- Generated parser support file + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2008 +;;; Free Software Foundation, Inc. + +;; 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: +;; +;; This file was generated from the grammar file +;; semantic/bovine/make.by in the CEDET repository. + +;;; Code: + +(require 'semantic/lex) +(eval-when-compile (require 'semantic/bovine)) + + +;;; Prologue +;; + +;;; Declarations +;; +(defconst semantic-make-by--keyword-table + (semantic-lex-make-keyword-table + '(("if" . IF) + ("ifdef" . IFDEF) + ("ifndef" . IFNDEF) + ("ifeq" . IFEQ) + ("ifneq" . IFNEQ) + ("else" . ELSE) + ("endif" . ENDIF) + ("include" . INCLUDE)) + '(("include" summary "Macro: include filename1 filename2 ...") + ("ifneq" summary "Conditional: ifneq (expression) ... else ... endif") + ("ifeq" summary "Conditional: ifeq (expression) ... else ... endif") + ("ifndef" summary "Conditional: ifndef (expression) ... else ... endif") + ("ifdef" summary "Conditional: ifdef (expression) ... else ... endif") + ("endif" summary "Conditional: if (expression) ... else ... endif") + ("else" summary "Conditional: if (expression) ... else ... endif") + ("if" summary "Conditional: if (expression) ... else ... endif"))) + "Table of language keywords.") + +(defconst semantic-make-by--token-table + (semantic-lex-make-type-table + '(("punctuation" + (BACKSLASH . "\\`[\\]\\'") + (DOLLAR . "\\`[$]\\'") + (EQUAL . "\\`[=]\\'") + (PLUS . "\\`[+]\\'") + (COLON . "\\`[:]\\'"))) + 'nil) + "Table of lexical tokens.") + +(defconst semantic-make-by--parse-table + `( + (bovine-toplevel + (Makefile) + ) ;; end bovine-toplevel + + (Makefile + (bol + newline + ,(semantic-lambda + (list nil)) + ) + (bol + variable + ,(semantic-lambda + (nth 1 vals)) + ) + (bol + rule + ,(semantic-lambda + (nth 1 vals)) + ) + (bol + conditional + ,(semantic-lambda + (nth 1 vals)) + ) + (bol + include + ,(semantic-lambda + (nth 1 vals)) + ) + (whitespace + ,(semantic-lambda + (list nil)) + ) + (newline + ,(semantic-lambda + (list nil)) + ) + ) ;; end Makefile + + (variable + (symbol + opt-whitespace + equals + opt-whitespace + element-list + ,(semantic-lambda + (semantic-tag-new-variable + (nth 0 vals) nil + (nth 4 vals))) + ) + ) ;; end variable + + (rule + (targets + opt-whitespace + colons + opt-whitespace + element-list + commands + ,(semantic-lambda + (semantic-tag-new-function + (nth 0 vals) nil + (nth 4 vals))) + ) + ) ;; end rule + + (targets + (target + opt-whitespace + targets + ,(semantic-lambda + (list + (car + (nth 0 vals)) + (car + (nth 2 vals)))) + ) + (target + ,(semantic-lambda + (list + (car + (nth 0 vals)))) + ) + ) ;; end targets + + (target + (sub-target + target + ,(semantic-lambda + (list + (concat + (car + (nth 0 vals)) + (car + (nth 2 vals))))) + ) + (sub-target + ,(semantic-lambda + (list + (car + (nth 0 vals)))) + ) + ) ;; end target + + (sub-target + (symbol) + (string) + (varref) + ) ;; end sub-target + + (conditional + (IF + some-whitespace + symbol + newline + ,(semantic-lambda + (list nil)) + ) + (IFDEF + some-whitespace + symbol + newline + ,(semantic-lambda + (list nil)) + ) + (IFNDEF + some-whitespace + symbol + newline + ,(semantic-lambda + (list nil)) + ) + (IFEQ + some-whitespace + expression + newline + ,(semantic-lambda + (list nil)) + ) + (IFNEQ + some-whitespace + expression + newline + ,(semantic-lambda + (list nil)) + ) + (ELSE + newline + ,(semantic-lambda + (list nil)) + ) + (ENDIF + newline + ,(semantic-lambda + (list nil)) + ) + ) ;; end conditional + + (expression + (semantic-list) + ) ;; end expression + + (include + (INCLUDE + some-whitespace + element-list + ,(semantic-lambda + (semantic-tag-new-include + (nth 2 vals) nil)) + ) + ) ;; end include + + (equals + (punctuation + "\\`[:]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda) + ) + (punctuation + "\\`[+]\\'" + punctuation + "\\`[=]\\'" + ,(semantic-lambda) + ) + (punctuation + "\\`[=]\\'" + ,(semantic-lambda) + ) + ) ;; end equals + + (colons + (punctuation + "\\`[:]\\'" + punctuation + "\\`[:]\\'" + ,(semantic-lambda) + ) + (punctuation + "\\`[:]\\'" + ,(semantic-lambda) + ) + ) ;; end colons + + (element-list + (elements + newline + ,(semantic-lambda + (nth 0 vals)) + ) + ) ;; end element-list + + (elements + (element + some-whitespace + elements + ,(semantic-lambda + (nth 0 vals) + (nth 2 vals)) + ) + (element + ,(semantic-lambda + (nth 0 vals)) + ) + ( ;;EMPTY + ) + ) ;; end elements + + (element + (sub-element + element + ,(semantic-lambda + (list + (concat + (car + (nth 0 vals)) + (car + (nth 1 vals))))) + ) + ( ;;EMPTY + ) + ) ;; end element + + (sub-element + (symbol) + (string) + (punctuation) + (semantic-list + ,(semantic-lambda + (list + (buffer-substring-no-properties + (identity start) + (identity end)))) + ) + ) ;; end sub-element + + (varref + (punctuation + "\\`[$]\\'" + semantic-list + ,(semantic-lambda + (list + (buffer-substring-no-properties + (identity start) + (identity end)))) + ) + ) ;; end varref + + (commands + (bol + shell-command + newline + commands + ,(semantic-lambda + (list + (nth 0 vals)) + (nth 1 vals)) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end commands + + (opt-whitespace + (some-whitespace + ,(semantic-lambda + (list nil)) + ) + ( ;;EMPTY + ) + ) ;; end opt-whitespace + + (some-whitespace + (whitespace + some-whitespace + ,(semantic-lambda + (list nil)) + ) + (whitespace + ,(semantic-lambda + (list nil)) + ) + ) ;; end some-whitespace + ) + "Parser table.") + +(defun semantic-make-by--install-parser () + "Setup the Semantic Parser." + (setq semantic--parse-table semantic-make-by--parse-table + semantic-debug-parser-source "make.by" + semantic-debug-parser-class 'semantic-bovine-debug-parser + semantic-flex-keywords-obarray semantic-make-by--keyword-table + )) + +(provide 'semantic/bovine/make-by) + +;;; semantic/bovine/make-by.el ends here diff --git a/lisp/cedet/semantic/bovine/make.el b/lisp/cedet/semantic/bovine/make.el new file mode 100644 index 00000000000..9f3edcfbe9b --- /dev/null +++ b/lisp/cedet/semantic/bovine/make.el @@ -0,0 +1,242 @@ +;;; semantic/bovine/make.el --- Makefile parsing rules. + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2008 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Use the Semantic Bovinator to parse Makefiles. +;; Concocted as an experiment for nonstandard languages. + +(require 'make-mode) + +(require 'semantic) +(require 'semantic/bovine/make-by) +(require 'semantic/analyze) +(require 'semantic/dep) + +(declare-function semantic-analyze-possible-completions-default + "semantic/analyze/complete") + +;;; Code: +(define-lex-analyzer semantic-lex-make-backslash-no-newline + "Detect and create a beginning of line token (BOL)." + (and (looking-at "\\(\\\\\n\\s-*\\)") + ;; We have a \ at eol. Push it as whitespace, but pretend + ;; it never happened so we can skip the BOL tokenizer. + (semantic-lex-push-token (semantic-lex-token 'whitespace + (match-beginning 1) + (match-end 1))) + (goto-char (match-end 1)) + nil) ;; CONTINUE + ;; We want to skip BOL, so move to the next condition. + nil) + +(define-lex-regex-analyzer semantic-lex-make-command + "A command in a Makefile consists of a line starting with TAB, and ending at the newline." + "^\\(\t\\)" + (let ((start (match-end 0))) + (while (progn (end-of-line) + (save-excursion (forward-char -1) (looking-at "\\\\"))) + (forward-char 1)) + (semantic-lex-push-token + (semantic-lex-token 'shell-command start (point))))) + +(define-lex-regex-analyzer semantic-lex-make-ignore-automake-conditional + "An automake conditional seems to really bog down the parser. +Ignore them." + "^@\\(\\w\\|\\s_\\)+@" + (setq semantic-lex-end-point (match-end 0))) + +(define-lex semantic-make-lexer + "Lexical analyzer for Makefiles." + semantic-lex-beginning-of-line + semantic-lex-make-ignore-automake-conditional + semantic-lex-make-command + semantic-lex-make-backslash-no-newline + semantic-lex-whitespace + semantic-lex-newline + semantic-lex-symbol-or-keyword + semantic-lex-charquote + semantic-lex-paren-or-list + semantic-lex-close-paren + semantic-lex-string + semantic-lex-ignore-comments + semantic-lex-punctuation + semantic-lex-default-action) + +(defun semantic-make-expand-tag (tag) + "Expand TAG into a list of equivalent tags, or nil." + (let ((name (semantic-tag-name tag)) + xpand) + ;(message "Expanding %S" name) + ;(goto-char (semantic-tag-start tag)) + ;(sit-for 0) + (if (and (consp name) + (memq (semantic-tag-class tag) '(function include)) + (> (length name) 1)) + (while name + (setq xpand (cons (semantic-tag-clone tag (car name)) xpand) + name (cdr name))) + ;; Else, only a single name. + (when (consp name) + (setcar tag (car name))) + (setq xpand (list tag))) + xpand)) + +(define-mode-local-override semantic-get-local-variables + makefile-mode (&optional point) + "Override `semantic-get-local-variables' so it does not throw an error. +We never have local variables in Makefiles." + nil) + +(define-mode-local-override semantic-ctxt-current-class-list + makefile-mode (&optional point) + "List of classes that are valid to place at point." + (let ((tag (semantic-current-tag))) + (when tag + (cond ((condition-case nil + (save-excursion + (condition-case nil (forward-sexp -1) + (error nil)) + (forward-char -2) + (looking-at "\\$\\s(")) + (error nil)) + ;; We are in a variable reference + '(variable)) + ((semantic-tag-of-class-p tag 'function) + ;; Note: variables are handled above. + '(function filename)) + ((semantic-tag-of-class-p tag 'variable) + '(function filename)) + )))) + +(define-mode-local-override semantic-format-tag-abbreviate + makefile-mode (tag &optional parent color) + "Return an abbreviated string describing tag for Makefiles." + (let ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color)) + ) + (cond ((eq class 'function) + (concat name ":")) + ((eq class 'filename) + (concat "./" name)) + (t + (semantic-format-tag-abbreviate-default tag parent color))))) + +(defvar-mode-local makefile-mode semantic-function-argument-separator + " " + "Separator used between dependencies to rules.") + +(define-mode-local-override semantic-format-tag-prototype + makefile-mode (tag &optional parent color) + "Return a prototype string describing tag for Makefiles." + (let* ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color)) + ) + (cond ((eq class 'function) + (concat name ": " + (semantic--format-tag-arguments + (semantic-tag-function-arguments tag) + #'semantic-format-tag-prototype + color))) + ((eq class 'filename) + (concat "./" name)) + (t + (semantic-format-tag-prototype-default tag parent color))))) + +(define-mode-local-override semantic-format-tag-concise-prototype + makefile-mode (tag &optional parent color) + "Return a concise prototype string describing tag for Makefiles. +This is the same as a regular prototype." + (semantic-format-tag-prototype tag parent color)) + +(define-mode-local-override semantic-format-tag-uml-prototype + makefile-mode (tag &optional parent color) + "Return a UML prototype string describing tag for Makefiles. +This is the same as a regular prototype." + (semantic-format-tag-prototype tag parent color)) + +(define-mode-local-override semantic-analyze-possible-completions + makefile-mode (context) + "Return a list of possible completions in a Makefile. +Uses default implementation, and also gets a list of filenames." + (save-excursion + (require 'semantic/analyze/complete) + (set-buffer (oref context buffer)) + (let* ((normal (semantic-analyze-possible-completions-default context)) + (classes (oref context :prefixclass)) + (filetags nil)) + (when (memq 'filename classes) + (let* ((prefix (car (oref context :prefix))) + (completetext (cond ((semantic-tag-p prefix) + (semantic-tag-name prefix)) + ((stringp prefix) + prefix) + ((stringp (car prefix)) + (car prefix)))) + (files (directory-files default-directory nil + (concat "^" completetext)))) + (setq filetags (mapcar (lambda (f) (semantic-tag f 'filename)) + files)))) + ;; Return the normal completions found, plus any filenames + ;; that match. + (append normal filetags) + ))) + +(defcustom-mode-local-semantic-dependency-system-include-path + makefile-mode semantic-makefile-dependency-system-include-path + nil + "The system include path used by Makefiles langauge.") + +;;;###autoload +(defun semantic-default-make-setup () + "Set up a Makefile buffer for parsing with semantic." + (semantic-make-by--install-parser) + (setq semantic-symbol->name-assoc-list '((variable . "Variables") + (function . "Rules") + (include . "Dependencies") + ;; File is a meta-type created + ;; to represent completions + ;; but not actually parsed. + (file . "File")) + semantic-case-fold t + semantic-tag-expand-function 'semantic-make-expand-tag + semantic-lex-syntax-modifications '((?. "_") + (?= ".") + (?/ "_") + (?$ ".") + (?+ ".") + (?\\ ".") + ) + imenu-create-index-function 'semantic-create-imenu-index + ) + (setq semantic-lex-analyzer #'semantic-make-lexer) + ) + +(provide 'semantic/bovine/make) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/bovine/make" +;; End: + +;;; semantic/bovine/make.el ends here diff --git a/lisp/cedet/semantic/bovine/scm-by.el b/lisp/cedet/semantic/bovine/scm-by.el new file mode 100644 index 00000000000..82a8ae6ffa3 --- /dev/null +++ b/lisp/cedet/semantic/bovine/scm-by.el @@ -0,0 +1,191 @@ +;;; semantic-scm-by.el --- Generated parser support file + +;; Copyright (C) 2001, 2003, 2009 Free Software Foundation, Inc. + +;; 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: +;; +;; This file was generated from the grammar file +;; semantic/bovine/scm.by in the CEDET repository. + +;;; Code: + +(require 'semantic/lex) + +(eval-when-compile (require 'semantic/bovine)) + +;;; Prologue +;; + +;;; Declarations +;; +(defconst semantic-scm-by--keyword-table + (semantic-lex-make-keyword-table + '(("define" . DEFINE) + ("define-module" . DEFINE-MODULE) + ("load" . LOAD)) + '(("load" summary "Function: (load \"filename\")") + ("define-module" summary "Function: (define-module (name arg1 ...)) ") + ("define" summary "Function: (define symbol expression)"))) + "Table of language keywords.") + +(defconst semantic-scm-by--token-table + (semantic-lex-make-type-table + '(("close-paren" + (CLOSEPAREN . ")")) + ("open-paren" + (OPENPAREN . "("))) + 'nil) + "Table of lexical tokens.") + +(defconst semantic-scm-by--parse-table + `( + (bovine-toplevel + (scheme) + ) ;; end bovine-toplevel + + (scheme + (semantic-list + ,(lambda (vals start end) + (semantic-bovinate-from-nonterminal + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'scheme-list)) + ) + ) ;; end scheme + + (scheme-list + (open-paren + "(" + scheme-in-list + close-paren + ")" + ,(semantic-lambda + (nth 1 vals)) + ) + ) ;; end scheme-list + + (scheme-in-list + (DEFINE + symbol + expression + ,(semantic-lambda + (semantic-tag-new-variable + (nth 1 vals) nil + (nth 2 vals))) + ) + (DEFINE + name-args + opt-doc + sequence + ,(semantic-lambda + (semantic-tag-new-function + (car + (nth 1 vals)) nil + (cdr + (nth 1 vals)))) + ) + (DEFINE-MODULE + name-args + ,(semantic-lambda + (semantic-tag-new-package + (nth + (length + (nth 1 vals)) + (nth 1 vals)) nil)) + ) + (LOAD + string + ,(semantic-lambda + (semantic-tag-new-include + (file-name-nondirectory + (read + (nth 1 vals))) + (read + (nth 1 vals)))) + ) + (symbol + ,(semantic-lambda + (semantic-tag-new-code + (nth 0 vals) nil)) + ) + ) ;; end scheme-in-list + + (name-args + (semantic-list + ,(lambda (vals start end) + (semantic-bovinate-from-nonterminal + (car + (nth 0 vals)) + (cdr + (nth 0 vals)) + 'name-arg-expand)) + ) + ) ;; end name-args + + (name-arg-expand + (open-paren + name-arg-expand + ,(semantic-lambda + (nth 1 vals)) + ) + (symbol + name-arg-expand + ,(semantic-lambda + (cons + (nth 0 vals) + (nth 1 vals))) + ) + ( ;;EMPTY + ,(semantic-lambda) + ) + ) ;; end name-arg-expand + + (opt-doc + (string) + ( ;;EMPTY + ) + ) ;; end opt-doc + + (sequence + (expression + sequence) + (expression) + ) ;; end sequence + + (expression + (symbol) + (semantic-list) + (string) + (number) + ) ;; end expression + ) + "Parser table.") + +(defun semantic-scm-by--install-parser () + "Setup the Semantic Parser." + (setq semantic--parse-table semantic-scm-by--parse-table + semantic-debug-parser-source "scheme.by" + semantic-debug-parser-class 'semantic-bovine-debug-parser + semantic-flex-keywords-obarray semantic-scm-by--keyword-table + )) + +(provide 'semantic/bovine/scm-by) + +;;; semantic/bovine/scm-by.el ends here diff --git a/lisp/cedet/semantic/bovine/scm.el b/lisp/cedet/semantic/bovine/scm.el new file mode 100644 index 00000000000..82a7dde039b --- /dev/null +++ b/lisp/cedet/semantic/bovine/scm.el @@ -0,0 +1,119 @@ +;;; semantic/bovine/scm.el --- Semantic details for Scheme (guile) + +;;; Copyright (C) 2001, 2002, 2003, 2004, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Use the Semantic Bovinator for Scheme (guile) + +(require 'semantic) +(require 'semantic/bovine/scm-by) +(require 'semantic/format) +(require 'semantic/dep) + +;;; Code: + +(defcustom-mode-local-semantic-dependency-system-include-path + scheme-mode semantic-default-scheme-path + '("/usr/share/guile/") + "Default set of include paths for scheme (guile) code. +This should probably do some sort of search to see what is +actually on the local machine.") + +(define-mode-local-override semantic-format-tag-prototype scheme-mode (tag) + "Return a prototype for the Emacs Lisp nonterminal TAG." + (let* ((tok (semantic-tag-class tag)) + (args (semantic-tag-components tag)) + ) + (if (eq tok 'function) + (concat (semantic-tag-name tag) " (" + (mapconcat (lambda (a) a) args " ") + ")") + (semantic-format-tag-prototype-default tag)))) + +(define-mode-local-override semantic-documentation-for-tag scheme-mode (tag &optional nosnarf) + "Return the documentation string for TAG. +Optional argument NOSNARF is ignored." + (let ((d (semantic-tag-docstring tag))) + (if (and d (> (length d) 0) (= (aref d 0) ?*)) + (substring d 1) + d))) + +(define-mode-local-override semantic-insert-foreign-tag scheme-mode (tag tagfile) + "Insert TAG from TAGFILE at point. +Attempts a simple prototype for calling or using TAG." + (cond ((eq (semantic-tag-class tag) 'function) + (insert "(" (semantic-tag-name tag) " )") + (forward-char -1)) + (t + (insert (semantic-tag-name tag))))) + +;; Note: Analyzer from Henry S. Thompson +(define-lex-regex-analyzer semantic-lex-scheme-symbol + "Detect and create symbol and keyword tokens." + "\\(\\sw\\([:]\\|\\sw\\|\\s_\\)+\\)" + ;; (message (format "symbol: %s" (match-string 0))) + (semantic-lex-push-token + (semantic-lex-token + (or (semantic-lex-keyword-p (match-string 0)) 'symbol) + (match-beginning 0) (match-end 0)))) + + +(define-lex semantic-scheme-lexer + "A simple lexical analyzer that handles simple buffers. +This lexer ignores comments and whitespace, and will return +syntax as specified by the syntax table." + semantic-lex-ignore-whitespace + semantic-lex-ignore-newline + semantic-lex-scheme-symbol + semantic-lex-charquote + semantic-lex-paren-or-list + semantic-lex-close-paren + semantic-lex-string + semantic-lex-ignore-comments + semantic-lex-punctuation + semantic-lex-number + semantic-lex-default-action) + +;;;###autoload +(defun semantic-default-scheme-setup () + "Setup hook function for Emacs Lisp files and Semantic." + (semantic-scm-by--install-parser) + (setq semantic-symbol->name-assoc-list '( (variable . "Variables") + ;;(type . "Types") + (function . "Functions") + (include . "Loads") + (package . "DefineModule")) + imenu-create-index-function 'semantic-create-imenu-index + imenu-create-index-function 'semantic-create-imenu-index + ) + (setq semantic-lex-analyzer #'semantic-scheme-lexer) + ) + +(provide 'semantic/bovine/scm) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/bovine/scm" +;; End: + +;;; semantic/bovine/scm.el ends here diff --git a/lisp/cedet/semantic/chart.el b/lisp/cedet/semantic/chart.el new file mode 100644 index 00000000000..7efe4e6dfae --- /dev/null +++ b/lisp/cedet/semantic/chart.el @@ -0,0 +1,174 @@ +;;; semantic/chart.el --- Utilities for use with semantic tag tables + +;;; Copyright (C) 1999, 2000, 2001, 2003, 2005, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; A set of simple functions for charting details about a file based on +;; the output of the semantic parser. +;; + +(require 'semantic) +(require 'chart) +(require 'semantic/db) +(require 'semantic/tag) + +(eval-when-compile (require 'semantic/find)) + +;;; Code: + +(defun semantic-chart-tags-by-class (&optional tagtable) + "Create a bar chart representing the number of tags for a given tag class. +Each bar represents how many toplevel tags in TAGTABLE +exist with a given class. See `semantic-symbol->name-assoc-list' +for tokens which will be charted. +TAGTABLE is passedto `semantic-something-to-tag-table'." + (interactive) + (let* ((stream (semantic-something-to-tag-table + (or tagtable (current-buffer)))) + (names (mapcar 'cdr semantic-symbol->name-assoc-list)) + (nums (mapcar + (lambda (symname) + (length + (semantic-brute-find-tag-by-class (car symname) + stream) + )) + semantic-symbol->name-assoc-list))) + (chart-bar-quickie 'vertical + "Semantic Toplevel Tag Volume" + names "Tag Class" + nums "Volume") + )) + +(defun semantic-chart-database-size (&optional tagtable) + "Create a bar chart representing the size of each file in semanticdb. +Each bar represents how many toplevel tags in TAGTABLE +exist in each database entry. +TAGTABLE is passed to `semantic-something-to-tag-table'." + (interactive) + (unless (and (fboundp 'semanticdb-minor-mode-p) + (semanticdb-minor-mode-p)) + (error "Semanticdb is not enabled")) + (let* ((db semanticdb-current-database) + (dbt (semanticdb-get-database-tables db)) + (names (mapcar 'car + (object-assoc-list + 'file + dbt))) + (numnuts (mapcar (lambda (dba) + (prog1 + (cons + (if (slot-boundp dba 'tags) + (length (oref dba tags)) + 1) + (car names)) + (setq names (cdr names)))) + dbt)) + (nums nil) + (fh (/ (- (frame-height) 7) 4))) + (setq numnuts (sort numnuts (lambda (a b) (> (car a) (car b))))) + (setq names (mapcar 'cdr numnuts) + nums (mapcar 'car numnuts)) + (if (> (length names) fh) + (progn + (setcdr (nthcdr fh names) nil) + (setcdr (nthcdr fh nums) nil))) + (chart-bar-quickie 'horizontal + "Semantic DB Toplevel Tag Volume" + names "File" + nums "Volume") + )) + +(defun semantic-chart-token-complexity (tok) + "Calculate the `complexity' of token TOK." + (count-lines + (semantic-tag-end tok) + (semantic-tag-start tok))) + +(defun semantic-chart-tag-complexity + (&optional class tagtable) + "Create a bar chart representing the complexity of some tags. +Complexity is calculated for tags of CLASS. Each bar represents +the complexity of some tag in TAGTABLE. Only the most complex +items are charted. TAGTABLE is passedto +`semantic-something-to-tag-table'." + (interactive) + (let* ((sym (if (not class) 'function)) + (stream + (semantic-find-tags-by-class + sym (semantic-something-to-tag-table (or tagtable + (current-buffer))) + )) + (name (cond ((semantic-tag-with-position-p (car stream)) + (buffer-name (semantic-tag-buffer (car stream)))) + (t ""))) + (cplx (mapcar (lambda (tok) + (cons tok (semantic-chart-token-complexity tok))) + stream)) + (namelabel (cdr (assoc 'function semantic-symbol->name-assoc-list))) + (names nil) + (nums nil)) + (setq cplx (sort cplx (lambda (a b) (> (cdr a) (cdr b))))) + (while (and cplx (<= (length names) (/ (- (frame-height) 7) 4))) + (setq names (cons (semantic-tag-name (car (car cplx))) + names) + nums (cons (cdr (car cplx)) nums) + cplx (cdr cplx))) +;; ;; (setq names (mapcar (lambda (str) +;; ;; (substring str (- (length str) 10))) +;; ;; names)) + (chart-bar-quickie 'horizontal + (format "%s Complexity in %s" + (capitalize (symbol-name sym)) + name) + names namelabel + nums "Complexity (Lines of code)") + )) + +(declare-function semanticdb-get-typecache "semantic/db-typecache") +(declare-function semantic-calculate-scope "semantic/scope") + +(defun semantic-chart-analyzer () + "Chart the extent of the context analysis." + (interactive) + (require 'semantic/db-typecache) + (require 'semantic/scope) + (let* ((p (semanticdb-find-translate-path nil nil)) + (plen (length p)) + (tab semanticdb-current-table) + (tc (semanticdb-get-typecache tab)) + (tclen (+ (length (oref tc filestream)) + (length (oref tc includestream)))) + (scope (semantic-calculate-scope)) + (fslen (length (oref scope fullscope))) + (lvarlen (length (oref scope localvar))) + ) + (chart-bar-quickie 'vertical + (format "Analyzer Overhead in %s" (buffer-name)) + '("includes" "typecache" "scopelen" "localvar") + "Overhead Entries" + (list plen tclen fslen lvarlen) + "Number of tags") + )) + +(provide 'semantic/chart) + +;;; semantic/chart.el ends here diff --git a/lisp/cedet/semantic/complete.el b/lisp/cedet/semantic/complete.el new file mode 100644 index 00000000000..cbf3d9da9ae --- /dev/null +++ b/lisp/cedet/semantic/complete.el @@ -0,0 +1,2101 @@ +;;; semantic/complete.el --- Routines for performing tag completion + +;;; Copyright (C) 2003, 2004, 2005, 2007, 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: +;; +;; Completion of tags by name using tables of semantic generated tags. +;; +;; While it would be a simple matter of flattening all tag known +;; tables to perform completion across them using `all-completions', +;; or `try-completion', that process would be slow. In particular, +;; when a system database is included in the mix, the potential for a +;; ludicrous number of options becomes apparent. +;; +;; As such, dynamically searching across tables using a prefix, +;; regular expression, or other feature is needed to help find symbols +;; quickly without resorting to "show me every possible option now". +;; +;; In addition, some symbol names will appear in multiple locations. +;; If it is important to distiguish, then a way to provide a choice +;; over these locations is important as well. +;; +;; Beyond brute force offers for completion of plain strings, +;; using the smarts of semantic-analyze to provide reduced lists of +;; symbols, or fancy tabbing to zoom into files to show multiple hits +;; of the same name can be provided. +;; +;;; How it works: +;; +;; There are several parts of any completion engine. They are: +;; +;; A. Collection of possible hits +;; B. Typing or selecting an option +;; C. Displaying possible unique completions +;; D. Using the result +;; +;; Here, we will treat each section separately (excluding D) +;; They can then be strung together in user-visible commands to +;; fullfill specific needs. +;; +;; COLLECTORS: +;; +;; A collector is an object which represents the means by which tags +;; to complete on are collected. It's first job is to find all the +;; tags which are to be completed against. It can also rename +;; some tags if needed so long as `semantic-tag-clone' is used. +;; +;; Some collectors will gather all tags to complete against first +;; (for in buffer queries, or other small list situations). It may +;; choose to do a broad search on each completion request. Built in +;; functionality automatically focuses the cache in as the user types. +;; +;; A collector choosing to create and rename tags could choose a +;; plain name format, a postfix name such as method:class, or a +;; prefix name such as class.method. +;; +;; DISPLAYORS +;; +;; A displayor is in charge if showing the user interesting things +;; about available completions, and can optionally provide a focus. +;; The simplest display just lists all available names in a separate +;; window. It may even choose to show short names when there are +;; many to choose from, or long names when there are fewer. +;; +;; A complex displayor could opt to help the user 'focus' on some +;; range. For example, if 4 tags all have the same name, subsequent +;; calls to the displayor may opt to show each tag one at a time in +;; the buffer. When the user likes one, selection would cause the +;; 'focus' item to be selected. +;; +;; CACHE FORMAT +;; +;; The format of the tag lists used to perform the completions are in +;; semanticdb "find" format, like this: +;; +;; ( ( DBTABLE1 TAG1 TAG2 ...) +;; ( DBTABLE2 TAG1 TAG2 ...) +;; ... ) +;; +;; INLINE vs MINIBUFFER +;; +;; Two major ways completion is used in Emacs is either through a +;; minibuffer query, or via completion in a normal editing buffer, +;; encompassing some small range of characters. +;; +;; Structure for both types of completion are provided here. +;; `semantic-complete-read-tag-engine' will use the minibuffer. +;; `semantic-complete-inline-tag-engine' will complete text in +;; a buffer. + +(require 'semantic) +(require 'eieio-opt) +(require 'semantic/analyze) +(require 'semantic/ctxt) +(require 'semantic/decorate) +(require 'semantic/format) + +(eval-when-compile + ;; For the semantic-find-tags-for-completion macro. + (require 'semantic/find)) + +;;; Code: + +(defvar semantic-complete-inline-overlay nil + "The overlay currently active while completing inline.") + +(defun semantic-completion-inline-active-p () + "Non-nil if inline completion is active." + (when (and semantic-complete-inline-overlay + (not (semantic-overlay-live-p semantic-complete-inline-overlay))) + (semantic-overlay-delete semantic-complete-inline-overlay) + (setq semantic-complete-inline-overlay nil)) + semantic-complete-inline-overlay) + +;;; ------------------------------------------------------------ +;;; MINIBUFFER or INLINE utils +;; +(defun semantic-completion-text () + "Return the text that is currently in the completion buffer. +For a minibuffer prompt, this is the minibuffer text. +For inline completion, this is the text wrapped in the inline completion +overlay." + (if semantic-complete-inline-overlay + (semantic-complete-inline-text) + (minibuffer-contents))) + +(defun semantic-completion-delete-text () + "Delete the text that is actively being completed. +Presumably if you call this you will insert something new there." + (if semantic-complete-inline-overlay + (semantic-complete-inline-delete-text) + (delete-minibuffer-contents))) + +(defun semantic-completion-message (fmt &rest args) + "Display the string FMT formatted with ARGS at the end of the minibuffer." + (if semantic-complete-inline-overlay + (apply 'message fmt args) + (message (concat (buffer-string) (apply 'format fmt args))))) + +;;; ------------------------------------------------------------ +;;; MINIBUFFER: Option Selection harnesses +;; +(defvar semantic-completion-collector-engine nil + "The tag collector for the current completion operation. +Value should be an object of a subclass of +`semantic-completion-engine-abstract'.") + +(defvar semantic-completion-display-engine nil + "The tag display engine for the current completion operation. +Value should be a ... what?") + +(defvar semantic-complete-key-map + (let ((km (make-sparse-keymap))) + (define-key km " " 'semantic-complete-complete-space) + (define-key km "\t" 'semantic-complete-complete-tab) + (define-key km "\C-m" 'semantic-complete-done) + (define-key km "\C-g" 'abort-recursive-edit) + (define-key km "\M-n" 'next-history-element) + (define-key km "\M-p" 'previous-history-element) + (define-key km "\C-n" 'next-history-element) + (define-key km "\C-p" 'previous-history-element) + ;; Add history navigation + km) + "Keymap used while completing across a list of tags.") + +(defvar semantic-completion-default-history nil + "Default history variable for any unhistoried prompt. +Keeps STRINGS only in the history.") + + +(defun semantic-complete-read-tag-engine (collector displayor prompt + default-tag initial-input + history) + "Read a semantic tag, and return a tag for the selection. +Argument COLLECTOR is an object which can be used to to calculate +a list of possible hits. See `semantic-completion-collector-engine' +for details on COLLECTOR. +Argumeng DISPLAYOR is an object used to display a list of possible +completions for a given prefix. See`semantic-completion-display-engine' +for details on DISPLAYOR. +PROMPT is a string to prompt with. +DEFAULT-TAG is a semantic tag or string to use as the default value. +If INITIAL-INPUT is non-nil, insert it in the minibuffer initially. +HISTORY is a symbol representing a variable to story the history in." + (let* ((semantic-completion-collector-engine collector) + (semantic-completion-display-engine displayor) + (semantic-complete-active-default nil) + (semantic-complete-current-matched-tag nil) + (default-as-tag (semantic-complete-default-to-tag default-tag)) + (default-as-string (when (semantic-tag-p default-as-tag) + (semantic-tag-name default-as-tag))) + ) + + (when default-as-string + ;; Add this to the prompt. + ;; + ;; I really want to add a lookup of the symbol in those + ;; tags available to the collector and only add it if it + ;; is available as a possibility, but I'm too lazy right + ;; now. + ;; + + ;; @todo - move from () to into the editable area + (if (string-match ":" prompt) + (setq prompt (concat + (substring prompt 0 (match-beginning 0)) + " (" default-as-string ")" + (substring prompt (match-beginning 0)))) + (setq prompt (concat prompt " (" default-as-string "): ")))) + ;; + ;; Perform the Completion + ;; + (unwind-protect + (read-from-minibuffer prompt + initial-input + semantic-complete-key-map + nil + (or history + 'semantic-completion-default-history) + default-tag) + (semantic-collector-cleanup semantic-completion-collector-engine) + (semantic-displayor-cleanup semantic-completion-display-engine) + ) + ;; + ;; Extract the tag from the completion machinery. + ;; + semantic-complete-current-matched-tag + )) + + +;;; Util for basic completion prompts +;; + +(defvar semantic-complete-active-default nil + "The current default tag calculated for this prompt.") + +(defun semantic-complete-default-to-tag (default) + "Convert a calculated or passed in DEFAULT into a tag." + (if (semantic-tag-p default) + ;; Just return what was passed in. + (setq semantic-complete-active-default default) + ;; If none was passed in, guess. + (if (null default) + (setq default (semantic-ctxt-current-thing))) + (if (null default) + ;; Do nothing + nil + ;; Turn default into something useful. + (let ((str + (cond + ;; Semantic-ctxt-current-symbol will return a list of + ;; strings. Technically, we should use the analyzer to + ;; fully extract what we need, but for now, just grab the + ;; first string + ((and (listp default) (stringp (car default))) + (car default)) + ((stringp default) + default) + ((symbolp default) + (symbol-name default)) + (t + (signal 'wrong-type-argument + (list default 'semantic-tag-p))))) + (tag nil)) + ;; Now that we have that symbol string, look it up using the active + ;; collector. If we get a match, use it. + (save-excursion + (semantic-collector-calculate-completions + semantic-completion-collector-engine + str nil)) + ;; Do we have the perfect match??? + (let ((ml (semantic-collector-current-exact-match + semantic-completion-collector-engine))) + (when ml + ;; We don't care about uniqueness. Just guess for convenience + (setq tag (semanticdb-find-result-nth-in-buffer ml 0)))) + ;; save it + (setq semantic-complete-active-default tag) + ;; Return it.. .whatever it may be + tag)))) + + +;;; Prompt Return Value +;; +;; Getting a return value out of this completion prompt is a bit +;; challenging. The read command returns the string typed in. +;; We need to convert this into a valid tag. We can exit the minibuffer +;; for different reasons. If we purposely exit, we must make sure +;; the focused tag is calculated... preferably once. +(defvar semantic-complete-current-matched-tag nil + "Variable used to pass the tags being matched to the prompt.") + +;; semantic-displayor-focus-abstract-child-p is part of the +;; semantic-displayor-focus-abstract class, defined later in this +;; file. +(declare-function semantic-displayor-focus-abstract-child-p "semantic/complete") + +(defun semantic-complete-current-match () + "Calculate a match from the current completion environment. +Save this in our completion variable. Make sure that variable +is cleared if any other keypress is made. +Return value can be: + tag - a single tag that has been matched. + string - a message to show in the minibuffer." + ;; Query the environment for an active completion. + (let ((collector semantic-completion-collector-engine) + (displayor semantic-completion-display-engine) + (contents (semantic-completion-text)) + matchlist + answer) + (if (string= contents "") + ;; The user wants the defaults! + (setq answer semantic-complete-active-default) + ;; This forces a full calculation of completion on CR. + (save-excursion + (semantic-collector-calculate-completions collector contents nil)) + (semantic-complete-try-completion) + (cond + ;; Input match displayor focus entry + ((setq answer (semantic-displayor-current-focus displayor)) + ;; We have answer, continue + ) + ;; One match from the collector + ((setq matchlist (semantic-collector-current-exact-match collector)) + (if (= (semanticdb-find-result-length matchlist) 1) + (setq answer (semanticdb-find-result-nth-in-buffer matchlist 0)) + (if (semantic-displayor-focus-abstract-child-p displayor) + ;; For focusing displayors, we can claim this is + ;; not unique. Multiple focuses can choose the correct + ;; one. + (setq answer "Not Unique") + ;; If we don't have a focusing displayor, we need to do something + ;; graceful. First, see if all the matches have the same name. + (let ((allsame t) + (firstname (semantic-tag-name + (car + (semanticdb-find-result-nth matchlist 0))) + ) + (cnt 1) + (max (semanticdb-find-result-length matchlist))) + (while (and allsame (< cnt max)) + (if (not (string= + firstname + (semantic-tag-name + (car + (semanticdb-find-result-nth matchlist cnt))))) + (setq allsame nil)) + (setq cnt (1+ cnt)) + ) + ;; Now we know if they are all the same. If they are, just + ;; accept the first, otherwise complain. + (if allsame + (setq answer (semanticdb-find-result-nth-in-buffer + matchlist 0)) + (setq answer "Not Unique")) + )))) + ;; No match + (t + (setq answer "No Match"))) + ) + ;; Set it into our completion target. + (when (semantic-tag-p answer) + (setq semantic-complete-current-matched-tag answer) + ;; Make sure it is up to date by clearing it if the user dares + ;; to touch the keyboard. + (add-hook 'pre-command-hook + (lambda () (setq semantic-complete-current-matched-tag nil))) + ) + ;; Return it + answer + )) + + +;;; Keybindings +;; +;; Keys are bound to to perform completion using our mechanisms. +;; Do that work here. +(defun semantic-complete-done () + "Accept the current input." + (interactive) + (let ((ans (semantic-complete-current-match))) + (if (stringp ans) + (semantic-completion-message (concat " [" ans "]")) + (exit-minibuffer))) + ) + +(defun semantic-complete-complete-space () + "Complete the partial input in the minibuffer." + (interactive) + (semantic-complete-do-completion t)) + +(defun semantic-complete-complete-tab () + "Complete the partial input in the minibuffer as far as possible." + (interactive) + (semantic-complete-do-completion)) + +;;; Completion Functions +;; +;; Thees routines are functional entry points to performing completion. +;; +(defun semantic-complete-hack-word-boundaries (original new) + "Return a string to use for completion. +ORIGINAL is the text in the minibuffer. +NEW is the new text to insert into the minibuffer. +Within the difference bounds of ORIGINAL and NEW, shorten NEW +to the nearest word boundary, and return that." + (save-match-data + (let* ((diff (substring new (length original))) + (end (string-match "\\>" diff)) + (start (string-match "\\<" diff))) + (cond + ((and start (> start 0)) + ;; If start is greater than 0, include only the new + ;; white-space stuff + (concat original (substring diff 0 start))) + (end + (concat original (substring diff 0 end))) + (t new))))) + +(defun semantic-complete-try-completion (&optional partial) + "Try a completion for the current minibuffer. +If PARTIAL, do partial completion stopping at spaces." + (let ((comp (semantic-collector-try-completion + semantic-completion-collector-engine + (semantic-completion-text)))) + (cond + ((null comp) + (semantic-completion-message " [No Match]") + (ding) + ) + ((stringp comp) + (if (string= (semantic-completion-text) comp) + (when partial + ;; Minibuffer isn't changing AND the text is not unique. + ;; Test for partial completion over a word separator character. + ;; If there is one available, use that so that SPC can + ;; act like a SPC insert key. + (let ((newcomp (semantic-collector-current-whitespace-completion + semantic-completion-collector-engine))) + (when newcomp + (semantic-completion-delete-text) + (insert newcomp)) + )) + (when partial + (let ((orig (semantic-completion-text))) + ;; For partial completion, we stop and step over + ;; word boundaries. Use this nifty function to do + ;; that calculation for us. + (setq comp + (semantic-complete-hack-word-boundaries orig comp)))) + ;; Do the replacement. + (semantic-completion-delete-text) + (insert comp)) + ) + ((and (listp comp) (semantic-tag-p (car comp))) + (unless (string= (semantic-completion-text) + (semantic-tag-name (car comp))) + ;; A fully unique completion was available. + (semantic-completion-delete-text) + (insert (semantic-tag-name (car comp)))) + ;; The match is complete + (if (= (length comp) 1) + (semantic-completion-message " [Complete]") + (semantic-completion-message " [Complete, but not unique]")) + ) + (t nil)))) + +(defun semantic-complete-do-completion (&optional partial inline) + "Do a completion for the current minibuffer. +If PARTIAL, do partial completion stopping at spaces. +if INLINE, then completion is happening inline in a buffer." + (let* ((collector semantic-completion-collector-engine) + (displayor semantic-completion-display-engine) + (contents (semantic-completion-text)) + (ans nil)) + + (save-excursion + (semantic-collector-calculate-completions collector contents partial)) + (let* ((na (semantic-complete-next-action partial))) + (cond + ;; We're all done, but only from a very specific + ;; area of completion. + ((eq na 'done) + (semantic-completion-message " [Complete]") + (setq ans 'done)) + ;; Perform completion + ((or (eq na 'complete) + (eq na 'complete-whitespace)) + (semantic-complete-try-completion partial) + (setq ans 'complete)) + ;; We need to display the completions. + ;; Set the completions into the display engine + ((or (eq na 'display) (eq na 'displayend)) + (semantic-displayor-set-completions + displayor + (or + (and (not (eq na 'displayend)) + (semantic-collector-current-exact-match collector)) + (semantic-collector-all-completions collector contents)) + contents) + ;; Ask the displayor to display them. + (semantic-displayor-show-request displayor)) + ((eq na 'scroll) + (semantic-displayor-scroll-request displayor) + ) + ((eq na 'focus) + (semantic-displayor-focus-next displayor) + (semantic-displayor-focus-request displayor) + ) + ((eq na 'empty) + (semantic-completion-message " [No Match]")) + (t nil))) + ans)) + + +;;; ------------------------------------------------------------ +;;; INLINE: tag completion harness +;; +;; Unlike the minibuffer, there is no mode nor other traditional +;; means of reading user commands in completion mode. Instead +;; we use a pre-command-hook to inset in our commands, and to +;; push ourselves out of this mode on alternate keypresses. +(defvar semantic-complete-inline-map + (let ((km (make-sparse-keymap))) + (define-key km "\C-i" 'semantic-complete-inline-TAB) + (define-key km "\M-p" 'semantic-complete-inline-up) + (define-key km "\M-n" 'semantic-complete-inline-down) + (define-key km "\C-m" 'semantic-complete-inline-done) + (define-key km "\C-\M-c" 'semantic-complete-inline-exit) + (define-key km "\C-g" 'semantic-complete-inline-quit) + (define-key km "?" + (lambda () (interactive) + (describe-variable 'semantic-complete-inline-map))) + km) + "Keymap used while performing Semantic inline completion. +\\{semantic-complete-inline-map}") + +(defface semantic-complete-inline-face + '((((class color) (background dark)) + (:underline "yellow")) + (((class color) (background light)) + (:underline "brown"))) + "*Face used to show the region being completed inline. +The face is used in `semantic-complete-inline-tag-engine'." + :group 'semantic-faces) + +(defun semantic-complete-inline-text () + "Return the text that is being completed inline. +Similar to `minibuffer-contents' when completing in the minibuffer." + (let ((s (semantic-overlay-start semantic-complete-inline-overlay)) + (e (semantic-overlay-end semantic-complete-inline-overlay))) + (if (= s e) + "" + (buffer-substring-no-properties s e )))) + +(defun semantic-complete-inline-delete-text () + "Delete the text currently being completed in the current buffer." + (delete-region + (semantic-overlay-start semantic-complete-inline-overlay) + (semantic-overlay-end semantic-complete-inline-overlay))) + +(defun semantic-complete-inline-done () + "This completion thing is DONE, OR, insert a newline." + (interactive) + (let* ((displayor semantic-completion-display-engine) + (tag (semantic-displayor-current-focus displayor))) + (if tag + (let ((txt (semantic-completion-text))) + (insert (substring (semantic-tag-name tag) + (length txt))) + (semantic-complete-inline-exit)) + + ;; Get whatever binding RET usually has. + (let ((fcn + (condition-case nil + (lookup-key (current-active-maps) (this-command-keys)) + (error + ;; I don't know why, but for some reason the above + ;; throws an error sometimes. + (lookup-key (current-global-map) (this-command-keys)) + )))) + (when fcn + (funcall fcn))) + ))) + +(defun semantic-complete-inline-quit () + "Quit an inline edit." + (interactive) + (semantic-complete-inline-exit) + (keyboard-quit)) + +(defun semantic-complete-inline-exit () + "Exit inline completion mode." + (interactive) + ;; Remove this hook FIRST! + (remove-hook 'pre-command-hook 'semantic-complete-pre-command-hook) + + (condition-case nil + (progn + (when semantic-completion-collector-engine + (semantic-collector-cleanup semantic-completion-collector-engine)) + (when semantic-completion-display-engine + (semantic-displayor-cleanup semantic-completion-display-engine)) + + (when semantic-complete-inline-overlay + (let ((wc (semantic-overlay-get semantic-complete-inline-overlay + 'window-config-start)) + (buf (semantic-overlay-buffer semantic-complete-inline-overlay)) + ) + (semantic-overlay-delete semantic-complete-inline-overlay) + (setq semantic-complete-inline-overlay nil) + ;; DONT restore the window configuration if we just + ;; switched windows! + (when (eq buf (current-buffer)) + (set-window-configuration wc)) + )) + + (setq semantic-completion-collector-engine nil + semantic-completion-display-engine nil)) + (error nil)) + + ;; Remove this hook LAST!!! + ;; This will force us back through this function if there was + ;; some sort of error above. + (remove-hook 'post-command-hook 'semantic-complete-post-command-hook) + + ;;(message "Exiting inline completion.") + ) + +(defun semantic-complete-pre-command-hook () + "Used to redefine what commands are being run while completing. +When installed as a `pre-command-hook' the special keymap +`semantic-complete-inline-map' is queried to replace commands normally run. +Commands which edit what is in the region of interest operate normally. +Commands which would take us out of the region of interest, or our +quit hook, will exit this completion mode." + (let ((fcn (lookup-key semantic-complete-inline-map + (this-command-keys) nil))) + (cond ((commandp fcn) + (setq this-command fcn)) + (t nil))) + ) + +(defun semantic-complete-post-command-hook () + "Used to determine if we need to exit inline completion mode. +If completion mode is active, check to see if we are within +the bounds of `semantic-complete-inline-overlay', or within +a reasonable distance." + (condition-case nil + ;; Exit if something bad happened. + (if (not semantic-complete-inline-overlay) + (progn + ;;(message "Inline Hook installed, but overlay deleted.") + (semantic-complete-inline-exit)) + ;; Exit if commands caused us to exit the area of interest + (let ((s (semantic-overlay-start semantic-complete-inline-overlay)) + (e (semantic-overlay-end semantic-complete-inline-overlay)) + (b (semantic-overlay-buffer semantic-complete-inline-overlay)) + (txt nil) + ) + (cond + ;; EXIT when we are no longer in a good place. + ((or (not (eq b (current-buffer))) + (< (point) s) + (> (point) e)) + ;;(message "Exit: %S %S %S" s e (point)) + (semantic-complete-inline-exit) + ) + ;; Exit if the user typed in a character that is not part + ;; of the symbol being completed. + ((and (setq txt (semantic-completion-text)) + (not (string= txt "")) + (and (/= (point) s) + (save-excursion + (forward-char -1) + (not (looking-at "\\(\\w\\|\\s_\\)"))))) + ;;(message "Non symbol character.") + (semantic-complete-inline-exit)) + ((lookup-key semantic-complete-inline-map + (this-command-keys) nil) + ;; If the last command was one of our completion commands, + ;; then do nothing. + nil + ) + (t + ;; Else, show completions now + (semantic-complete-inline-force-display) + + )))) + ;; If something goes terribly wrong, clean up after ourselves. + (error (semantic-complete-inline-exit)))) + +(defun semantic-complete-inline-force-display () + "Force the display of whatever the current completions are. +DO NOT CALL THIS IF THE INLINE COMPLETION ENGINE IS NOT ACTIVE." + (condition-case e + (save-excursion + (let ((collector semantic-completion-collector-engine) + (displayor semantic-completion-display-engine) + (contents (semantic-completion-text))) + (when collector + (semantic-collector-calculate-completions + collector contents nil) + (semantic-displayor-set-completions + displayor + (semantic-collector-all-completions collector contents) + contents) + ;; Ask the displayor to display them. + (semantic-displayor-show-request displayor)) + )) + (error (message "Bug Showing Completions: %S" e)))) + +(defun semantic-complete-inline-tag-engine + (collector displayor buffer start end) + "Perform completion based on semantic tags in a buffer. +Argument COLLECTOR is an object which can be used to to calculate +a list of possible hits. See `semantic-completion-collector-engine' +for details on COLLECTOR. +Argumeng DISPLAYOR is an object used to display a list of possible +completions for a given prefix. See`semantic-completion-display-engine' +for details on DISPLAYOR. +BUFFER is the buffer in which completion will take place. +START is a location for the start of the full symbol. +If the symbol being completed is \"foo.ba\", then START +is on the \"f\" character. +END is at the end of the current symbol being completed." + ;; Set us up for doing completion + (setq semantic-completion-collector-engine collector + semantic-completion-display-engine displayor) + ;; Create an overlay + (setq semantic-complete-inline-overlay + (semantic-make-overlay start end buffer nil t)) + (semantic-overlay-put semantic-complete-inline-overlay + 'face + 'semantic-complete-inline-face) + (semantic-overlay-put semantic-complete-inline-overlay + 'window-config-start + (current-window-configuration)) + ;; Install our command hooks + (add-hook 'pre-command-hook 'semantic-complete-pre-command-hook) + (add-hook 'post-command-hook 'semantic-complete-post-command-hook) + ;; Go! + (semantic-complete-inline-force-display) + ) + +;;; Inline Completion Keymap Functions +;; +(defun semantic-complete-inline-TAB () + "Perform inline completion." + (interactive) + (let ((cmpl (semantic-complete-do-completion nil t))) + (cond + ((eq cmpl 'complete) + (semantic-complete-inline-force-display)) + ((eq cmpl 'done) + (semantic-complete-inline-done)) + )) + ) + +(defun semantic-complete-inline-down() + "Focus forwards through the displayor." + (interactive) + (let ((displayor semantic-completion-display-engine)) + (semantic-displayor-focus-next displayor) + (semantic-displayor-focus-request displayor) + )) + +(defun semantic-complete-inline-up () + "Focus backwards through the displayor." + (interactive) + (let ((displayor semantic-completion-display-engine)) + (semantic-displayor-focus-previous displayor) + (semantic-displayor-focus-request displayor) + )) + + +;;; ------------------------------------------------------------ +;;; Interactions between collection and displaying +;; +;; Functional routines used to help collectors communicate with +;; the current displayor, or for the previous section. + +(defun semantic-complete-next-action (partial) + "Determine what the next completion action should be. +PARTIAL is non-nil if we are doing partial completion. +First, the collector can determine if we should perform a completion or not. +If there is nothing to complete, then the displayor determines if we are +to show a completion list, scroll, or perhaps do a focus (if it is capable.) +Expected return values are: + done -> We have a singular match + empty -> There are no matches to the current text + complete -> Perform a completion action + complete-whitespace -> Complete next whitespace type character. + display -> Show the list of completions + scroll -> The completions have been shown, and the user keeps hitting + the complete button. If possible, scroll the completions + focus -> The displayor knows how to shift focus among possible completions. + Let it do that. + displayend -> Whatever options the displayor had for repeating options, there + are none left. Try something new." + (let ((ans1 (semantic-collector-next-action + semantic-completion-collector-engine + partial)) + (ans2 (semantic-displayor-next-action + semantic-completion-display-engine)) + ) + (cond + ;; No collector answer, use displayor answer. + ((not ans1) + ans2) + ;; Displayor selection of 'scroll, 'display, or 'focus trumps + ;; 'done + ((and (eq ans1 'done) ans2) + ans2) + ;; Use ans1 when we have it. + (t + ans1)))) + + + +;;; ------------------------------------------------------------ +;;; Collection Engines +;; +;; Collection engines can scan tags from the current environment and +;; provide lists of possible completions. +;; +;; General features of the abstract collector: +;; * Cache completion lists between uses +;; * Cache itself per buffer. Handle reparse hooks +;; +;; Key Interface Functions to implement: +;; * semantic-collector-next-action +;; * semantic-collector-calculate-completions +;; * semantic-collector-try-completion +;; * semantic-collector-all-completions + +(defvar semantic-collector-per-buffer-list nil + "List of collectors active in this buffer.") +(make-variable-buffer-local 'semantic-collector-per-buffer-list) + +(defvar semantic-collector-list nil + "List of global collectors active this session.") + +(defclass semantic-collector-abstract () + ((buffer :initarg :buffer + :type buffer + :documentation "Originating buffer for this collector. +Some collectors use a given buffer as a starting place while looking up +tags.") + (cache :initform nil + :type (or null semanticdb-find-result-with-nil) + :documentation "Cache of tags. +These tags are re-used during a completion session. +Sometimes these tags are cached between completion sessions.") + (last-all-completions :initarg nil + :type semanticdb-find-result-with-nil + :documentation "Last result of `all-completions'. +This result can be used for refined completions as `last-prefix' gets +closer to a specific result.") + (last-prefix :type string + :protection :protected + :documentation "The last queried prefix. +This prefix can be used to cache intermediate completion offers. +making the action of homing in on a token faster.") + (last-completion :type (or null string) + :documentation "The last calculated completion. +This completion is calculated and saved for future use.") + (last-whitespace-completion :type (or null string) + :documentation "The last whitespace completion. +For partial completion, SPC will disabiguate over whitespace type +characters. This is the last calculated version.") + (current-exact-match :type list + :protection :protected + :documentation "The list of matched tags. +When tokens are matched, they are added to this list.") + ) + "Root class for completion engines. +The baseclass provides basic functionality for interacting with +a completion displayor object, and tracking the current progress +of a completion." + :abstract t) + +(defmethod semantic-collector-cleanup ((obj semantic-collector-abstract)) + "Clean up any mess this collector may have." + nil) + +(defmethod semantic-collector-next-action + ((obj semantic-collector-abstract) partial) + "What should we do next? OBJ can predict a next good action. +PARTIAL indicates if we are doing a partial completion." + (if (and (slot-boundp obj 'last-completion) + (string= (semantic-completion-text) (oref obj last-completion))) + (let* ((cem (semantic-collector-current-exact-match obj)) + (cemlen (semanticdb-find-result-length cem)) + (cac (semantic-collector-all-completions + obj (semantic-completion-text))) + (caclen (semanticdb-find-result-length cac))) + (cond ((and cem (= cemlen 1) + cac (> caclen 1) + (eq last-command this-command)) + ;; Defer to the displayor... + nil) + ((and cem (= cemlen 1)) + 'done) + ((and (not cem) (not cac)) + 'empty) + ((and partial (semantic-collector-try-completion-whitespace + obj (semantic-completion-text))) + 'complete-whitespace))) + 'complete)) + +(defmethod semantic-collector-last-prefix= ((obj semantic-collector-abstract) + last-prefix) + "Return non-nil if OBJ's prefix matches PREFIX." + (and (slot-boundp obj 'last-prefix) + (string= (oref obj last-prefix) last-prefix))) + +(defmethod semantic-collector-get-cache ((obj semantic-collector-abstract)) + "Get the raw cache of tags for completion. +Calculate the cache if there isn't one." + (or (oref obj cache) + (semantic-collector-calculate-cache obj))) + +(defmethod semantic-collector-calculate-completions-raw + ((obj semantic-collector-abstract) prefix completionlist) + "Calculate the completions for prefix from completionlist. +Output must be in semanticdb Find result format." + ;; Must output in semanticdb format + (let ((table (save-excursion + (set-buffer (oref obj buffer)) + semanticdb-current-table)) + (result (semantic-find-tags-for-completion + prefix + ;; To do this kind of search with a pre-built completion + ;; list, we need to strip it first. + (semanticdb-strip-find-results completionlist))) + ) + (if result + (list (cons table result))))) + +(defmethod semantic-collector-calculate-completions + ((obj semantic-collector-abstract) prefix partial) + "Calculate completions for prefix as setup for other queries." + (let* ((case-fold-search semantic-case-fold) + (same-prefix-p (semantic-collector-last-prefix= obj prefix)) + (completionlist + (if (or same-prefix-p + (and (slot-boundp obj 'last-prefix) + (eq (compare-strings (oref obj last-prefix) 0 nil + prefix 0 (length prefix)) + t))) + ;; New prefix is subset of old prefix + (oref obj last-all-completions) + (semantic-collector-get-cache obj))) + ;; Get the result + (answer (if same-prefix-p + completionlist + (semantic-collector-calculate-completions-raw + obj prefix completionlist)) + ) + (completion nil) + (complete-not-uniq nil) + ) + ;;(semanticdb-find-result-test answer) + (when (not same-prefix-p) + ;; Save results if it is interesting and beneficial + (oset obj last-prefix prefix) + (oset obj last-all-completions answer)) + ;; Now calculate the completion. + (setq completion (try-completion + prefix + (semanticdb-strip-find-results answer))) + (oset obj last-whitespace-completion nil) + (oset obj current-exact-match nil) + ;; Only do this if a completion was found. Letting a nil in + ;; could cause a full semanticdb search by accident. + (when completion + (oset obj last-completion + (cond + ;; Unique match in AC. Last completion is a match. + ;; Also set the current-exact-match. + ((eq completion t) + (oset obj current-exact-match answer) + prefix) + ;; It may be complete (a symbol) but still not unique. + ;; We can capture a match + ((setq complete-not-uniq + (semanticdb-find-tags-by-name + prefix + answer)) + (oset obj current-exact-match + complete-not-uniq) + prefix + ) + ;; Non unique match, return the string that handles + ;; completion + (t (or completion prefix)) + ))) + )) + +(defmethod semantic-collector-try-completion-whitespace + ((obj semantic-collector-abstract) prefix) + "For OBJ, do whatepsace completion based on PREFIX. +This implies that if there are two completions, one matching +the test \"preifx\\>\", and one not, the one matching the full +word version of PREFIX will be chosen, and that text returned. +This function requires that `semantic-collector-calculate-completions' +has been run first." + (let* ((ac (semantic-collector-all-completions obj prefix)) + (matchme (concat "^" prefix "\\>")) + (compare (semanticdb-find-tags-by-name-regexp matchme ac)) + (numtag (semanticdb-find-result-length compare)) + ) + (if compare + (let* ((idx 0) + (cutlen (1+ (length prefix))) + (twws (semanticdb-find-result-nth compare idx))) + ;; Is our tag with whitespace a match that has whitespace + ;; after it, or just an already complete symbol? + (while (and (< idx numtag) + (< (length (semantic-tag-name (car twws))) cutlen)) + (setq idx (1+ idx) + twws (semanticdb-find-result-nth compare idx))) + (when (and twws (car-safe twws)) + ;; If COMPARE has succeeded, then we should take the very + ;; first match, and extend prefix by one character. + (oset obj last-whitespace-completion + (substring (semantic-tag-name (car twws)) + 0 cutlen)))) + ))) + + +(defmethod semantic-collector-current-exact-match ((obj semantic-collector-abstract)) + "Return the active valid MATCH from the semantic collector. +For now, just return the first element from our list of available +matches. For semanticdb based results, make sure the file is loaded +into a buffer." + (when (slot-boundp obj 'current-exact-match) + (oref obj current-exact-match))) + +(defmethod semantic-collector-current-whitespace-completion ((obj semantic-collector-abstract)) + "Return the active whitespace completion value." + (when (slot-boundp obj 'last-whitespace-completion) + (oref obj last-whitespace-completion))) + +(defmethod semantic-collector-get-match ((obj semantic-collector-abstract)) + "Return the active valid MATCH from the semantic collector. +For now, just return the first element from our list of available +matches. For semanticdb based results, make sure the file is loaded +into a buffer." + (when (slot-boundp obj 'current-exact-match) + (semanticdb-find-result-nth-in-buffer (oref obj current-exact-match) 0))) + +(defmethod semantic-collector-all-completions + ((obj semantic-collector-abstract) prefix) + "For OBJ, retrieve all completions matching PREFIX. +The returned list consists of all the tags currently +matching PREFIX." + (when (slot-boundp obj 'last-all-completions) + (oref obj last-all-completions))) + +(defmethod semantic-collector-try-completion + ((obj semantic-collector-abstract) prefix) + "For OBJ, attempt to match PREFIX. +See `try-completion' for details on how this works. +Return nil for no match. +Return a string for a partial match. +For a unique match of PREFIX, return the list of all tags +with that name." + (if (slot-boundp obj 'last-completion) + (oref obj last-completion))) + +(defmethod semantic-collector-calculate-cache + ((obj semantic-collector-abstract)) + "Calculate the completion cache for OBJ." + nil + ) + +(defmethod semantic-collector-flush ((this semantic-collector-abstract)) + "Flush THIS collector object, clearing any caches and prefix." + (oset this cache nil) + (slot-makeunbound this 'last-prefix) + (slot-makeunbound this 'last-completion) + (slot-makeunbound this 'last-all-completions) + (slot-makeunbound this 'current-exact-match) + ) + +;;; PER BUFFER +;; +(defclass semantic-collector-buffer-abstract (semantic-collector-abstract) + () + "Root class for per-buffer completion engines. +These collectors track themselves on a per-buffer basis." + :abstract t) + +(defmethod constructor :STATIC ((this semantic-collector-buffer-abstract) + newname &rest fields) + "Reuse previously created objects of this type in buffer." + (let ((old nil) + (bl semantic-collector-per-buffer-list)) + (while (and bl (null old)) + (if (eq (object-class (car bl)) this) + (setq old (car bl)))) + (unless old + (let ((new (call-next-method))) + (add-to-list 'semantic-collector-per-buffer-list new) + (setq old new))) + (slot-makeunbound old 'last-completion) + (slot-makeunbound old 'last-prefix) + (slot-makeunbound old 'current-exact-match) + old)) + +;; Buffer specific collectors should flush themselves +(defun semantic-collector-buffer-flush (newcache) + "Flush all buffer collector objects. +NEWCACHE is the new tag table, but we ignore it." + (condition-case nil + (let ((l semantic-collector-per-buffer-list)) + (while l + (if (car l) (semantic-collector-flush (car l))) + (setq l (cdr l)))) + (error nil))) + +(add-hook 'semantic-after-toplevel-cache-change-hook + 'semantic-collector-buffer-flush) + +;;; DEEP BUFFER SPECIFIC COMPLETION +;; +(defclass semantic-collector-buffer-deep + (semantic-collector-buffer-abstract) + () + "Completion engine for tags in the current buffer. +When searching for a tag, uses semantic deep searche functions. +Basics search only in the current buffer.") + +(defmethod semantic-collector-calculate-cache + ((obj semantic-collector-buffer-deep)) + "Calculate the completion cache for OBJ. +Uses `semantic-flatten-tags-table'" + (oset obj cache + ;; Must create it in SEMANTICDB find format. + ;; ( ( DBTABLE TAG TAG ... ) ... ) + (list + (cons semanticdb-current-table + (semantic-flatten-tags-table (oref obj buffer)))))) + +;;; PROJECT SPECIFIC COMPLETION +;; +(defclass semantic-collector-project-abstract (semantic-collector-abstract) + ((path :initarg :path + :initform nil + :documentation "List of database tables to search. +At creation time, it can be anything accepted by +`semanticdb-find-translate-path' as a PATH argument.") + ) + "Root class for project wide completion engines. +Uses semanticdb for searching all tags in the current project." + :abstract t) + +;;; Project Search +(defclass semantic-collector-project (semantic-collector-project-abstract) + () + "Completion engine for tags in a project.") + + +(defmethod semantic-collector-calculate-completions-raw + ((obj semantic-collector-project) prefix completionlist) + "Calculate the completions for prefix from completionlist." + (semanticdb-find-tags-for-completion prefix (oref obj path))) + +;;; Brutish Project search +(defclass semantic-collector-project-brutish (semantic-collector-project-abstract) + () + "Completion engine for tags in a project.") + +(declare-function semanticdb-brute-deep-find-tags-for-completion + "semantic/db-find") + +(defmethod semantic-collector-calculate-completions-raw + ((obj semantic-collector-project-brutish) prefix completionlist) + "Calculate the completions for prefix from completionlist." + (require 'semantic/db-find) + (semanticdb-brute-deep-find-tags-for-completion prefix (oref obj path))) + +(defclass semantic-collector-analyze-completions (semantic-collector-abstract) + ((context :initarg :context + :type semantic-analyze-context + :documentation "An analysis context. +Specifies some context location from whence completion lists will be drawn." + ) + (first-pass-completions :type list + :documentation "List of valid completion tags. +This list of tags is generated when completion starts. All searches +derive from this list.") + ) + "Completion engine that uses the context analyzer to provide options. +The only options available for completion are those which can be logically +inserted into the current context.") + +(defmethod semantic-collector-calculate-completions-raw + ((obj semantic-collector-analyze-completions) prefix completionlist) + "calculate the completions for prefix from completionlist." + ;; if there are no completions yet, calculate them. + (if (not (slot-boundp obj 'first-pass-completions)) + (oset obj first-pass-completions + (semantic-analyze-possible-completions (oref obj context)))) + ;; search our cached completion list. make it look like a semanticdb + ;; results type. + (list (cons (save-excursion + (set-buffer (oref (oref obj context) buffer)) + semanticdb-current-table) + (semantic-find-tags-for-completion + prefix + (oref obj first-pass-completions))))) + + +;;; ------------------------------------------------------------ +;;; Tag List Display Engines +;; +;; A typical displayor accepts a pre-determined list of completions +;; generated by a collector. This format is in semanticdb search +;; form. This vaguely standard form is a bit challenging to navigate +;; because the tags do not contain buffer info, but the file assocated +;; with the tags preceed the tag in the list. +;; +;; Basic displayors don't care, and can strip the results. +;; Advanced highlighting displayors need to know when they need +;; to load a file so that the tag in question can be highlighted. +;; +;; Key interface methods to a displayor are: +;; * semantic-displayor-next-action +;; * semantic-displayor-set-completions +;; * semantic-displayor-current-focus +;; * semantic-displayor-show-request +;; * semantic-displayor-scroll-request +;; * semantic-displayor-focus-request + +(defclass semantic-displayor-abstract () + ((table :type (or null semanticdb-find-result-with-nil) + :initform nil + :protection :protected + :documentation "List of tags this displayor is showing.") + (last-prefix :type string + :protection :protected + :documentation "Prefix associated with slot `table'") + ) + "Abstract displayor baseclass. +Manages the display of some number of tags. +Provides the basics for a displayor, including interacting with +a collector, and tracking tables of completion to display." + :abstract t) + +(defmethod semantic-displayor-cleanup ((obj semantic-displayor-abstract)) + "Clean up any mess this displayor may have." + nil) + +(defmethod semantic-displayor-next-action ((obj semantic-displayor-abstract)) + "The next action to take on the minibuffer related to display." + (if (and (slot-boundp obj 'last-prefix) + (string= (oref obj last-prefix) (semantic-completion-text)) + (eq last-command this-command)) + 'scroll + 'display)) + +(defmethod semantic-displayor-set-completions ((obj semantic-displayor-abstract) + table prefix) + "Set the list of tags to be completed over to TABLE." + (oset obj table table) + (oset obj last-prefix prefix)) + +(defmethod semantic-displayor-show-request ((obj semantic-displayor-abstract)) + "A request to show the current tags table." + (ding)) + +(defmethod semantic-displayor-focus-request ((obj semantic-displayor-abstract)) + "A request to for the displayor to focus on some tag option." + (ding)) + +(defmethod semantic-displayor-scroll-request ((obj semantic-displayor-abstract)) + "A request to for the displayor to scroll the completion list (if needed)." + (scroll-other-window)) + +(defmethod semantic-displayor-focus-previous ((obj semantic-displayor-abstract)) + "Set the current focus to the previous item." + nil) + +(defmethod semantic-displayor-focus-next ((obj semantic-displayor-abstract)) + "Set the current focus to the next item." + nil) + +(defmethod semantic-displayor-current-focus ((obj semantic-displayor-abstract)) + "Return a single tag currently in focus. +This object type doesn't do focus, so will never have a focus object." + nil) + +;; Traditional displayor +(defcustom semantic-completion-displayor-format-tag-function + #'semantic-format-tag-name + "*A Tag format function to use when showing completions." + :group 'semantic + :type semantic-format-tag-custom-list) + +(defclass semantic-displayor-traditional (semantic-displayor-abstract) + () + "Display options in *Completions* buffer. +Traditional display mechanism for a list of possible completions. +Completions are showin in a new buffer and listed with the ability +to click on the items to aid in completion.") + +(defmethod semantic-displayor-show-request ((obj semantic-displayor-traditional)) + "A request to show the current tags table." + + ;; NOTE TO SELF. Find the character to type next, and emphesize it. + + (with-output-to-temp-buffer "*Completions*" + (display-completion-list + (mapcar semantic-completion-displayor-format-tag-function + (semanticdb-strip-find-results (oref obj table)))) + ) + ) + +;;; Abstract baseclass for any displayor which supports focus +(defclass semantic-displayor-focus-abstract (semantic-displayor-abstract) + ((focus :type number + :protection :protected + :documentation "A tag index from `table' which has focus. +Multiple calls to the display function can choose to focus on a +given tag, by highlighting its location.") + (find-file-focus + :allocation :class + :initform nil + :documentation + "Non-nil if focusing requires a tag's buffer be in memory.") + ) + "Abstract displayor supporting `focus'. +A displayor which has the ability to focus in on one tag. +Focusing is a way of differentiationg between multiple tags +which have the same name." + :abstract t) + +(defmethod semantic-displayor-next-action ((obj semantic-displayor-focus-abstract)) + "The next action to take on the minibuffer related to display." + (if (and (slot-boundp obj 'last-prefix) + (string= (oref obj last-prefix) (semantic-completion-text)) + (eq last-command this-command)) + (if (and + (slot-boundp obj 'focus) + (slot-boundp obj 'table) + (<= (semanticdb-find-result-length (oref obj table)) + (1+ (oref obj focus)))) + ;; We are at the end of the focus road. + 'displayend + ;; Focus on some item. + 'focus) + 'display)) + +(defmethod semantic-displayor-set-completions ((obj semantic-displayor-focus-abstract) + table prefix) + "Set the list of tags to be completed over to TABLE." + (call-next-method) + (slot-makeunbound obj 'focus)) + +(defmethod semantic-displayor-focus-previous ((obj semantic-displayor-focus-abstract)) + "Set the current focus to the previous item. +Not meaningful return value." + (when (and (slot-boundp obj 'table) (oref obj table)) + (with-slots (table) obj + (if (or (not (slot-boundp obj 'focus)) + (<= (oref obj focus) 0)) + (oset obj focus (1- (semanticdb-find-result-length table))) + (oset obj focus (1- (oref obj focus))) + ) + ))) + +(defmethod semantic-displayor-focus-next ((obj semantic-displayor-focus-abstract)) + "Set the current focus to the next item. +Not meaningful return value." + (when (and (slot-boundp obj 'table) (oref obj table)) + (with-slots (table) obj + (if (not (slot-boundp obj 'focus)) + (oset obj focus 0) + (oset obj focus (1+ (oref obj focus))) + ) + (if (<= (semanticdb-find-result-length table) (oref obj focus)) + (oset obj focus 0)) + ))) + +(defmethod semantic-displayor-focus-tag ((obj semantic-displayor-focus-abstract)) + "Return the next tag OBJ should focus on." + (when (and (slot-boundp obj 'table) (oref obj table)) + (with-slots (table) obj + (semanticdb-find-result-nth table (oref obj focus))))) + +(defmethod semantic-displayor-current-focus ((obj semantic-displayor-focus-abstract)) + "Return the tag currently in focus, or call parent method." + (if (and (slot-boundp obj 'focus) + (slot-boundp obj 'table) + ;; Only return the current focus IFF the minibuffer reflects + ;; the list this focus was derived from. + (slot-boundp obj 'last-prefix) + (string= (semantic-completion-text) (oref obj last-prefix)) + ) + ;; We need to focus + (if (oref obj find-file-focus) + (semanticdb-find-result-nth-in-buffer (oref obj table) (oref obj focus)) + ;; result-nth returns a cons with car being the tag, and cdr the + ;; database. + (car (semanticdb-find-result-nth (oref obj table) (oref obj focus)))) + ;; Do whatever + (call-next-method))) + +;;; Simple displayor which performs traditional display completion, +;; and also focuses with highlighting. +(defclass semantic-displayor-traditional-with-focus-highlight + (semantic-displayor-focus-abstract semantic-displayor-traditional) + ((find-file-focus :initform t)) + "Display completions in *Completions* buffer, with focus highlight. +A traditional displayor which can focus on a tag by showing it. +Same as `semantic-displayor-traditional', but with selection between +multiple tags with the same name done by 'focusing' on the source +location of the different tags to differentiate them.") + +(defmethod semantic-displayor-focus-request + ((obj semantic-displayor-traditional-with-focus-highlight)) + "Focus in on possible tag completions. +Focus is performed by cycling through the tags and highlighting +one in the source buffer." + (let* ((tablelength (semanticdb-find-result-length (oref obj table))) + (focus (semantic-displayor-focus-tag obj)) + ;; Raw tag info. + (rtag (car focus)) + (rtable (cdr focus)) + ;; Normalize + (nt (semanticdb-normalize-one-tag rtable rtag)) + (tag (cdr nt)) + (table (car nt)) + ) + ;; If we fail to normalize, resete. + (when (not tag) (setq table rtable tag rtag)) + ;; Do the focus. + (let ((buf (or (semantic-tag-buffer tag) + (and table (semanticdb-get-buffer table))))) + ;; If no buffer is provided, then we can make up a summary buffer. + (when (not buf) + (save-excursion + (set-buffer (get-buffer-create "*Completion Focus*")) + (erase-buffer) + (insert "Focus on tag: \n") + (insert (semantic-format-tag-summarize tag nil t) "\n\n") + (when table + (insert "From table: \n") + (insert (object-name table) "\n\n")) + (when buf + (insert "In buffer: \n\n") + (insert (format "%S" buf))) + (setq buf (current-buffer)))) + ;; Show the tag in the buffer. + (if (get-buffer-window buf) + (select-window (get-buffer-window buf)) + (switch-to-buffer-other-window buf t) + (select-window (get-buffer-window buf))) + ;; Now do some positioning + (unwind-protect + (if (semantic-tag-with-position-p tag) + ;; Full tag positional information available + (progn + (goto-char (semantic-tag-start tag)) + ;; This avoids a dangerous problem if we just loaded a tag + ;; from a file, but the original position was not updated + ;; in the TAG variable we are currently using. + (semantic-momentary-highlight-tag (semantic-current-tag)) + )) + (select-window (minibuffer-window))) + ;; Calculate text difference between contents and the focus item. + (let* ((mbc (semantic-completion-text)) + (ftn (semantic-tag-name tag)) + (diff (substring ftn (length mbc)))) + (semantic-completion-message + (format "%s [%d of %d matches]" diff (1+ (oref obj focus)) tablelength))) + ))) + + +;;; Tooltip completion lister +;; +;; Written and contributed by Masatake YAMATO <jet@gyve.org> +;; +;; Modified by Eric Ludlam for +;; * Safe compatibility for tooltip free systems. +;; * Don't use 'avoid package for tooltip positioning. + +(defclass semantic-displayor-tooltip (semantic-displayor-traditional) + ((max-tags :type integer + :initarg :max-tags + :initform 5 + :custom integer + :documentation + "Max number of tags displayed on tooltip at once. +If `force-show' is 1, this value is ignored with typing tab or space twice continuously. +if `force-show' is 0, this value is always ignored.") + (force-show :type integer + :initarg :force-show + :initform 1 + :custom (choice (const + :tag "Show when double typing" + 1) + (const + :tag "Show always" + 0) + (const + :tag "Show if the number of tags is less than `max-tags'." + -1)) + :documentation + "Control the behavior of the number of tags is greater than `max-tags'. +-1 means tags are never shown. +0 means the tags are always shown. +1 means tags are shown if space or tab is typed twice continuously.") + (typing-count :type integer + :initform 0 + :documentation + "Counter holding how many times the user types space or tab continuously before showing tags.") + (shown :type boolean + :initform nil + :documentation + "Flag representing whether tags is shown once or not.") + ) + "Display completions options in a tooltip. +Display mechanism using tooltip for a list of possible completions.") + +(defmethod initialize-instance :AFTER ((obj semantic-displayor-tooltip) &rest args) + "Make sure we have tooltips required." + (condition-case nil + (require 'tooltip) + (error nil)) + ) + +(defmethod semantic-displayor-show-request ((obj semantic-displayor-tooltip)) + "A request to show the current tags table." + (if (or (not (featurep 'tooltip)) (not tooltip-mode)) + ;; If we cannot use tooltips, then go to the normal mode with + ;; a traditional completion buffer. + (call-next-method) + (let* ((tablelong (semanticdb-strip-find-results (oref obj table))) + (table (semantic-unique-tag-table-by-name tablelong)) + (l (mapcar semantic-completion-displayor-format-tag-function table)) + (ll (length l)) + (typing-count (oref obj typing-count)) + (force-show (oref obj force-show)) + (matchtxt (semantic-completion-text)) + msg) + (if (or (oref obj shown) + (< ll (oref obj max-tags)) + (and (<= 0 force-show) + (< (1- force-show) typing-count))) + (progn + (oset obj typing-count 0) + (oset obj shown t) + (if (eq 1 ll) + ;; We Have only one possible match. There could be two cases. + ;; 1) input text != single match. + ;; --> Show it! + ;; 2) input text == single match. + ;; --> Complain about it, but still show the match. + (if (string= matchtxt (semantic-tag-name (car table))) + (setq msg (concat "[COMPLETE]\n" (car l))) + (setq msg (car l))) + ;; Create the long message. + (setq msg (mapconcat 'identity l "\n")) + ;; If there is nothing, say so! + (if (eq 0 (length msg)) + (setq msg "[NO MATCH]"))) + (semantic-displayor-tooltip-show msg)) + ;; The typing count determines if the user REALLY REALLY + ;; wanted to show that much stuff. Only increment + ;; if the current command is a completion command. + (if (and (stringp (this-command-keys)) + (string= (this-command-keys) "\C-i")) + (oset obj typing-count (1+ typing-count))) + ;; At this point, we know we have too many items. + ;; Lets be brave, and truncate l + (setcdr (nthcdr (oref obj max-tags) l) nil) + (setq msg (mapconcat 'identity l "\n")) + (cond + ((= force-show -1) + (semantic-displayor-tooltip-show (concat msg "\n..."))) + ((= force-show 1) + (semantic-displayor-tooltip-show (concat msg "\n(TAB for more)"))) + ))))) + +;;; Compatibility +;; +(eval-and-compile + (if (fboundp 'window-inside-edges) + ;; Emacs devel. + (defalias 'semantic-displayor-window-edges + 'window-inside-edges) + ;; Emacs 21 + (defalias 'semantic-displayor-window-edges + 'window-edges) + )) + +(defun semantic-displayor-point-position () + "Return the location of POINT as positioned on the selected frame. +Return a cons cell (X . Y)" + (let* ((frame (selected-frame)) + (left (frame-parameter frame 'left)) + (top (frame-parameter frame 'top)) + (point-pix-pos (posn-x-y (posn-at-point))) + (edges (window-inside-pixel-edges (selected-window)))) + (cons (+ (car point-pix-pos) (car edges) left) + (+ (cdr point-pix-pos) (cadr edges) top)))) + + +(defun semantic-displayor-tooltip-show (text) + "Display a tooltip with TEXT near cursor." + (let ((point-pix-pos (semantic-displayor-point-position)) + (tooltip-frame-parameters + (append tooltip-frame-parameters nil))) + (push + (cons 'left (+ (car point-pix-pos) (frame-char-width))) + tooltip-frame-parameters) + (push + (cons 'top (+ (cdr point-pix-pos) (frame-char-height))) + tooltip-frame-parameters) + (tooltip-show text))) + +(defmethod semantic-displayor-scroll-request ((obj semantic-displayor-tooltip)) + "A request to for the displayor to scroll the completion list (if needed)." + ;; Do scrolling in the tooltip. + (oset obj max-tags 30) + (semantic-displayor-show-request obj) + ) + +;; End code contributed by Masatake YAMATO <jet@gyve.org> + + +;;; Ghost Text displayor +;; +(defclass semantic-displayor-ghost (semantic-displayor-focus-abstract) + + ((ghostoverlay :type overlay + :documentation + "The overlay the ghost text is displayed in.") + (first-show :initform t + :documentation + "Non nil if we have not seen our first show request.") + ) + "Cycle completions inline with ghost text. +Completion displayor using ghost chars after point for focus options. +Whichever completion is currently in focus will be displayed as ghost +text using overlay options.") + +(defmethod semantic-displayor-next-action ((obj semantic-displayor-ghost)) + "The next action to take on the inline completion related to display." + (let ((ans (call-next-method)) + (table (when (slot-boundp obj 'table) + (oref obj table)))) + (if (and (eq ans 'displayend) + table + (= (semanticdb-find-result-length table) 1) + ) + nil + ans))) + +(defmethod semantic-displayor-cleanup ((obj semantic-displayor-ghost)) + "Clean up any mess this displayor may have." + (when (slot-boundp obj 'ghostoverlay) + (semantic-overlay-delete (oref obj ghostoverlay))) + ) + +(defmethod semantic-displayor-set-completions ((obj semantic-displayor-ghost) + table prefix) + "Set the list of tags to be completed over to TABLE." + (call-next-method) + + (semantic-displayor-cleanup obj) + ) + + +(defmethod semantic-displayor-show-request ((obj semantic-displayor-ghost)) + "A request to show the current tags table." +; (if (oref obj first-show) +; (progn +; (oset obj first-show nil) + (semantic-displayor-focus-next obj) + (semantic-displayor-focus-request obj) +; ) + ;; Only do the traditional thing if the first show request + ;; has been seen. Use the first one to start doing the ghost + ;; text display. +; (call-next-method) +; ) +) + +(defmethod semantic-displayor-focus-request + ((obj semantic-displayor-ghost)) + "Focus in on possible tag completions. +Focus is performed by cycling through the tags and showing a possible +completion text in ghost text." + (let* ((tablelength (semanticdb-find-result-length (oref obj table))) + (focus (semantic-displayor-focus-tag obj)) + (tag (car focus)) + ) + (if (not tag) + (semantic-completion-message "No tags to focus on.") + ;; Display the focus completion as ghost text after the current + ;; inline text. + (when (or (not (slot-boundp obj 'ghostoverlay)) + (not (semantic-overlay-live-p (oref obj ghostoverlay)))) + (oset obj ghostoverlay + (semantic-make-overlay (point) (1+ (point)) (current-buffer) t))) + + (let* ((lp (semantic-completion-text)) + (os (substring (semantic-tag-name tag) (length lp))) + (ol (oref obj ghostoverlay)) + ) + + (put-text-property 0 (length os) 'face 'region os) + + (semantic-overlay-put + ol 'display (concat os (buffer-substring (point) (1+ (point))))) + ) + ;; Calculate text difference between contents and the focus item. + (let* ((mbc (semantic-completion-text)) + (ftn (concat (semantic-tag-name tag))) + ) + (put-text-property (length mbc) (length ftn) 'face + 'bold ftn) + (semantic-completion-message + (format "%s [%d of %d matches]" ftn (1+ (oref obj focus)) tablelength))) + ))) + + +;;; ------------------------------------------------------------ +;;; Specific queries +;; +(defvar semantic-complete-inline-custom-type + (append '(radio) + (mapcar + (lambda (class) + (let* ((C (intern (car class))) + (doc (documentation-property C 'variable-documentation)) + (doc1 (car (split-string doc "\n"))) + ) + (list 'const + :tag doc1 + C))) + (eieio-build-class-alist semantic-displayor-abstract t)) + ) + "Possible options for inlince completion displayors. +Use this to enable custom editing.") + +(defcustom semantic-complete-inline-analyzer-displayor-class + 'semantic-displayor-traditional + "*Class for displayor to use with inline completion." + :group 'semantic + :type semantic-complete-inline-custom-type + ) + +(defun semantic-complete-read-tag-buffer-deep (prompt &optional + default-tag + initial-input + history) + "Ask for a tag by name from the current buffer. +Available tags are from the current buffer, at any level. +Completion options are presented in a traditional way, with highlighting +to resolve same-name collisions. +PROMPT is a string to prompt with. +DEFAULT-TAG is a semantic tag or string to use as the default value. +If INITIAL-INPUT is non-nil, insert it in the minibuffer initially. +HISTORY is a symbol representing a variable to store the history in." + (semantic-complete-read-tag-engine + (semantic-collector-buffer-deep prompt :buffer (current-buffer)) + (semantic-displayor-traditional-with-focus-highlight "simple") + ;;(semantic-displayor-tooltip "simple") + prompt + default-tag + initial-input + history) + ) + +(defun semantic-complete-read-tag-project (prompt &optional + default-tag + initial-input + history) + "Ask for a tag by name from the current project. +Available tags are from the current project, at the top level. +Completion options are presented in a traditional way, with highlighting +to resolve same-name collisions. +PROMPT is a string to prompt with. +DEFAULT-TAG is a semantic tag or string to use as the default value. +If INITIAL-INPUT is non-nil, insert it in the minibuffer initially. +HISTORY is a symbol representing a variable to store the history in." + (semantic-complete-read-tag-engine + (semantic-collector-project-brutish prompt + :buffer (current-buffer) + :path (current-buffer) + ) + (semantic-displayor-traditional-with-focus-highlight "simple") + prompt + default-tag + initial-input + history) + ) + +(defun semantic-complete-inline-tag-project () + "Complete a symbol name by name from within the current project. +This is similar to `semantic-complete-read-tag-project', except +that the completion interaction is in the buffer where the context +was calculated from. +Customize `semantic-complete-inline-analyzer-displayor-class' +to control how completion options are displayed. +See `semantic-complete-inline-tag-engine' for details on how +completion works." + (let* ((collector (semantic-collector-project-brutish + "inline" + :buffer (current-buffer) + :path (current-buffer))) + (sbounds (semantic-ctxt-current-symbol-and-bounds)) + (syms (car sbounds)) + (start (car (nth 2 sbounds))) + (end (cdr (nth 2 sbounds))) + (rsym (reverse syms)) + (thissym (nth 1 sbounds)) + (nextsym (car-safe (cdr rsym))) + (complst nil)) + (when (and thissym (or (not (string= thissym "")) + nextsym)) + ;; Do a quick calcuation of completions. + (semantic-collector-calculate-completions + collector thissym nil) + ;; Get the master list + (setq complst (semanticdb-strip-find-results + (semantic-collector-all-completions collector thissym))) + ;; Shorten by name + (setq complst (semantic-unique-tag-table-by-name complst)) + (if (or (and (= (length complst) 1) + ;; Check to see if it is the same as what is there. + ;; if so, we can offer to complete. + (let ((compname (semantic-tag-name (car complst)))) + (not (string= compname thissym)))) + (> (length complst) 1)) + ;; There are several options. Do the completion. + (semantic-complete-inline-tag-engine + collector + (funcall semantic-complete-inline-analyzer-displayor-class + "inline displayor") + ;;(semantic-displayor-tooltip "simple") + (current-buffer) + start end)) + ))) + +(defun semantic-complete-read-tag-analyzer (prompt &optional + context + history) + "Ask for a tag by name based on the current context. +The function `semantic-analyze-current-context' is used to +calculate the context. `semantic-analyze-possible-completions' is used +to generate the list of possible completions. +PROMPT is the first part of the prompt. Additional prompt +is added based on the contexts full prefix. +CONTEXT is the semantic analyzer context to start with. +HISTORY is a symbol representing a variable to stor the history in. +usually a default-tag and initial-input are available for completion +prompts. these are calculated from the CONTEXT variable passed in." + (if (not context) (setq context (semantic-analyze-current-context (point)))) + (let* ((syms (semantic-ctxt-current-symbol (point))) + (inp (car (reverse syms)))) + (setq syms (nreverse (cdr (nreverse syms)))) + (semantic-complete-read-tag-engine + (semantic-collector-analyze-completions + prompt + :buffer (oref context buffer) + :context context) + (semantic-displayor-traditional-with-focus-highlight "simple") + (save-excursion + (set-buffer (oref context buffer)) + (goto-char (cdr (oref context bounds))) + (concat prompt (mapconcat 'identity syms ".") + (if syms "." "") + )) + nil + inp + history))) + +(defun semantic-complete-inline-analyzer (context) + "Complete a symbol name by name based on the current context. +This is similar to `semantic-complete-read-tag-analyze', except +that the completion interaction is in the buffer where the context +was calculated from. +CONTEXT is the semantic analyzer context to start with. +Customize `semantic-complete-inline-analyzer-displayor-class' +to control how completion options are displayed. + +See `semantic-complete-inline-tag-engine' for details on how +completion works." + (if (not context) (setq context (semantic-analyze-current-context (point)))) + (if (not context) (error "Nothing to complete on here")) + (let* ((collector (semantic-collector-analyze-completions + "inline" + :buffer (oref context buffer) + :context context)) + (syms (semantic-ctxt-current-symbol (point))) + (rsym (reverse syms)) + (thissym (car rsym)) + (nextsym (car-safe (cdr rsym))) + (complst nil)) + (when (and thissym (or (not (string= thissym "")) + nextsym)) + ;; Do a quick calcuation of completions. + (semantic-collector-calculate-completions + collector thissym nil) + ;; Get the master list + (setq complst (semanticdb-strip-find-results + (semantic-collector-all-completions collector thissym))) + ;; Shorten by name + (setq complst (semantic-unique-tag-table-by-name complst)) + (if (or (and (= (length complst) 1) + ;; Check to see if it is the same as what is there. + ;; if so, we can offer to complete. + (let ((compname (semantic-tag-name (car complst)))) + (not (string= compname thissym)))) + (> (length complst) 1)) + ;; There are several options. Do the completion. + (semantic-complete-inline-tag-engine + collector + (funcall semantic-complete-inline-analyzer-displayor-class + "inline displayor") + ;;(semantic-displayor-tooltip "simple") + (oref context buffer) + (car (oref context bounds)) + (cdr (oref context bounds)) + )) + ))) + +(defcustom semantic-complete-inline-analyzer-idle-displayor-class + 'semantic-displayor-ghost + "*Class for displayor to use with inline completion at idle time." + :group 'semantic + :type semantic-complete-inline-custom-type + ) + +(defun semantic-complete-inline-analyzer-idle (context) + "Complete a symbol name by name based on the current context for idle time. +CONTEXT is the semantic analyzer context to start with. +This function is used from `semantic-idle-completions-mode'. + +This is the same as `semantic-complete-inline-analyzer', except that +it uses `semantic-complete-inline-analyzer-idle-displayor-class' +to control how completions are displayed. + +See `semantic-complete-inline-tag-engine' for details on how +completion works." + (let ((semantic-complete-inline-analyzer-displayor-class + semantic-complete-inline-analyzer-idle-displayor-class)) + (semantic-complete-inline-analyzer context) + )) + + +;;;###autoload +(defun semantic-complete-jump-local () + "Jump to a semantic symbol." + (interactive) + (let ((tag (semantic-complete-read-tag-buffer-deep "Symbol: "))) + (when (semantic-tag-p tag) + (push-mark) + (goto-char (semantic-tag-start tag)) + (semantic-momentary-highlight-tag tag) + (message "%S: %s " + (semantic-tag-class tag) + (semantic-tag-name tag))))) + +;;;###autoload +(defun semantic-complete-jump () + "Jump to a semantic symbol." + (interactive) + (let* ((tag (semantic-complete-read-tag-project "Symbol: "))) + (when (semantic-tag-p tag) + (push-mark) + (semantic-go-to-tag tag) + (switch-to-buffer (current-buffer)) + (semantic-momentary-highlight-tag tag) + (message "%S: %s " + (semantic-tag-class tag) + (semantic-tag-name tag))))) + +;;;###autoload +(defun semantic-complete-analyze-and-replace () + "Perform prompt completion to do in buffer completion. +`semantic-analyze-possible-completions' is used to determine the +possible values. +The minibuffer is used to perform the completion. +The result is inserted as a replacement of the text that was there." + (interactive) + (let* ((c (semantic-analyze-current-context (point))) + (tag (save-excursion (semantic-complete-read-tag-analyzer "" c)))) + ;; Take tag, and replace context bound with its name. + (goto-char (car (oref c bounds))) + (delete-region (point) (cdr (oref c bounds))) + (insert (semantic-tag-name tag)) + (message "%S" (semantic-format-tag-summarize tag)))) + +;;;###autoload +(defun semantic-complete-analyze-inline () + "Perform prompt completion to do in buffer completion. +`semantic-analyze-possible-completions' is used to determine the +possible values. +The function returns immediately, leaving the buffer in a mode that +will perform the completion. +Configure `semantic-complete-inline-analyzer-displayor-class' to change +how completion options are displayed." + (interactive) + ;; Only do this if we are not already completing something. + (if (not (semantic-completion-inline-active-p)) + (semantic-complete-inline-analyzer + (semantic-analyze-current-context (point)))) + ;; Report a message if things didn't startup. + (if (and (interactive-p) + (not (semantic-completion-inline-active-p))) + (message "Inline completion not needed.") + ;; Since this is most likely bound to something, and not used + ;; at idle time, throw in a TAB for good measure. + (semantic-complete-inline-TAB) + )) + +;;;###autoload +(defun semantic-complete-analyze-inline-idle () + "Perform prompt completion to do in buffer completion. +`semantic-analyze-possible-completions' is used to determine the +possible values. +The function returns immediately, leaving the buffer in a mode that +will perform the completion. +Configure `semantic-complete-inline-analyzer-idle-displayor-class' +to change how completion options are displayed." + (interactive) + ;; Only do this if we are not already completing something. + (if (not (semantic-completion-inline-active-p)) + (semantic-complete-inline-analyzer-idle + (semantic-analyze-current-context (point)))) + ;; Report a message if things didn't startup. + (if (and (interactive-p) + (not (semantic-completion-inline-active-p))) + (message "Inline completion not needed.")) + ) + +;;;###autoload +(defun semantic-complete-self-insert (arg) + "Like `self-insert-command', but does completion afterwards. +ARG is passed to `self-insert-command'. If ARG is nil, +use `semantic-complete-analyze-inline' to complete." + (interactive "p") + ;; If we are already in a completion scenario, exit now, and then start over. + (semantic-complete-inline-exit) + + ;; Insert the key + (self-insert-command arg) + + ;; Prepare for doing completion, but exit quickly if there is keyboard + ;; input. + (when (and (not (semantic-exit-on-input 'csi + (semantic-fetch-tags) + (semantic-throw-on-input 'csi) + nil)) + (= arg 1) + (not (semantic-exit-on-input 'csi + (semantic-analyze-current-context) + (semantic-throw-on-input 'csi) + nil))) + (condition-case nil + (semantic-complete-analyze-inline) + ;; Ignore errors. Seems likely that we'll get some once in a while. + (error nil)) + )) + +(provide 'semantic/complete) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/complete" +;; End: + +;;; semantic/complete.el ends here diff --git a/lisp/cedet/semantic/ctxt.el b/lisp/cedet/semantic/ctxt.el new file mode 100644 index 00000000000..ab72f4d4e7f --- /dev/null +++ b/lisp/cedet/semantic/ctxt.el @@ -0,0 +1,621 @@ +;;; semantic/ctxt.el --- Context calculations for Semantic tools. + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 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: +;; +;; Semantic, as a tool, provides a nice list of searchable tags. +;; That information can provide some very accurate answers if the current +;; context of a position is known. +;; +;; This library provides the hooks needed for a language to specify how +;; the current context is calculated. +;; +(require 'semantic) + +;;; Code: +(defvar semantic-command-separation-character + ";" + "String which indicates the end of a command. +Used for identifying the end of a single command.") +(make-variable-buffer-local 'semantic-command-separation-character) + +(defvar semantic-function-argument-separation-character + "," + "String which indicates the end of an argument. +Used for identifying arguments to functions.") +(make-variable-buffer-local 'semantic-function-argument-separation-character) + +;;; Local Contexts +;; +;; These context are nested blocks of code, such as code in an +;; if clause +(declare-function semantic-current-tag-of-class "semantic/find") + +(define-overloadable-function semantic-up-context (&optional point bounds-type) + "Move point up one context from POINT. +Return non-nil if there are no more context levels. +Overloaded functions using `up-context' take no parameters. +BOUNDS-TYPE is a symbol representing a tag class to restrict +movement to. If this is nil, 'function is used. +This will find the smallest tag of that class (function, variable, +type, etc) and make sure non-nil is returned if you cannot +go up past the bounds of that tag." + (require 'semantic/find) + (if point (goto-char point)) + (let ((nar (semantic-current-tag-of-class (or bounds-type 'function)))) + (if nar + (semantic-with-buffer-narrowed-to-tag nar (:override-with-args ())) + (when bounds-type + (error "No context of type %s to advance in" bounds-type)) + (:override-with-args ())))) + +(defun semantic-up-context-default () + "Move the point up and out one context level. +Works with languages that use parenthetical grouping." + ;; By default, assume that the language uses some form of parenthetical + ;; do dads for their context. + (condition-case nil + (progn + (up-list -1) + nil) + (error t))) + +(define-overloadable-function semantic-beginning-of-context (&optional point) + "Move POINT to the beginning of the current context. +Return non-nil if there is no upper context. +The default behavior uses `semantic-up-context'.") + +(defun semantic-beginning-of-context-default (&optional point) + "Move POINT to the beginning of the current context via parenthisis. +Return non-nil if there is no upper context." + (if point (goto-char point)) + (if (semantic-up-context) + t + (forward-char 1) + nil)) + +(define-overloadable-function semantic-end-of-context (&optional point) + "Move POINT to the end of the current context. +Return non-nil if there is no upper context. +Be default, this uses `semantic-up-context', and assumes parenthetical +block delimiters.") + +(defun semantic-end-of-context-default (&optional point) + "Move POINT to the end of the current context via parenthisis. +Return non-nil if there is no upper context." + (if point (goto-char point)) + (let ((start (point))) + (if (semantic-up-context) + t + ;; Go over the list, and back over the end parenthisis. + (condition-case nil + (progn + (forward-sexp 1) + (forward-char -1)) + (error + ;; If an error occurs, get the current tag from the cache, + ;; and just go to the end of that. Make sure we end up at least + ;; where start was so parse-region type calls work. + (if (semantic-current-tag) + (progn + (goto-char (semantic-tag-end (semantic-current-tag))) + (when (< (point) start) + (goto-char start))) + (goto-char start)) + t))) + nil)) + +(defun semantic-narrow-to-context () + "Narrow the buffer to the extent of the current context." + (let (b e) + (save-excursion + (if (semantic-beginning-of-context) + nil + (setq b (point)))) + (save-excursion + (if (semantic-end-of-context) + nil + (setq e (point)))) + (if (and b e) (narrow-to-region b e)))) + +(defmacro semantic-with-buffer-narrowed-to-context (&rest body) + "Execute BODY with the buffer narrowed to the current context." + `(save-restriction + (semantic-narrow-to-context) + ,@body)) +(put 'semantic-with-buffer-narrowed-to-context 'lisp-indent-function 0) +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec semantic-with-buffer-narrowed-to-context + (def-body)))) + +;;; Local Variables +;; +;; +(define-overloadable-function semantic-get-local-variables (&optional point) + "Get the local variables based on POINT's context. +Local variables are returned in Semantic tag format. +This can be overriden with `get-local-variables'." + ;; The working status is to let the parser work properly + (let ((semantic--progress-reporter + (make-progress-reporter (semantic-parser-working-message "Local") + 0 100))) + (save-excursion + (if point (goto-char point)) + (let* ((semantic-working-type nil) + ;; Disable parsing messages + (case-fold-search semantic-case-fold)) + (:override-with-args ()))))) + +(defun semantic-get-local-variables-default () + "Get local values from a specific context. +Uses the bovinator with the special top-symbol `bovine-inner-scope' +to collect tags, such as local variables or prototypes." + ;; This assumes a bovine parser. Make sure we don't do + ;; anything in that case. + (when (and semantic--parse-table (not (eq semantic--parse-table t)) + (not (semantic-parse-tree-unparseable-p))) + (let ((vars (semantic-get-cache-data 'get-local-variables))) + (if vars + (progn + ;;(message "Found cached vars.") + vars) + (let ((vars2 nil) + ;; We want nothing to do with funny syntaxing while doing this. + (semantic-unmatched-syntax-hook nil) + (start (point)) + (firstusefulstart nil) + ) + (while (not (semantic-up-context (point) 'function)) + (when (not vars) + (setq firstusefulstart (point))) + (save-excursion + (forward-char 1) + (setq vars + ;; Note to self: semantic-parse-region returns cooked + ;; but unlinked tags. File information is lost here + ;; and is added next. + (append (semantic-parse-region + (point) + (save-excursion (semantic-end-of-context) (point)) + 'bovine-inner-scope + nil + t) + vars)))) + ;; Modify the tags in place. + (setq vars2 vars) + (while vars2 + (semantic--tag-put-property (car vars2) :filename (buffer-file-name)) + (setq vars2 (cdr vars2))) + ;; Hash our value into the first context that produced useful results. + (when (and vars firstusefulstart) + (let ((end (save-excursion + (goto-char firstusefulstart) + (save-excursion + (unless (semantic-end-of-context) + (point)))))) + ;;(message "Caching values %d->%d." firstusefulstart end) + (semantic-cache-data-to-buffer + (current-buffer) firstusefulstart + (or end + ;; If the end-of-context fails, + ;; just use our cursor starting + ;; position. + start) + vars 'get-local-variables 'exit-cache-zone)) + ) + ;; Return our list. + vars))))) + +(define-overloadable-function semantic-get-local-arguments (&optional point) + "Get arguments (variables) from the current context at POINT. +Parameters are available if the point is in a function or method. +Return a list of tags unlinked from the originating buffer. +Arguments are obtained by overriding `get-local-arguments', or by the +default function `semantic-get-local-arguments-default'. This, must +return a list of tags, or a list of strings that will be converted to +tags." + (save-excursion + (if point (goto-char point)) + (let* ((case-fold-search semantic-case-fold) + (args (:override-with-args ())) + arg tags) + ;; Convert unsafe arguments to the right thing. + (while args + (setq arg (car args) + args (cdr args) + tags (cons (cond + ((semantic-tag-p arg) + ;; Return a copy of tag without overlay. + ;; The overlay is preserved. + (semantic-tag-copy arg nil t)) + ((stringp arg) + (semantic--tag-put-property + (semantic-tag-new-variable arg nil nil) + :filename (buffer-file-name))) + (t + (error "Unknown parameter element %S" arg))) + tags))) + (nreverse tags)))) + +(defun semantic-get-local-arguments-default () + "Get arguments (variables) from the current context. +Parameters are available if the point is in a function or method." + (let ((tag (semantic-current-tag))) + (if (and tag (semantic-tag-of-class-p tag 'function)) + (semantic-tag-function-arguments tag)))) + +(define-overloadable-function semantic-get-all-local-variables (&optional point) + "Get all local variables for this context, and parent contexts. +Local variables are returned in Semantic tag format. +Be default, this gets local variables, and local arguments. +Optional argument POINT is the location to start getting the variables from.") + +(defun semantic-get-all-local-variables-default (&optional point) + "Get all local variables for this context. +Optional argument POINT is the location to start getting the variables from. +That is a cons (LOCAL-ARGUMENTS . LOCAL-VARIABLES) where: + +- LOCAL-ARGUMENTS is collected by `semantic-get-local-arguments'. +- LOCAL-VARIABLES is collected by `semantic-get-local-variables'." + (save-excursion + (if point (goto-char point)) + (let ((case-fold-search semantic-case-fold)) + (append (semantic-get-local-arguments) + (semantic-get-local-variables))))) + +;;; Local context parsing +;; +;; Context parsing assumes a series of language independent commonalities. +;; These terms are used to describe those contexts: +;; +;; command - One command in the language. +;; symbol - The symbol the cursor is on. +;; This would include a series of type/field when applicable. +;; assignment - The variable currently being assigned to +;; function - The function call the cursor is on/in +;; argument - The index to the argument the cursor is on. +;; +;; +(define-overloadable-function semantic-end-of-command () + "Move to the end of the current command. +Be default, uses `semantic-command-separation-character'.") + +(defun semantic-end-of-command-default () + "Move to the end of the current command. +Depends on `semantic-command-separation-character' to find the +beginning and end of a command." + (semantic-with-buffer-narrowed-to-context + (let ((case-fold-search semantic-case-fold)) + (with-syntax-table semantic-lex-syntax-table + + (if (re-search-forward (regexp-quote semantic-command-separation-character) + nil t) + (forward-char -1) + ;; If there wasn't a command after this, we are the last + ;; command, and we are incomplete. + (goto-char (point-max))))))) + +(define-overloadable-function semantic-beginning-of-command () + "Move to the beginning of the current command. +Be default, uses `semantic-command-separation-character'.") + +(defun semantic-beginning-of-command-default () + "Move to the beginning of the current command. +Depends on `semantic-command-separation-character' to find the +beginning and end of a command." + (semantic-with-buffer-narrowed-to-context + (with-syntax-table semantic-lex-syntax-table + (let ((case-fold-search semantic-case-fold)) + (skip-chars-backward semantic-command-separation-character) + (if (re-search-backward (regexp-quote semantic-command-separation-character) + nil t) + (goto-char (match-end 0)) + ;; If there wasn't a command after this, we are the last + ;; command, and we are incomplete. + (goto-char (point-min))) + (skip-chars-forward " \t\n") + )))) + + +(defsubst semantic-point-at-beginning-of-command () + "Return the point at the beginning of the current command." + (save-excursion (semantic-beginning-of-command) (point))) + +(defsubst semantic-point-at-end-of-command () + "Return the point at the beginning of the current command." + (save-excursion (semantic-end-of-command) (point))) + +(defsubst semantic-narrow-to-command () + "Narrow the current buffer to the current command." + (narrow-to-region (semantic-point-at-beginning-of-command) + (semantic-point-at-end-of-command))) + +(defmacro semantic-with-buffer-narrowed-to-command (&rest body) + "Execute BODY with the buffer narrowed to the current command." + `(save-restriction + (semantic-narrow-to-command) + ,@body)) +(put 'semantic-with-buffer-narrowed-to-command 'lisp-indent-function 0) +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec semantic-with-buffer-narrowed-to-command + (def-body)))) + + +(define-overloadable-function semantic-ctxt-current-symbol (&optional point) + "Return the current symbol the cursor is on at POINT in a list. +The symbol includes all logical parts of a complex reference. +For example, in C the statement: + this.that().entry + +Would be object `this' calling method `that' which returns some structure +whose field `entry' is being reference. In this case, this function +would return the list: + ( \"this\" \"that\" \"entry\" )") + +(defun semantic-ctxt-current-symbol-default (&optional point) + "Return the current symbol the cursor is on at POINT in a list. +This will include a list of type/field names when applicable. +Depends on `semantic-type-relation-separator-character'." + (save-excursion + (if point (goto-char point)) + (let* ((fieldsep1 (mapconcat (lambda (a) (regexp-quote a)) + semantic-type-relation-separator-character + "\\|")) + ;; NOTE: The [ \n] expression below should used \\s-, but that + ;; doesn't work in C since \n means end-of-comment, and isn't + ;; really whitespace. + (fieldsep (concat "[ \t\n\r]*\\(" fieldsep1 "\\)[ \t\n\r]*\\(\\w\\|\\s_\\)")) + (case-fold-search semantic-case-fold) + (symlist nil) + end) + (with-syntax-table semantic-lex-syntax-table + (save-excursion + (cond ((looking-at "\\w\\|\\s_") + ;; In the middle of a symbol, move to the end. + (forward-sexp 1)) + ((looking-at fieldsep1) + ;; We are in a find spot.. do nothing. + nil + ) + ((save-excursion + (and (condition-case nil + (progn (forward-sexp -1) + (forward-sexp 1) + t) + (error nil)) + (looking-at fieldsep1))) + (setq symlist (list "")) + (forward-sexp -1) + ;; Skip array expressions. + (while (looking-at "\\s(") (forward-sexp -1)) + (forward-sexp 1)) + ) + ;; Set our end point. + (setq end (point)) + + ;; Now that we have gotten started, lets do the rest. + (condition-case nil + (while (save-excursion + (forward-char -1) + (looking-at "\\w\\|\\s_")) + ;; We have a symbol.. Do symbol things + (forward-sexp -1) + (setq symlist (cons (buffer-substring-no-properties (point) end) + symlist)) + ;; Skip the next syntactic expression backwards, then go forwards. + (let ((cp (point))) + (forward-sexp -1) + (forward-sexp 1) + ;; If we end up at the same place we started, we are at the + ;; beginning of a buffer, or narrowed to a command and + ;; have to stop. + (if (<= cp (point)) (error nil))) + (if (looking-at fieldsep) + (progn + (forward-sexp -1) + ;; Skip array expressions. + (while (and (looking-at "\\s(") (not (bobp))) + (forward-sexp -1)) + (forward-sexp 1) + (setq end (point))) + (error nil)) + ) + (error nil))) + symlist)))) + + +(define-overloadable-function semantic-ctxt-current-symbol-and-bounds (&optional point) + "Return the current symbol and bounds the cursor is on at POINT. +The symbol should be the same as returned by `semantic-ctxt-current-symbol'. +Return (PREFIX ENDSYM BOUNDS).") + +(defun semantic-ctxt-current-symbol-and-bounds-default (&optional point) + "Return the current symbol and bounds the cursor is on at POINT. +Uses `semantic-ctxt-current-symbol' to calculate the symbol. +Return (PREFIX ENDSYM BOUNDS)." + (save-excursion + (when point (goto-char (point))) + (let* ((prefix (semantic-ctxt-current-symbol)) + (endsym (car (reverse prefix))) + ;; @todo - Can we get this data direct from ctxt-current-symbol? + (bounds (save-excursion + (cond ((string= endsym "") + (cons (point) (point)) + ) + ((and prefix (looking-at endsym)) + (cons (point) (progn + (condition-case nil + (forward-sexp 1) + (error nil)) + (point)))) + (prefix + (condition-case nil + (cons (progn (forward-sexp -1) (point)) + (progn (forward-sexp 1) (point))) + (error nil))) + (t nil)))) + ) + (list prefix endsym bounds)))) + +(define-overloadable-function semantic-ctxt-current-assignment (&optional point) + "Return the current assignment near the cursor at POINT. +Return a list as per `semantic-ctxt-current-symbol'. +Return nil if there is nothing relevant.") + +(defun semantic-ctxt-current-assignment-default (&optional point) + "Return the current assignment near the cursor at POINT. +By default, assume that \"=\" indicates an assignment." + (if point (goto-char point)) + (let ((case-fold-search semantic-case-fold)) + (with-syntax-table semantic-lex-syntax-table + (condition-case nil + (semantic-with-buffer-narrowed-to-command + (save-excursion + (skip-chars-forward " \t=") + (condition-case nil (forward-char 1) (error nil)) + (re-search-backward "[^=]=\\([^=]\\|$\\)") + ;; We are at an equals sign. Go backwards a sexp, and + ;; we'll have the variable. Otherwise we threw an error + (forward-sexp -1) + (semantic-ctxt-current-symbol))) + (error nil))))) + +(define-overloadable-function semantic-ctxt-current-function (&optional point) + "Return the current function call the cursor is in at POINT. +The function returned is the one accepting the arguments that +the cursor is currently in. It will not return function symbol if the +cursor is on the text representing that function.") + +(defun semantic-ctxt-current-function-default (&optional point) + "Return the current function call the cursor is in at POINT. +The call will be identifed for C like langauges with the form + NAME ( args ... )" + (if point (goto-char point)) + (let ((case-fold-search semantic-case-fold)) + (with-syntax-table semantic-lex-syntax-table + (save-excursion + (semantic-up-context) + (when (looking-at "(") + (semantic-ctxt-current-symbol)))) + )) + +(define-overloadable-function semantic-ctxt-current-argument (&optional point) + "Return the index of the argument position the cursor is on at POINT.") + +(defun semantic-ctxt-current-argument-default (&optional point) + "Return the index of the argument the cursor is on at POINT. +Depends on `semantic-function-argument-separation-character'." + (if point (goto-char point)) + (let ((case-fold-search semantic-case-fold)) + (with-syntax-table semantic-lex-syntax-table + (when (semantic-ctxt-current-function) + (save-excursion + ;; Only get the current arg index if we are in function args. + (let ((p (point)) + (idx 1)) + (semantic-up-context) + (while (re-search-forward + (regexp-quote semantic-function-argument-separation-character) + p t) + (setq idx (1+ idx))) + idx)))))) + +(defun semantic-ctxt-current-thing () + "Calculate a thing identified by the current cursor position. +Calls previously defined `semantic-ctxt-current-...' calls until something +gets a match. See `semantic-ctxt-current-symbol', +`semantic-ctxt-current-function', and `semantic-ctxt-current-assignment' +for details on the return value." + (or (semantic-ctxt-current-symbol) + (semantic-ctxt-current-function) + (semantic-ctxt-current-assignment))) + +(define-overloadable-function semantic-ctxt-current-class-list (&optional point) + "Return a list of tag classes that are allowed at POINT. +If POINT is nil, the current buffer location is used. +For example, in Emacs Lisp, the symbol after a ( is most likely +a function. In a makefile, symbols after a : are rules, and symbols +after a $( are variables.") + +(defun semantic-ctxt-current-class-list-default (&optional point) + "Return a list of tag classes that are allowed at POINT. +Assume a functional typed language. Uses very simple rules." + (save-excursion + (if point (goto-char point)) + + (let ((tag (semantic-current-tag))) + (if tag + (cond ((semantic-tag-of-class-p tag 'function) + '(function variable type)) + ((or (semantic-tag-of-class-p tag 'type) + (semantic-tag-of-class-p tag 'variable)) + '(type)) + (t nil)) + '(type) + )))) + +;;;###autoload +(define-overloadable-function semantic-ctxt-current-mode (&optional point) + "Return the major mode active at POINT. +POINT defaults to the value of point in current buffer. +You should override this function in multiple mode buffers to +determine which major mode apply at point.") + +(defun semantic-ctxt-current-mode-default (&optional point) + "Return the major mode active at POINT. +POINT defaults to the value of point in current buffer. +This default implementation returns the current major mode." + major-mode) + +;;; Scoped Types +;; +;; Scoped types are types that the current code would have access to. +;; The come from the global namespace or from special commands such as "using" +(define-overloadable-function semantic-ctxt-scoped-types (&optional point) + "Return a list of type names currently in scope at POINT. +The return value can be a mixed list of either strings (names of +types that are in scope) or actual tags (type declared locally +that may or may not have a name.)") + +(defun semantic-ctxt-scoped-types-default (&optional point) + "Return a list of scoped types by name for the current context at POINT. +This is very different for various languages, and does nothing unless +overriden." + (if point (goto-char point)) + (let ((case-fold-search semantic-case-fold)) + ;; We need to look at TYPES within the bounds of locally parse arguments. + ;; C needs to find using statements and the like too. Bleh. + nil + )) + +(provide 'semantic/ctxt) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/ctxt" +;; End: + +;;; semantic/ctxt.el ends here diff --git a/lisp/cedet/semantic/db-debug.el b/lisp/cedet/semantic/db-debug.el new file mode 100644 index 00000000000..cf188af8323 --- /dev/null +++ b/lisp/cedet/semantic/db-debug.el @@ -0,0 +1,111 @@ +;;; semantic/db-debug.el --- Extra level debugging routines for Semantic + +;;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Various routines for debugging SemanticDB issues, or viewing +;; semanticdb state. + +(require 'data-debug) +(require 'semantic/db) +(require 'semantic/format) + +;;; Code: +;; +(defun semanticdb-dump-all-table-summary () + "Dump a list of all databases in Emacs memory." + (interactive) + (require 'data-debug) + (let ((db semanticdb-database-list)) + (data-debug-new-buffer "*SEMANTICDB*") + (data-debug-insert-stuff-list db "*"))) + +(defalias 'semanticdb-adebug-database-list 'semanticdb-dump-all-table-summary) + +(defun semanticdb-adebug-current-database () + "Run ADEBUG on the current database." + (interactive) + (require 'data-debug) + (let ((p semanticdb-current-database) + ) + (data-debug-new-buffer "*SEMANTICDB ADEBUG*") + (data-debug-insert-stuff-list p "*"))) + +(defun semanticdb-adebug-current-table () + "Run ADEBUG on the current database." + (interactive) + (require 'data-debug) + (let ((p semanticdb-current-table)) + (data-debug-new-buffer "*SEMANTICDB ADEBUG*") + (data-debug-insert-stuff-list p "*"))) + + +(defun semanticdb-adebug-project-database-list () + "Run ADEBUG on the current database." + (interactive) + (require 'data-debug) + (let ((p (semanticdb-current-database-list))) + (data-debug-new-buffer "*SEMANTICDB ADEBUG*") + (data-debug-insert-stuff-list p "*"))) + + + +;;; Sanity Checks +;; + +(defun semanticdb-table-oob-sanity-check (cache) + "Validate that CACHE tags do not have any overlays in them." + (while cache + (when (semantic-overlay-p (semantic-tag-overlay cache)) + (message "Tag %s has an erroneous overlay!" + (semantic-format-tag-summarize (car cache)))) + (semanticdb-table-oob-sanity-check + (semantic-tag-components-with-overlays (car cache))) + (setq cache (cdr cache)))) + +(defun semanticdb-table-sanity-check (&optional table) + "Validate the current semanticdb TABLE." + (interactive) + (if (not table) (setq table semanticdb-current-table)) + (let* ((full-filename (semanticdb-full-filename table)) + (buff (find-buffer-visiting full-filename))) + (if buff + (save-excursion + (set-buffer buff) + (semantic-sanity-check)) + ;; We can't use the usual semantic validity check, so hack our own. + (semanticdb-table-oob-sanity-check (semanticdb-get-tags table))))) + +(defun semanticdb-database-sanity-check () + "Validate the current semantic database." + (interactive) + (let ((tables (semanticdb-get-database-tables + semanticdb-current-database))) + (while tables + (semanticdb-table-sanity-check (car tables)) + (setq tables (cdr tables))) + )) + + + +(provide 'semantic/db-debug) + +;;; semantic/db-debug.el ends here diff --git a/lisp/cedet/semantic/db-ebrowse.el b/lisp/cedet/semantic/db-ebrowse.el new file mode 100644 index 00000000000..8c6237f542c --- /dev/null +++ b/lisp/cedet/semantic/db-ebrowse.el @@ -0,0 +1,666 @@ +;;; semantic/db-ebrowse.el --- Semanticdb backend using ebrowse. + +;;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Authors: Eric M. Ludlam <zappo@gnu.org>, Joakim Verona +;; Keywords: tags + +;; 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: +;; +;; This program was started by Eric Ludlam, and Joakim Verona finished +;; the implementation by adding searches and fixing bugs. +;; +;; Read in custom-created ebrowse BROWSE files into a semanticdb back +;; end. +;; +;; Add these databases to the 'system' search. +;; Possibly use ebrowse for local parsing too. +;; +;; When real details are needed out of the tag system from ebrowse, +;; we will need to delve into the originating source and parse those +;; files the usual way. +;; +;; COMMANDS: +;; `semanticdb-create-ebrowse-database' - Call EBROWSE to create a +;; system database for some directory. In general, use this for +;; system libraries, such as /usr/include, or include directories +;; large software projects. +;; Customize `semanticdb-ebrowse-file-match' to make sure the correct +;; file extensions are matched. +;; +;; `semanticdb-load-ebrowse-caches' - Load all the EBROWSE caches from +;; your semanticdb system database directory. Once they are +;; loaded, they become searchable as omnipotent databases for +;; all C++ files. This is called automatically by semantic-load. +;; Call it a second time to refresh the Emacs DB with the file. +;; + +(require 'ebrowse) +(require 'semantic) +(require 'semantic/db-file) + +(eval-when-compile + ;; For generic function searching. + (require 'eieio) + (require 'eieio-opt) + (require 'semantic/find)) + +(declare-function semantic-add-system-include "semantic/dep") + +;;; Code: +(defvar semanticdb-ebrowse-default-file-name "BROWSE" + "The EBROWSE file name used for system caches.") + +(defcustom semanticdb-ebrowse-file-match "\\.\\(hh?\\|HH?\\|hpp\\)" + "Regular expression matching file names for ebrowse to parse. +This expression should exclude C++ headers that have no extension. +By default, include only headers since the semantic use of EBrowse +is only for searching via semanticdb, and thus only headers would +be searched." + :group 'semanticdb + :type 'string) + +;;; SEMANTIC Database related Code +;;; Classes: +(defclass semanticdb-table-ebrowse (semanticdb-table) + ((major-mode :initform c++-mode) + (ebrowse-tree :initform nil + :initarg :ebrowse-tree + :documentation + "The raw ebrowse tree for this file." + ) + (global-extract :initform nil + :initarg :global-extract + :documentation + "Table of ebrowse tags specific to this file. +This table is compisited from the ebrowse *Globals* section.") + ) + "A table for returning search results from ebrowse.") + +(defclass semanticdb-project-database-ebrowse + (semanticdb-project-database) + ((new-table-class :initform semanticdb-table-ebrowse + :type class + :documentation + "New tables created for this database are of this class.") + (system-include-p :initform nil + :initarg :system-include + :documentation + "Flag indicating this database represents a system include directory.") + (ebrowse-struct :initform nil + :initarg :ebrowse-struct + ) + ) + "Semantic Database deriving tags using the EBROWSE tool. +EBROWSE is a C/C++ parser for use with `ebrowse' Emacs program.") + + +(defun semanticdb-ebrowse-C-file-p (file) + "Is FILE a C or C++ file?" + (or (string-match semanticdb-ebrowse-file-match file) + (and (string-match "/\\w+$" file) + (not (file-directory-p file)) + (let ((tmp (get-buffer-create "*semanticdb-ebrowse-tmp*"))) + (save-excursion + (set-buffer tmp) + (condition-case nil + (insert-file-contents file nil 0 100 t) + (error (insert-file-contents file nil nil nil t))) + (goto-char (point-min)) + (looking-at "\\s-*/\\(\\*\\|/\\)") + )) + ))) + +(defun semanticdb-create-ebrowse-database (dir) + "Create an EBROSE database for directory DIR. +The database file is stored in ~/.semanticdb, or whichever directory +is specified by `semanticdb-default-save-directory'." + (interactive "DDirectory: ") + (setq dir (file-name-as-directory dir)) ;; for / on end + (let* ((savein (semanticdb-ebrowse-file-for-directory dir)) + (filebuff (get-buffer-create "*SEMANTICDB EBROWSE TMP*")) + (files (directory-files (expand-file-name dir) t)) + (mma auto-mode-alist) + (regexp nil) + ) + ;; Create the input to the ebrowse command + (save-excursion + (set-buffer filebuff) + (buffer-disable-undo filebuff) + (setq default-directory (expand-file-name dir)) + + ;;; @TODO - convert to use semanticdb-collect-matching-filenames + ;; to get the file names. + + + (mapc (lambda (f) + (when (semanticdb-ebrowse-C-file-p f) + (insert f) + (insert "\n"))) + files) + ;; Cleanup the ebrowse output buffer. + (save-excursion + (set-buffer (get-buffer-create "*EBROWSE OUTPUT*")) + (erase-buffer)) + ;; Call the EBROWSE command. + (message "Creating ebrowse file: %s ..." savein) + (call-process-region (point-min) (point-max) + "ebrowse" nil "*EBROWSE OUTPUT*" nil + (concat "--output-file=" savein) + "--very-verbose") + ) + ;; Create a short LOADER program for loading in this database. + (let* ((lfn (concat savein "-load.el")) + (lf (find-file-noselect lfn))) + (save-excursion + (set-buffer lf) + (erase-buffer) + (insert "(semanticdb-ebrowse-load-helper \"" + (expand-file-name dir) + "\")\n") + (save-buffer) + (kill-buffer (current-buffer))) + (message "Creating ebrowse file: %s ... done" savein) + ;; Reload that database + (load lfn nil t) + ))) + +(defun semanticdb-load-ebrowse-caches () + "Load all semanticdb controlled EBROWSE caches." + (interactive) + (let ((f (directory-files semanticdb-default-save-directory + t (concat semanticdb-ebrowse-default-file-name "-load.el$") t))) + (while f + (load (car f) nil t) + (setq f (cdr f))) + )) + +(defun semanticdb-ebrowse-load-helper (directory) + "Create the semanticdb database via ebrowse for directory. +If DIRECTORY is found to be defunct, it won't load the DB, and will +warn instead." + (if (file-directory-p directory) + (semanticdb-create-database semanticdb-project-database-ebrowse + directory) + (let* ((BF (semanticdb-ebrowse-file-for-directory directory)) + (BFL (concat BF "-load.el")) + (BFLB (concat BF "-load.el~"))) + (save-window-excursion + (with-output-to-temp-buffer "*FILES TO DELETE*" + (princ "The following BROWSE files are obsolete.\n\n") + (princ BF) + (princ "\n") + (princ BFL) + (princ "\n") + (when (file-exists-p BFLB) + (princ BFLB) + (princ "\n")) + ) + (when (y-or-n-p (format + "Warning: Obsolete BROWSE file for: %s\nDelete? " + directory)) + (delete-file BF) + (delete-file BFL) + (when (file-exists-p BFLB) + (delete-file BFLB)) + ))))) + +;JAVE this just instantiates a default empty ebrowse struct? +; how would new instances wind up here? +; the ebrowse class isnt singleton, unlike the emacs lisp one +(defvar-mode-local c++-mode semanticdb-project-system-databases + () + "Search Ebrowse for symbols.") + +(defmethod semanticdb-needs-refresh-p ((table semanticdb-table-ebrowse)) + "EBROWSE database do not need to be refreshed. + +JAVE: stub for needs-refresh, because, how do we know if BROWSE files + are out of date? + +EML: Our database should probably remember the timestamp/checksum of + the most recently read EBROWSE file, and use that." + nil +) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +;;; EBROWSE code +;; +;; These routines deal with part of the ebrowse interface. +(defun semanticdb-ebrowse-file-for-directory (dir) + "Return the file name for DIR where the ebrowse BROWSE file is. +This file should reside in `semanticdb-default-save-directory'." + (let* ((semanticdb-default-save-directory + semanticdb-default-save-directory) + (B (semanticdb-file-name-directory + 'semanticdb-project-database-file + (concat (expand-file-name dir) + semanticdb-ebrowse-default-file-name))) + ) + B)) + +(defun semanticdb-ebrowse-get-ebrowse-structure (dir) + "Return the ebrowse structure for directory DIR. +This assumes semantic manages the BROWSE files, so they are assumed to live +where semantic cache files live, depending on your settings. + +For instance: /home/<username>/.semanticdb/!usr!include!BROWSE" + (let* ((B (semanticdb-ebrowse-file-for-directory dir)) + (buf (get-buffer-create "*semanticdb ebrowse*"))) + (message "semanticdb-ebrowse %s" B) + (when (file-exists-p B) + (set-buffer buf) + (buffer-disable-undo buf) + (erase-buffer) + (insert-file-contents B) + (let ((ans nil) + (efcn (symbol-function 'ebrowse-show-progress))) + (fset 'ebrowse-show-progress #'(lambda (&rest junk) nil)) + (unwind-protect ;; Protect against errors w/ ebrowse + (setq ans (list B (ebrowse-read))) + ;; These items must always happen + (erase-buffer) + (fset 'ebrowse-show-fcn efcn) + ) + ans)))) + +;;; Methods for creating a database or tables +;; +(defmethod semanticdb-create-database :STATIC ((dbeC semanticdb-project-database-ebrowse) + directory) + "Create a new semantic database for DIRECTORY based on ebrowse. +If there is no database for DIRECTORY available, then +{not implemented yet} create one. Return nil if that is not possible." + ;; MAKE SURE THAT THE FILE LOADED DOESN'T ALREADY EXIST. + (require 'semantic/dep) + (let ((dbs semanticdb-database-list) + (found nil)) + (while (and (not found) dbs) + (when (semanticdb-project-database-ebrowse-p (car dbs)) + (when (string= (oref (car dbs) reference-directory) directory) + (setq found (car dbs)))) + (setq dbs (cdr dbs))) + ;;STATIC means DBE cant be used as object, only as a class + (let* ((ebrowse-data (semanticdb-ebrowse-get-ebrowse-structure directory)) + (dat (car (cdr ebrowse-data))) + (ebd (car dat)) + (db nil) + (default-directory directory) + ) + (if found + (setq db found) + (setq db (make-instance + dbeC + directory + :ebrowse-struct ebd + )) + (oset db reference-directory directory)) + + ;; Once we recycle or make a new DB, refresh the + ;; contents from the BROWSE file. + (oset db tables nil) + ;; only possible after object creation, tables inited to nil. + (semanticdb-ebrowse-strip-trees db dat) + + ;; Once our database is loaded, if we are a system DB, we + ;; add ourselves to the include list for C++. + (semantic-add-system-include directory 'c++-mode) + (semantic-add-system-include directory 'c-mode) + + db))) + +(defmethod semanticdb-ebrowse-strip-trees ((dbe semanticdb-project-database-ebrowse) + data) + "For the ebrowse database DBE, strip all tables from DATA." +;JAVE what it actually seems to do is split the original tree in "tables" associated with files +; im not sure it actually works: +; the filename slot sometimes gets to be nil, +; apparently for classes which definition cant be found, yet needs to be included in the tree +; like library baseclasses +; a file can define several classes + (let ((T (car (cdr data))));1st comes a header, then the tree + (while T + + (let* ((tree (car T)) + (class (ebrowse-ts-class tree)); root class of tree + ;; Something funny going on with this file thing... + (filename (or (ebrowse-cs-source-file class) + (ebrowse-cs-file class))) + ) + (cond + ((ebrowse-globals-tree-p tree) + ;; We have the globals tree.. save this special. + (semanticdb-ebrowse-add-globals-to-table dbe tree) + ) + (t + ;; ebrowse will collect all the info from multiple files + ;; into one tree. Semantic wants all the bits to be tied + ;; into different files. We need to do a full dissociation + ;; into semantic parsable tables. + (semanticdb-ebrowse-add-tree-to-table dbe tree) + )) + (setq T (cdr T)))) + )) + +;;; Filename based methods +;; +(defun semanticdb-ebrowse-add-globals-to-table (dbe tree) + "For database DBE, add the ebrowse TREE into the table." + (if (or (not (ebrowse-ts-p tree)) + (not (ebrowse-globals-tree-p tree))) + (signal 'wrong-type-argument (list 'ebrowse-ts-p tree))) + + (let* ((class (ebrowse-ts-class tree)) + (fname (or (ebrowse-cs-source-file class) + (ebrowse-cs-file class) + ;; Not def'd here, assume our current + ;; file + (concat default-directory "/unknown-proxy.hh"))) + (vars (ebrowse-ts-member-functions tree)) + (fns (ebrowse-ts-member-variables tree)) + (toks nil) + ) + (while vars + (let ((nt (semantic-tag (ebrowse-ms-name (car vars)) + 'variable)) + (defpoint (ebrowse-bs-point class))) + (when defpoint + (semantic--tag-set-overlay nt + (vector defpoint defpoint))) + (setq toks (cons nt toks))) + (setq vars (cdr vars))) + (while fns + (let ((nt (semantic-tag (ebrowse-ms-name (car fns)) + 'function)) + (defpoint (ebrowse-bs-point class))) + (when defpoint + (semantic--tag-set-overlay nt + (vector defpoint defpoint))) + (setq toks (cons nt toks))) + (setq fns (cdr fns))) + + )) + +(defun semanticdb-ebrowse-add-tree-to-table (dbe tree &optional fname baseclasses) + "For database DBE, add the ebrowse TREE into the table for FNAME. +Optional argument BASECLASSES specifyies a baseclass to the tree being provided." + (if (not (ebrowse-ts-p tree)) + (signal 'wrong-type-argument (list 'ebrowse-ts-p tree))) + + ;; Strategy overview: + ;; 1) Calculate the filename for this tree. + ;; 2) Find a matching namespace in TAB, or create a new one. + ;; 3) Fabricate a tag proxy for CLASS + ;; 4) Add it to the namespace + ;; 5) Add subclasses + + ;; 1 - Find the filename + (if (not fname) + (setq fname (or (ebrowse-cs-source-file (ebrowse-ts-class tree)) + (ebrowse-cs-file (ebrowse-ts-class tree)) + ;; Not def'd here, assume our current + ;; file + (concat default-directory "/unknown-proxy.hh")))) + + (let* ((tab (or (semanticdb-file-table dbe fname) + (semanticdb-create-table dbe fname))) + (class (ebrowse-ts-class tree)) + (scope (ebrowse-cs-scope class)) + (ns (when scope (split-string scope ":" t))) + (nst nil) + (cls nil) + ) + + ;; 2 - Get the namespace tag + (when ns + (let ((taglst (if (slot-boundp tab 'tags) (oref tab tags) nil))) + (setq nst (semantic-find-first-tag-by-name (car ns) taglst)) + (when (not nst) + (setq nst (semantic-tag (car ns) 'type :type "namespace")) + (oset tab tags (cons nst taglst)) + ))) + + ;; 3 - Create a proxy tg. + (setq cls (semantic-tag (ebrowse-cs-name class) + 'type + :type "class" + :superclasses baseclasses + :faux t + :filename fname + )) + (let ((defpoint (ebrowse-bs-point class))) + (when defpoint + (semantic--tag-set-overlay cls + (vector defpoint defpoint)))) + + ;; 4 - add to namespace + (if nst + (semantic-tag-put-attribute + nst :members (cons cls (semantic-tag-get-attribute nst :members))) + (oset tab tags (cons cls (when (slot-boundp tab 'tags) + (oref tab tags))))) + + ;; 5 - Subclasses + (let* ((subclass (ebrowse-ts-subclasses tree)) + (pname (ebrowse-cs-name class))) + (when (ebrowse-cs-scope class) + (setq pname (concat (mapconcat (lambda (a) a) (cdr ns) "::") "::" pname))) + + (while subclass + (let* ((scc (ebrowse-ts-class (car subclass))) + (fname (or (ebrowse-cs-source-file scc) + (ebrowse-cs-file scc) + ;; Not def'd here, assume our current + ;; file + fname + ))) + (when fname + (semanticdb-ebrowse-add-tree-to-table + dbe (car subclass) fname pname))) + (setq subclass (cdr subclass)))) + )) + +;;; +;; Overload for converting the simple faux tag into something better. +;; +(defmethod semanticdb-normalize-tags ((obj semanticdb-table-ebrowse) tags) + "Convert in Ebrowse database OBJ a list of TAGS into a complete tag. +The default tag provided by searches exclude many features of a +semantic parsed tag. Look up the file for OBJ, and match TAGS +against a semantic parsed tag that has all the info needed, and +return that." + (let ((tagret nil) + ) + ;; SemanticDB will automatically create a regular database + ;; on top of the file just loaded by ebrowse during the set + ;; buffer. Fetch that table, and use it's tag list to look + ;; up the tag we just got, and thus turn it into a full semantic + ;; tag. + (while tags + (let ((tag (car tags))) + (save-excursion + (semanticdb-set-buffer obj) + (let ((ans nil)) + ;; Gee, it would be nice to do this, but ebrowse LIES. Oi. + (when (semantic-tag-with-position-p tag) + (goto-char (semantic-tag-start tag)) + (let ((foundtag (semantic-current-tag))) + ;; Make sure the discovered tag is the same as what we started with. + (when (string= (semantic-tag-name tag) + (semantic-tag-name foundtag)) + ;; We have a winner! + (setq ans foundtag)))) + ;; Sometimes ebrowse lies. Do a generic search + ;; to find it within this file. + (when (not ans) + ;; We might find multiple hits for this tag, and we have no way + ;; of knowing which one the user wanted. Return the first one. + (setq ans (semantic-deep-find-tags-by-name + (semantic-tag-name tag) + (semantic-fetch-tags)))) + (if (semantic-tag-p ans) + (setq tagret (cons ans tagret)) + (setq tagret (append ans tagret))) + )) + (setq tags (cdr tags)))) + tagret)) + +(defmethod semanticdb-normalize-one-tag ((obj semanticdb-table-ebrowse) tag) + "Convert in Ebrowse database OBJ one TAG into a complete tag. +The default tag provided by searches exclude many features of a +semantic parsed tag. Look up the file for OBJ, and match TAG +against a semantic parsed tag that has all the info needed, and +return that." + (let ((tagret nil) + (objret nil)) + ;; SemanticDB will automatically create a regular database + ;; on top of the file just loaded by ebrowse during the set + ;; buffer. Fetch that table, and use it's tag list to look + ;; up the tag we just got, and thus turn it into a full semantic + ;; tag. + (save-excursion + (semanticdb-set-buffer obj) + (setq objret semanticdb-current-table) + (when (not objret) + ;; What to do?? + (debug)) + (let ((ans nil)) + ;; Gee, it would be nice to do this, but ebrowse LIES. Oi. + (when (semantic-tag-with-position-p tag) + (goto-char (semantic-tag-start tag)) + (let ((foundtag (semantic-current-tag))) + ;; Make sure the discovered tag is the same as what we started with. + (when (string= (semantic-tag-name tag) + (semantic-tag-name foundtag)) + ;; We have a winner! + (setq ans foundtag)))) + ;; Sometimes ebrowse lies. Do a generic search + ;; to find it within this file. + (when (not ans) + ;; We might find multiple hits for this tag, and we have no way + ;; of knowing which one the user wanted. Return the first one. + (setq ans (semantic-deep-find-tags-by-name + (semantic-tag-name tag) + (semantic-fetch-tags)))) + (if (semantic-tag-p ans) + (setq tagret ans) + (setq tagret (car ans))) + )) + (cons objret tagret))) + +;;; Search Overrides +;; +;; NOTE WHEN IMPLEMENTING: Be sure to add doc-string updates explaining +;; how your new search routines are implemented. +;; +(defmethod semanticdb-find-tags-by-name-method + ((table semanticdb-table-ebrowse) name &optional tags) + "Find all tags named NAME in TABLE. +Return a list of tags." + ;;(message "semanticdb-find-tags-by-name-method name -- %s" name) + (if tags + ;; If TAGS are passed in, then we don't need to do work here. + (call-next-method) + ;; If we ever need to do something special, add here. + ;; Since ebrowse tags are converted into semantic tags, we can + ;; get away with this sort of thing. + (call-next-method) + ) + ) + +(defmethod semanticdb-find-tags-by-name-regexp-method + ((table semanticdb-table-ebrowse) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Return a list of tags." + (if tags (call-next-method) + ;; YOUR IMPLEMENTATION HERE + (call-next-method) + )) + +(defmethod semanticdb-find-tags-for-completion-method + ((table semanticdb-table-ebrowse) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (if tags (call-next-method) + ;; YOUR IMPLEMENTATION HERE + (call-next-method) + )) + +(defmethod semanticdb-find-tags-by-class-method + ((table semanticdb-table-ebrowse) class &optional tags) + "In TABLE, find all occurances of tags of CLASS. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (if tags (call-next-method) + (call-next-method))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Deep Searches +;; +;; If your language does not have a `deep' concept, these can be left +;; alone, otherwise replace with implementations similar to those +;; above. +;; + +(defmethod semanticdb-deep-find-tags-by-name-method + ((table semanticdb-table-ebrowse) name &optional tags) + "Find all tags name NAME in TABLE. +Optional argument TAGS is a list of tags t +Like `semanticdb-find-tags-by-name-method' for ebrowse." + ;;(semanticdb-find-tags-by-name-method table name tags) + (call-next-method)) + +(defmethod semanticdb-deep-find-tags-by-name-regexp-method + ((table semanticdb-table-ebrowse) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-by-name-method' for ebrowse." + ;;(semanticdb-find-tags-by-name-regexp-method table regex tags) + (call-next-method)) + +(defmethod semanticdb-deep-find-tags-for-completion-method + ((table semanticdb-table-ebrowse) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-for-completion-method' for ebrowse." + ;;(semanticdb-find-tags-for-completion-method table prefix tags) + (call-next-method)) + +;;; Advanced Searches +;; +(defmethod semanticdb-find-tags-external-children-of-type-method + ((table semanticdb-table-ebrowse) type &optional tags) + "Find all nonterminals which are child elements of TYPE +Optional argument TAGS is a list of tags to search. +Return a list of tags." + (if tags (call-next-method) + ;; Ebrowse collects all this type of stuff together for us. + ;; but we can't use it.... yet. + nil + )) + +(provide 'semantic/db-ebrowse) + +;;; semantic/db-ebrowse.el ends here diff --git a/lisp/cedet/semantic/db-el.el b/lisp/cedet/semantic/db-el.el new file mode 100644 index 00000000000..f98226906c8 --- /dev/null +++ b/lisp/cedet/semantic/db-el.el @@ -0,0 +1,347 @@ +;;; semantic/db-el.el --- Semantic database extensions for Emacs Lisp + +;;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: tags + +;; 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: +;; +;; There are a lot of Emacs Lisp functions and variables available for +;; the asking. This adds on to the semanticdb programming interface to +;; allow all loaded Emacs Lisp functions to be queried via semanticdb. +;; +;; This allows you to use programs written for Semantic using the database +;; to also work in Emacs Lisp with no compromises. +;; + +(require 'semantic/db) + +(eval-when-compile + ;; For generic function searching. + (require 'eieio) + (require 'eieio-opt) + (require 'eieio-base)) + +(declare-function semantic-elisp-desymbolify "semantic/bovine/el") + +;;; Code: + +;;; Classes: +(defclass semanticdb-table-emacs-lisp (semanticdb-abstract-table) + ((major-mode :initform emacs-lisp-mode) + ) + "A table for returning search results from Emacs.") + +(defmethod semanticdb-refresh-table ((obj semanticdb-table-emacs-lisp) &optional force) + "Do not refresh Emacs Lisp table. +It does not need refreshing." + nil) + +(defmethod semanticdb-needs-refresh-p ((obj semanticdb-table-emacs-lisp)) + "Return nil, we never need a refresh." + nil) + +(defclass semanticdb-project-database-emacs-lisp + (semanticdb-project-database eieio-singleton) + ((new-table-class :initform semanticdb-table-emacs-lisp + :type class + :documentation + "New tables created for this database are of this class.") + ) + "Database representing Emacs core.") + +;; Create the database, and add it to searchable databases for Emacs Lisp mode. +(defvar-mode-local emacs-lisp-mode semanticdb-project-system-databases + (list + (semanticdb-project-database-emacs-lisp "Emacs")) + "Search Emacs core for symbols.") + +(defvar-mode-local emacs-lisp-mode semanticdb-find-default-throttle + '(project omniscience) + "Search project files, then search this omniscience database. +It is not necessary to to system or recursive searching because of +the omniscience database.") + +;;; Filename based methods +;; +(defmethod semanticdb-get-database-tables ((obj semanticdb-project-database-emacs-lisp)) + "For an Emacs Lisp database, there are no explicit tables. +Create one of our special tables that can act as an intermediary." + ;; We need to return something since there is always the "master table" + ;; The table can then answer file name type questions. + (when (not (slot-boundp obj 'tables)) + (let ((newtable (semanticdb-table-emacs-lisp "Emacs System Table"))) + (oset obj tables (list newtable)) + (oset newtable parent-db obj) + (oset newtable tags nil) + )) + (call-next-method)) + +(defmethod semanticdb-file-table ((obj semanticdb-project-database-emacs-lisp) filename) + "From OBJ, return FILENAME's associated table object. +For Emacs Lisp, creates a specialized table." + (car (semanticdb-get-database-tables obj)) + ) + +(defmethod semanticdb-get-tags ((table semanticdb-table-emacs-lisp )) + "Return the list of tags belonging to TABLE." + ;; specialty table ? Probably derive tags at request time. + nil) + +(defmethod semanticdb-equivalent-mode ((table semanticdb-table-emacs-lisp) &optional buffer) + "Return non-nil if TABLE's mode is equivalent to BUFFER. +Equivalent modes are specified by by `semantic-equivalent-major-modes' +local variable." + (save-excursion + (set-buffer buffer) + (eq (or mode-local-active-mode major-mode) 'emacs-lisp-mode))) + +(defmethod semanticdb-full-filename ((obj semanticdb-table-emacs-lisp)) + "Fetch the full filename that OBJ refers to. +For Emacs Lisp system DB, there isn't one." + nil) + +;;; Conversion +;; +(defmethod semanticdb-normalize-tags ((obj semanticdb-table-emacs-lisp) tags) + "Convert tags, originating from Emacs OBJ, into standardized form." + (let ((newtags nil)) + (dolist (T tags) + (let* ((ot (semanticdb-normalize-one-tag obj T)) + (tag (cdr ot))) + (setq newtags (cons tag newtags)))) + ;; There is no promise to have files associated. + (nreverse newtags))) + +(defmethod semanticdb-normalize-one-tag ((obj semanticdb-table-emacs-lisp) tag) + "Convert one TAG, originating from Emacs OBJ, into standardized form. +If Emacs cannot resolve this symbol to a particular file, then return nil." + ;; Here's the idea. For each tag, get the name, then use + ;; Emacs' `symbol-file' to get the source. Once we have that, + ;; we can use more typical semantic searching techniques to + ;; get a regularly parsed tag. + (let* ((type (cond ((semantic-tag-of-class-p tag 'function) + 'defun) + ((semantic-tag-of-class-p tag 'variable) + 'defvar) + )) + (sym (intern (semantic-tag-name tag))) + (file (condition-case err + (symbol-file sym type) + ;; Older [X]Emacs don't have a 2nd argument. + (error (symbol-file sym)))) + ) + (if (or (not file) (not (file-exists-p file))) + ;; The file didn't exist. Return nil. + ;; We can't normalize this tag. Fake it out. + (cons obj tag) + (when (string-match "\\.elc" file) + (setq file (concat (file-name-sans-extension file) + ".el")) + (when (and (not (file-exists-p file)) + (file-exists-p (concat file ".gz"))) + ;; Is it a .gz file? + (setq file (concat file ".gz")))) + + (let* ((tab (semanticdb-file-table-object file)) + (alltags (semanticdb-get-tags tab)) + (newtags (semanticdb-find-tags-by-name-method + tab (semantic-tag-name tag))) + (match nil)) + ;; Find the best match. + (dolist (T newtags) + (when (semantic-tag-similar-p T tag) + (setq match T))) + ;; Backup system. + (when (not match) + (setq match (car newtags))) + ;; Return it. + (cons tab match))))) + +(defun semanticdb-elisp-sym-function-arglist (sym) + "Get the argument list for SYM. +Deal with all different forms of function. +This was snarfed out of eldoc." + (let* ((prelim-def + (let ((sd (and (fboundp sym) + (symbol-function sym)))) + (and (symbolp sd) + (condition-case err + (setq sd (indirect-function sym)) + (error (setq sd nil)))) + sd)) + (def (if (eq (car-safe prelim-def) 'macro) + (cdr prelim-def) + prelim-def)) + (arglist (cond ((null def) nil) + ((byte-code-function-p def) + ;; This is an eieio compatibility function. + ;; We depend on EIEIO, so use this. + (eieio-compiled-function-arglist def)) + ((eq (car-safe def) 'lambda) + (nth 1 def)) + (t nil)))) + arglist)) + +(defun semanticdb-elisp-sym->tag (sym &optional toktype) + "Convert SYM into a semantic tag. +TOKTYPE is a hint to the type of tag desired." + (if (stringp sym) + (setq sym (intern-soft sym))) + (when sym + (cond ((and (eq toktype 'function) (fboundp sym)) + (require 'semantic/bovine/el) + (semantic-tag-new-function + (symbol-name sym) + nil ;; return type + (semantic-elisp-desymbolify + (semanticdb-elisp-sym-function-arglist sym)) ;; arg-list + :user-visible-flag (condition-case nil + (interactive-form sym) + (error nil)) + )) + ((and (eq toktype 'variable) (boundp sym)) + (semantic-tag-new-variable + (symbol-name sym) + nil ;; type + nil ;; value - ignore for now + )) + ((and (eq toktype 'type) (class-p sym)) + (semantic-tag-new-type + (symbol-name sym) + "class" + (semantic-elisp-desymbolify + (aref (class-v semanticdb-project-database) + class-public-a)) ;; slots + (semantic-elisp-desymbolify (class-parents sym)) ;; parents + )) + ((not toktype) + ;; Figure it out on our own. + (cond ((class-p sym) + (semanticdb-elisp-sym->tag sym 'type)) + ((fboundp sym) + (semanticdb-elisp-sym->tag sym 'function)) + ((boundp sym) + (semanticdb-elisp-sym->tag sym 'variable)) + (t nil)) + ) + (t nil)))) + +;;; Search Overrides +;; +(defvar semanticdb-elisp-mapatom-collector nil + "Variable used to collect mapatoms output.") + +(defmethod semanticdb-find-tags-by-name-method + ((table semanticdb-table-emacs-lisp) name &optional tags) + "Find all tags name NAME in TABLE. +Uses `inter-soft' to match NAME to emacs symbols. +Return a list of tags." + (if tags (call-next-method) + ;; No need to search. Use `intern-soft' which does the same thing for us. + (let* ((sym (intern-soft name)) + (fun (semanticdb-elisp-sym->tag sym 'function)) + (var (semanticdb-elisp-sym->tag sym 'variable)) + (typ (semanticdb-elisp-sym->tag sym 'type)) + (taglst nil) + ) + (when (or fun var typ) + ;; If the symbol is any of these things, build the search table. + (when var (setq taglst (cons var taglst))) + (when typ (setq taglst (cons typ taglst))) + (when fun (setq taglst (cons fun taglst))) + taglst + )))) + +(defmethod semanticdb-find-tags-by-name-regexp-method + ((table semanticdb-table-emacs-lisp) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Uses `apropos-internal' to find matches. +Return a list of tags." + (if tags (call-next-method) + (delq nil (mapcar 'semanticdb-elisp-sym->tag + (apropos-internal regex))))) + +(defmethod semanticdb-find-tags-for-completion-method + ((table semanticdb-table-emacs-lisp) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (if tags (call-next-method) + (delq nil (mapcar 'semanticdb-elisp-sym->tag + (all-completions prefix obarray))))) + +(defmethod semanticdb-find-tags-by-class-method + ((table semanticdb-table-emacs-lisp) class &optional tags) + "In TABLE, find all occurances of tags of CLASS. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (if tags (call-next-method) + ;; We could implement this, but it could be messy. + nil)) + +;;; Deep Searches +;; +;; For Emacs Lisp deep searches are like top level searches. +(defmethod semanticdb-deep-find-tags-by-name-method + ((table semanticdb-table-emacs-lisp) name &optional tags) + "Find all tags name NAME in TABLE. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-by-name-method' for Emacs Lisp." + (semanticdb-find-tags-by-name-method table name tags)) + +(defmethod semanticdb-deep-find-tags-by-name-regexp-method + ((table semanticdb-table-emacs-lisp) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-by-name-method' for Emacs Lisp." + (semanticdb-find-tags-by-name-regexp-method table regex tags)) + +(defmethod semanticdb-deep-find-tags-for-completion-method + ((table semanticdb-table-emacs-lisp) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-for-completion-method' for Emacs Lisp." + (semanticdb-find-tags-for-completion-method table prefix tags)) + +;;; Advanced Searches +;; +(defmethod semanticdb-find-tags-external-children-of-type-method + ((table semanticdb-table-emacs-lisp) type &optional tags) + "Find all nonterminals which are child elements of TYPE +Optional argument TAGS is a list of tags to search. +Return a list of tags." + (if tags (call-next-method) + ;; EIEIO is the only time this matters + (when (featurep 'eieio) + (let* ((class (intern-soft type)) + (taglst (when class + (delq nil + (mapcar 'semanticdb-elisp-sym->tag + ;; Fancy eieio function that knows all about + ;; built in methods belonging to CLASS. + (eieio-all-generic-functions class))))) + ) + taglst)))) + +(provide 'semantic/db-el) + +;;; semantic/db-el.el ends here diff --git a/lisp/cedet/semantic/db-file.el b/lisp/cedet/semantic/db-file.el new file mode 100644 index 00000000000..2ba2b44461f --- /dev/null +++ b/lisp/cedet/semantic/db-file.el @@ -0,0 +1,457 @@ +;;; semantic/db-file.el --- Save a semanticdb to a cache file. + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: tags + +;; 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: +;; +;; A set of semanticdb classes for persistently saving caches on disk. +;; + +(require 'semantic) +(require 'semantic/db) +(require 'cedet-files) + +(defvar semanticdb-file-version semantic-version + "Version of semanticdb we are writing files to disk with.") +(defvar semanticdb-file-incompatible-version "1.4" + "Version of semanticdb we are not reverse compatible with.") + +;;; Settings +;; +(defcustom semanticdb-default-file-name "semantic.cache" + "File name of the semantic tag cache." + :group 'semanticdb + :type 'string) + +(defcustom semanticdb-default-save-directory + (expand-file-name "semanticdb" user-emacs-directory) + "Directory name where semantic cache files are stored. +If this value is nil, files are saved in the current directory. If the value +is a valid directory, then it overrides `semanticdb-default-file-name' and +stores caches in a coded file name in this directory." + :group 'semanticdb + :type '(choice :tag "Default-Directory" + :menu-tag "Default-Directory" + (const :tag "Use current directory" :value nil) + (directory))) + +(defcustom semanticdb-persistent-path '(always) + "List of valid paths that semanticdb will cache tags to. +When `global-semanticdb-minor-mode' is active, tag lists will +be saved to disk when Emacs exits. Not all directories will have +tags that should be saved. +The value should be a list of valid paths. A path can be a string, +indicating a directory in which to save a variable. An element in the +list can also be a symbol. Valid symbols are `never', which will +disable any saving anywhere, `always', which enables saving +everywhere, or `project', which enables saving in any directory that +passes a list of predicates in `semanticdb-project-predicate-functions'." + :group 'semanticdb + :type nil) + +(defcustom semanticdb-save-database-hooks nil + "Abnormal hook run after a database is saved. +Each function is called with one argument, the object representing +the database recently written." + :group 'semanticdb + :type 'hook) + +(defvar semanticdb-dir-sep-char (if (boundp 'directory-sep-char) + (symbol-value 'directory-sep-char) + ?/) + "Character used for directory separation. +Obsoleted in some versions of Emacs. Needed in others. +NOTE: This should get deleted from semantic soon.") + +(defun semanticdb-fix-pathname (dir) + "If DIR is broken, fix it. +Force DIR to end with a /. +Note: Same as `file-name-as-directory'. +NOTE: This should get deleted from semantic soon." + (file-name-as-directory dir)) +;; I didn't initially know about the above fcn. Keep the below as a +;; reference. Delete it someday once I've proven everything is the same. +;; (if (not (= semanticdb-dir-sep-char (aref path (1- (length path))))) +;; (concat path (list semanticdb-dir-sep-char)) +;; path)) + +;;; Classes +;; +;;;###autoload +(defclass semanticdb-project-database-file (semanticdb-project-database + eieio-persistent) + ((file-header-line :initform ";; SEMANTICDB Tags save file") + (do-backups :initform nil) + (semantic-tag-version :initarg :semantic-tag-version + :initform "1.4" + :documentation + "The version of the tags saved. +The default value is 1.4. In semantic 1.4 there was no versioning, so +when those files are loaded, this becomes the version number. +To save the version number, we must hand-set this version string.") + (semanticdb-version :initarg :semanticdb-version + :initform "1.4" + :documentation + "The version of the object system saved. +The default value is 1.4. In semantic 1.4, there was no versioning, +so when those files are loaded, this becomes the version number. +To save the version number, we must hand-set this version string.") + ) + "Database of file tables saved to disk.") + +;;; Code: +;; +(defmethod semanticdb-create-database :STATIC ((dbc semanticdb-project-database-file) + directory) + "Create a new semantic database for DIRECTORY and return it. +If a database for DIRECTORY has already been loaded, return it. +If a database for DIRECTORY exists, then load that database, and return it. +If DIRECTORY doesn't exist, create a new one." + ;; Make sure this is fully expanded so we don't get duplicates. + (setq directory (file-truename directory)) + (let* ((fn (semanticdb-cache-filename dbc directory)) + (db (or (semanticdb-file-loaded-p fn) + (if (file-exists-p fn) + (progn + (semanticdb-load-database fn)))))) + (unless db + (setq db (make-instance + dbc ; Create the database requested. Perhaps + (concat (file-name-nondirectory + (directory-file-name + directory)) + "/") + :file fn :tables nil + :semantic-tag-version semantic-version + :semanticdb-version semanticdb-file-version))) + ;; Set this up here. We can't put it in the constructor because it + ;; would be saved, and we want DB files to be portable. + (oset db reference-directory directory) + db)) + +;;; File IO + +(declare-function inversion-test "inversion") + +(defun semanticdb-load-database (filename) + "Load the database FILENAME." + (condition-case foo + (let* ((r (eieio-persistent-read filename)) + (c (semanticdb-get-database-tables r)) + (tv (oref r semantic-tag-version)) + (fv (oref r semanticdb-version)) + ) + ;; Restore the parent-db connection + (while c + (oset (car c) parent-db r) + (setq c (cdr c))) + (unless (and (equal semanticdb-file-version fv) + (equal semantic-tag-version tv)) + ;; Try not to load inversion unless we need it: + (require 'inversion) + (if (not (inversion-test 'semanticdb-file fv)) + (when (inversion-test 'semantic-tag tv) + ;; Incompatible version. Flush tables. + (semanticdb-flush-database-tables r) + ;; Reset the version to new version. + (oset r semantic-tag-version semantic-tag-version) + ;; Warn user + (message "Semanticdb file is old. Starting over for %s" + filename)) + ;; Version is not ok. Flush whole system + (message "semanticdb file is old. Starting over for %s" + filename) + ;; This database is so old, we need to replace it. + ;; We also need to delete it from the instance tracker. + (delete-instance r) + (setq r nil))) + r) + (error (message "Cache Error: [%s] %s, Restart" + filename foo) + nil))) + +(defun semanticdb-file-loaded-p (filename) + "Return the project belonging to FILENAME if it was already loaded." + (eieio-instance-tracker-find filename 'file 'semanticdb-database-list)) + +(defmethod semanticdb-file-directory-exists-p ((DB semanticdb-project-database-file) + &optional supress-questions) + "Does the directory the database DB needs to write to exist? +If SUPRESS-QUESTIONS, then do not ask to create the directory." + (let ((dest (file-name-directory (oref DB file))) + ) + (cond ((null dest) + ;; @TODO - If it was never set up... what should we do ? + nil) + ((file-exists-p dest) t) + ((or supress-questions + (and (boundp 'semanticdb--inhibit-make-directory) + semanticdb--inhibit-make-directory)) + nil) + ((y-or-n-p (format "Create directory %s for SemanticDB? " dest)) + (make-directory dest t) + t) + (t + (if (boundp 'semanticdb--inhibit-make-directory) + (setq semanticdb--inhibit-make-directory t)) + nil)))) + +(defmethod semanticdb-save-db ((DB semanticdb-project-database-file) + &optional + supress-questions) + "Write out the database DB to its file. +If DB is not specified, then use the current database." + (let ((objname (oref DB file))) + (when (and (semanticdb-dirty-p DB) + (semanticdb-live-p DB) + (semanticdb-file-directory-exists-p DB supress-questions) + (semanticdb-write-directory-p DB) + ) + ;;(message "Saving tag summary for %s..." objname) + (condition-case foo + (eieio-persistent-save (or DB semanticdb-current-database)) + (file-error ; System error saving? Ignore it. + (message "%S: %s" foo objname)) + (error + (cond + ((and (listp foo) + (stringp (nth 1 foo)) + (string-match "write[- ]protected" (nth 1 foo))) + (message (nth 1 foo))) + ((and (listp foo) + (stringp (nth 1 foo)) + (string-match "no such directory" (nth 1 foo))) + (message (nth 1 foo))) + (t + ;; @todo - It should ask if we are not called from a hook. + ;; How? + (if (or supress-questions + (y-or-n-p (format "Skip Error: %S ?" (car (cdr foo))))) + (message "Save Error: %S: %s" (car (cdr foo)) + objname) + (error "%S" (car (cdr foo)))))))) + (run-hook-with-args 'semanticdb-save-database-hooks + (or DB semanticdb-current-database)) + ;;(message "Saving tag summary for %s...done" objname) + ) + )) + +(defmethod semanticdb-live-p ((obj semanticdb-project-database)) + "Return non-nil if the file associated with OBJ is live. +Live databases are objects associated with existing directories." + (and (slot-boundp obj 'reference-directory) + (file-exists-p (oref obj reference-directory)))) + +(defmethod semanticdb-live-p ((obj semanticdb-table)) + "Return non-nil if the file associated with OBJ is live. +Live files are either buffers in Emacs, or files existing on the filesystem." + (let ((full-filename (semanticdb-full-filename obj))) + (or (find-buffer-visiting full-filename) + (file-exists-p full-filename)))) + +(defvar semanticdb-data-debug-on-write-error nil + "Run the data debugger on tables that issue errors. +This variable is set to nil after the first error is encountered +to prevent overload.") + +(declare-function data-debug-insert-thing "data-debug") + +(defmethod object-write ((obj semanticdb-table)) + "When writing a table, we have to make sure we deoverlay it first. +Restore the overlays after writting. +Argument OBJ is the object to write." + (when (semanticdb-live-p obj) + (when (semanticdb-in-buffer-p obj) + (save-excursion + (set-buffer (semanticdb-in-buffer-p obj)) + + ;; Make sure all our tag lists are up to date. + (semantic-fetch-tags) + + ;; Try to get an accurate unmatched syntax table. + (when (and (boundp semantic-show-unmatched-syntax-mode) + semantic-show-unmatched-syntax-mode) + ;; Only do this if the user runs unmatched syntax + ;; mode display enties. + (oset obj unmatched-syntax + (semantic-show-unmatched-lex-tokens-fetch)) + ) + + ;; Make sure pointmax is up to date + (oset obj pointmax (point-max)) + )) + + ;; Make sure that the file size and other attributes are + ;; up to date. + (let ((fattr (file-attributes (semanticdb-full-filename obj)))) + (oset obj fsize (nth 7 fattr)) + (oset obj lastmodtime (nth 5 fattr)) + ) + + ;; Do it! + (condition-case tableerror + (call-next-method) + (error + (when semanticdb-data-debug-on-write-error + (require 'data-debug) + (data-debug-new-buffer (concat "*SEMANTICDB ERROR*")) + (data-debug-insert-thing obj "*" "") + (setq semanticdb-data-debug-on-write-error nil)) + (message "Error Writing Table: %s" (object-name obj)) + (error "%S" (car (cdr tableerror))))) + + ;; Clear the dirty bit. + (oset obj dirty nil) + )) + +;;; State queries +;; +(defmethod semanticdb-write-directory-p ((obj semanticdb-project-database-file)) + "Return non-nil if OBJ should be written to disk. +Uses `semanticdb-persistent-path' to determine the return value." + (let ((path semanticdb-persistent-path)) + (catch 'found + (while path + (cond ((stringp (car path)) + (if (string= (oref obj reference-directory) (car path)) + (throw 'found t))) + ((eq (car path) 'project) + ;; @TODO - EDE causes us to go in here and disable + ;; the old default 'always save' setting. + ;; + ;; With new default 'always' should I care? + (if semanticdb-project-predicate-functions + (if (run-hook-with-args-until-success + 'semanticdb-project-predicate-functions + (oref obj reference-directory)) + (throw 'found t)) + ;; If the mode is 'project, and there are no project + ;; modes, then just always save the file. If users + ;; wish to restrict the search, modify + ;; `semanticdb-persistent-path' to include desired paths. + (if (= (length semanticdb-persistent-path) 1) + (throw 'found t)) + )) + ((eq (car path) 'never) + (throw 'found nil)) + ((eq (car path) 'always) + (throw 'found t)) + (t (error "Invalid path %S" (car path)))) + (setq path (cdr path))) + (call-next-method)) + )) + +;;; Filename manipulation +;; +(defmethod semanticdb-file-table ((obj semanticdb-project-database-file) filename) + "From OBJ, return FILENAME's associated table object." + ;; Cheater option. In this case, we always have files directly + ;; under ourselves. The main project type may not. + (object-assoc (file-name-nondirectory filename) 'file (oref obj tables))) + +(defmethod semanticdb-file-name-non-directory :STATIC + ((dbclass semanticdb-project-database-file)) + "Return the file name DBCLASS will use. +File name excludes any directory part." + semanticdb-default-file-name) + +(defmethod semanticdb-file-name-directory :STATIC + ((dbclass semanticdb-project-database-file) directory) + "Return the relative directory to where DBCLASS will save its cache file. +The returned path is related to DIRECTORY." + (if semanticdb-default-save-directory + (let ((file (cedet-directory-name-to-file-name directory))) + ;; Now create a filename for the cache file in + ;; ;`semanticdb-default-save-directory'. + (expand-file-name + file (file-name-as-directory semanticdb-default-save-directory))) + directory)) + +(defmethod semanticdb-cache-filename :STATIC + ((dbclass semanticdb-project-database-file) path) + "For DBCLASS, return a file to a cache file belonging to PATH. +This could be a cache file in the current directory, or an encoded file +name in a secondary directory." + ;; Use concat and not expand-file-name, because the dir part + ;; may include some of the file name. + (concat (semanticdb-file-name-directory dbclass path) + (semanticdb-file-name-non-directory dbclass))) + +(defmethod semanticdb-full-filename ((obj semanticdb-project-database-file)) + "Fetch the full filename that OBJ refers to." + (oref obj file)) + +;;; FLUSH OLD FILES +;; +(defun semanticdb-cleanup-cache-files (&optional noerror) + "Cleanup any cache files associated with directories that no longer exist. +Optional NOERROR prevents errors from being displayed." + (interactive) + (when (and (not semanticdb-default-save-directory) + (not noerror)) + (error "No default save directory for semantic-save files")) + + (when semanticdb-default-save-directory + + ;; Calculate all the cache files we have. + (let* ((regexp (regexp-quote semanticdb-default-file-name)) + (files (directory-files semanticdb-default-save-directory + t regexp)) + (orig nil) + (to-delete nil)) + (dolist (F files) + (setq orig (cedet-file-name-to-directory-name + (file-name-nondirectory F))) + (when (not (file-exists-p (file-name-directory orig))) + (setq to-delete (cons F to-delete)) + )) + (if to-delete + (save-window-excursion + (let ((buff (get-buffer-create "*Semanticdb Delete*"))) + (with-current-buffer buff + (erase-buffer) + (insert "The following Cache files appear to be obsolete.\n\n") + (dolist (F to-delete) + (insert F "\n"))) + (pop-to-buffer buff t t) + (fit-window-to-buffer (get-buffer-window buff) nil 1) + (when (y-or-n-p "Delete Old Cache Files? ") + (mapc (lambda (F) + (message "Deleting to %s..." F) + (delete-file F)) + to-delete) + (message "done.")) + )) + ;; No files to delete + (when (not noerror) + (message "No obsolete semanticdb.cache files.")) + )))) + +(provide 'semantic/db-file) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/db-file" +;; End: + +;;; semantic/db-file.el ends here diff --git a/lisp/cedet/semantic/db-find.el b/lisp/cedet/semantic/db-find.el new file mode 100644 index 00000000000..e7ce7fcbdef --- /dev/null +++ b/lisp/cedet/semantic/db-find.el @@ -0,0 +1,1373 @@ +;;; semantic/db-find.el --- Searching through semantic databases. + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, +;;; 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: tags + +;; 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: +;; +;; Databases of various forms can all be searched. +;; There are a few types of searches that can be done: +;; +;; Basic Name Search: +;; These searches scan a database table collection for tags based +;; on name. +;; +;; Basic Attribute Search: +;; These searches allow searching on specific attributes of tags, +;; such as name, type, or other attribute. +;; +;; Advanced Search: +;; These are searches that were needed to accomplish some +;; specialized tasks as discovered in utilities. Advanced searches +;; include matching methods defined outside some parent class. +;; +;; The reason for advanced searches are so that external +;; repositories such as the Emacs obarray, or java .class files can +;; quickly answer these needed questions without dumping the entire +;; symbol list into Emacs for additional refinement searches via +;; regular semanticdb search. +;; +;; How databases are decided upon is another important aspect of a +;; database search. When it comes to searching for a name, there are +;; these types of searches: +;; +;; Basic Search: +;; Basic search means that tags looking for a given name start +;; with a specific search path. Names are sought on that path +;; until it is empty or items on the path can no longer be found. +;; Use `semanticdb-dump-all-table-summary' to test this list. +;; Use `semanticdb-find-throttle-custom-list' to refine this list. +;; +;; Deep Search: +;; A deep search will search more than just the global namespace. +;; It will recurse into tags that contain more tags, and search +;; those too. +;; +;; Brute Search: +;; Brute search means that all tables in all databases in a given +;; project are searched. Brute searches are the search style as +;; written for semantic version 1.x. +;; +;; How does the search path work? +;; +;; A basic search starts with three parameters: +;; +;; (FINDME &optional PATH FIND-FILE-MATCH) +;; +;; FINDME is key to be searched for dependent on the type of search. +;; PATH is an indicator of which tables are to be searched. +;; FIND-FILE-MATCH indicates that any time a match is found, the +;; file associated with the tag should be read into a file. +;; +;; The PATH argument is then the most interesting argument. It can +;; have these values: +;; +;; nil - Take the current buffer, and use it's include list +;; buffer - Use that buffer's include list. +;; filename - Use that file's include list. If the file is not +;; in a buffer, see of there is a semanticdb table for it. If +;; not, read that file into a buffer. +;; tag - Get that tag's buffer of file file. See above. +;; table - Search that table, and it's include list. +;; +;; Search Results: +;; +;; Semanticdb returns the results in a specific format. There are a +;; series of routines for using those results, and results can be +;; passed in as a search-path for refinement searches with +;; semanticdb. Apropos for semanticdb.*find-result for more. +;; +;; Application: +;; +;; Here are applications where different searches are needed which +;; exist as of semantic 1.4.x +;; +;; eldoc - popup help +;; => Requires basic search using default path. (Header files ok) +;; tag jump - jump to a named tag +;; => Requires a brute search useing whole project. (Source files only) +;; completion - Completing symbol names in a smart way +;; => Basic search (headers ok) +;; type analysis - finding type definitions for variables & fcns +;; => Basic search (headers ok) +;; Class browser - organize types into some structure +;; => Brute search, or custom navigation. + +;; TODO: +;; During a search, load any unloaded DB files based on paths in the +;; current project. + +(require 'semantic/db) +(require 'semantic/db-ref) +(eval-when-compile + (require 'semantic/find)) + +;;; Code: + +(defvar data-debug-thing-alist) +(declare-function data-debug-insert-stuff-list "data-debug") +(declare-function data-debug-insert-tag-list "data-debug") +(declare-function semantic-scope-reset-cache "semantic/scope") +(declare-function semanticdb-typecache-notify-reset "semantic/db-typecache") +(declare-function ede-current-project "ede") + +(defvar semanticdb-find-throttle-custom-list + '(repeat (radio (const 'local) + (const 'project) + (const 'unloaded) + (const 'system) + (const 'recursive) + (const 'omniscience))) + "Customization values for semanticdb find throttle. +See `semanticdb-find-throttle' for details.") + +;;;###autoload +(defcustom semanticdb-find-default-throttle + '(local project unloaded system recursive) + "The default throttle for `semanticdb-find' routines. +The throttle controls how detailed the list of database +tables is for a symbol lookup. The value is a list with +the following keys: + `file' - The file the search is being performed from. + This option is here for completeness only, and + is assumed to always be on. + `local' - Tables from the same local directory are included. + This includes files directly referenced by a file name + which might be in a different directory. + `project' - Tables from the same local project are included + If `project' is specified, then `local' is assumed. + `unloaded' - If a table is not in memory, load it. If it is not cached + on disk either, get the source, parse it, and create + the table. + `system' - Tables from system databases. These are specifically + tables from system header files, or language equivalent. + `recursive' - For include based searches, includes tables referenced + by included files. + `omniscience' - Included system databases which are omniscience, or + somehow know everything. Omniscience databases are found + in `semanticdb-project-system-databases'. + The Emacs Lisp system DB is an omniscience database." + :group 'semanticdb + :type semanticdb-find-throttle-custom-list) + +(defun semanticdb-find-throttle-active-p (access-type) + "Non-nil if ACCESS-TYPE is an active throttle type." + (or (memq access-type semanticdb-find-default-throttle) + (eq access-type 'file) + (and (eq access-type 'local) + (memq 'project semanticdb-find-default-throttle)) + )) + +;;; Index Class +;; +;; The find routines spend a lot of time looking stuff up. +;; Use this handy search index to cache data between searches. +;; This should allow searches to start running faster. +(defclass semanticdb-find-search-index (semanticdb-abstract-search-index) + ((include-path :initform nil + :documentation + "List of semanticdb tables from the include path.") + (type-cache :initform nil + :documentation + "Cache of all the data types accessible from this file. +Includes all types from all included files, merged namespaces, and +expunge duplicates.") + ) + "Concrete search index for `semanticdb-find'. +This class will cache data derived during various searches.") + +(defmethod semantic-reset ((idx semanticdb-find-search-index)) + "Reset the object IDX." + (require 'semantic/scope) + ;; Clear the include path. + (oset idx include-path nil) + (when (oref idx type-cache) + (semantic-reset (oref idx type-cache))) + ;; Clear the scope. Scope doesn't have the data it needs to track + ;; it's own reset. + (semantic-scope-reset-cache) + ) + +(defmethod semanticdb-synchronize ((idx semanticdb-find-search-index) + new-tags) + "Synchronize the search index IDX with some NEW-TAGS." + ;; Reset our parts. + (semantic-reset idx) + ;; Notify dependants by clearning their indicies. + (semanticdb-notify-references + (oref idx table) + (lambda (tab me) + (semantic-reset (semanticdb-get-table-index tab)))) + ) + +(defmethod semanticdb-partial-synchronize ((idx semanticdb-find-search-index) + new-tags) + "Synchronize the search index IDX with some changed NEW-TAGS." + ;; Only reset if include statements changed. + (if (semantic-find-tags-by-class 'include new-tags) + (progn + (semantic-reset idx) + ;; Notify dependants by clearning their indicies. + (semanticdb-notify-references + (oref idx table) + (lambda (tab me) + (semantic-reset (semanticdb-get-table-index tab)))) + ) + ;; Else, not an include, by just a type. + (when (oref idx type-cache) + (when (semanticdb-partial-synchronize (oref idx type-cache) new-tags) + ;; If the synchronize returns true, we need to notify. + ;; Notify dependants by clearning their indicies. + (semanticdb-notify-references + (oref idx table) + (lambda (tab me) + (let ((tab-idx (semanticdb-get-table-index tab))) + ;; Not a full reset? + (when (oref tab-idx type-cache) + (require 'db-typecache) + (semanticdb-typecache-notify-reset + (oref tab-idx type-cache))) + ))) + )) + )) + + +;;; Path Translations +;; +;;; OVERLOAD Functions +;; +;; These routines needed to be overloaded by specific language modes. +;; They are needed for translating an INCLUDE tag into a semanticdb +;; TABLE object. +;;;###autoload +(define-overloadable-function semanticdb-find-translate-path (path brutish) + "Translate PATH into a list of semantic tables. +Path translation involves identifying the PATH input argument +in one of the following ways: + nil - Take the current buffer, and use it's include list + buffer - Use that buffer's include list. + filename - Use that file's include list. If the file is not + in a buffer, see of there is a semanticdb table for it. If + not, read that file into a buffer. + tag - Get that tag's buffer of file file. See above. + table - Search that table, and it's include list. + find result - Search the results of a previous find. + +In addition, once the base path is found, there is the possibility of +each added table adding yet more tables to the path, so this routine +can return a lengthy list. + +If argument BRUTISH is non-nil, then instead of using the include +list, use all tables found in the parent project of the table +identified by translating PATH. Such searches use brute force to +scan every available table. + +The return value is a list of objects of type `semanticdb-table' or +it's children. In the case of passing in a find result, the result +is returned unchanged. + +This routine uses `semanticdb-find-table-for-include' to translate +specific include tags into a semanticdb table. + +Note: When searching using a non-brutish method, the list of +included files will be cached between runs. Database-references +are used to track which files need to have their include lists +refreshed when things change. See `semanticdb-ref-test'. + +Note for overloading: If you opt to overload this function for your +major mode, and your routine takes a long time, be sure to call + + (semantic-throw-on-input 'your-symbol-here) + +so that it can be called from the idle work handler." + ) + +(defun semanticdb-find-translate-path-default (path brutish) + "Translate PATH into a list of semantic tables. +If BRUTISH is non nil, return all tables associated with PATH. +Default action as described in `semanticdb-find-translate-path'." + (if (semanticdb-find-results-p path) + ;; nil means perform the search over these results. + nil + (if brutish + (semanticdb-find-translate-path-brutish-default path) + (semanticdb-find-translate-path-includes-default path)))) + +;;;###autoload +(define-overloadable-function semanticdb-find-table-for-include (includetag &optional table) + "For a single INCLUDETAG found in TABLE, find a `semanticdb-table' object +INCLUDETAG is a semantic TAG of class 'include. +TABLE is a semanticdb table that identifies where INCLUDETAG came from. +TABLE is optional if INCLUDETAG has an overlay of :filename attribute." + ) + +(defun semanticdb-find-translate-path-brutish-default (path) + "Translate PATH into a list of semantic tables. +Default action as described in `semanticdb-find-translate-path'." + (let ((basedb + (cond ((null path) semanticdb-current-database) + ((semanticdb-table-p path) (oref path parent-db)) + (t (let ((tt (semantic-something-to-tag-table path))) + (save-excursion + ;; @todo - What does this DO ??!?! + (set-buffer (semantic-tag-buffer (car tt))) + semanticdb-current-database)))))) + (apply + #'nconc + (mapcar + (lambda (db) + (let ((tabs (semanticdb-get-database-tables db)) + (ret nil)) + ;; Only return tables of the same language (major-mode) + ;; as the current search environment. + (while tabs + + (semantic-throw-on-input 'translate-path-brutish) + + (if (semanticdb-equivalent-mode-for-search (car tabs) + (current-buffer)) + (setq ret (cons (car tabs) ret))) + (setq tabs (cdr tabs))) + ret)) + ;; FIXME: + ;; This should scan the current project directory list for all + ;; semanticdb files, perhaps handling proxies for them. + (semanticdb-current-database-list + (if basedb (oref basedb reference-directory) + default-directory)))) + )) + +(defun semanticdb-find-incomplete-cache-entries-p (cache) + "Are there any incomplete entries in CACHE?" + (let ((ans nil)) + (dolist (tab cache) + (when (and (semanticdb-table-child-p tab) + (not (number-or-marker-p (oref tab pointmax)))) + (setq ans t)) + ) + ans)) + +(defun semanticdb-find-need-cache-update-p (table) + "Non nil if the semanticdb TABLE cache needs to be updated." + ;; If we were passed in something related to a TABLE, + ;; do a caching lookup. + (let* ((index (semanticdb-get-table-index table)) + (cache (when index (oref index include-path))) + (incom (semanticdb-find-incomplete-cache-entries-p cache)) + (unl (semanticdb-find-throttle-active-p 'unloaded)) + ) + (if (and + cache ;; Must have a cache + (or + ;; If all entries are "full", or if 'unloaded + ;; OR + ;; is not in the throttle, it is ok to use the cache. + (not incom) (not unl) + )) + nil + ;;cache + ;; ELSE + ;; + ;; We need an update. + t)) + ) + +(defun semanticdb-find-translate-path-includes-default (path) + "Translate PATH into a list of semantic tables. +Default action as described in `semanticdb-find-translate-path'." + (let ((table (cond ((null path) + semanticdb-current-table) + ((bufferp path) + (semantic-buffer-local-value 'semanticdb-current-table path)) + ((and (stringp path) (file-exists-p path)) + (semanticdb-file-table-object path t)) + ((semanticdb-abstract-table-child-p path) + path) + (t nil)))) + (if table + ;; If we were passed in something related to a TABLE, + ;; do a caching lookup. + (let ((index (semanticdb-get-table-index table))) + (if (semanticdb-find-need-cache-update-p table) + ;; Lets go look up our indicies + (let ((ans (semanticdb-find-translate-path-includes--internal path))) + (oset index include-path ans) + ;; Once we have our new indicies set up, notify those + ;; who depend on us if we found something for them to + ;; depend on. + (when ans (semanticdb-refresh-references table)) + ans) + ;; ELSE + ;; + ;; Just return the cache. + (oref index include-path))) + ;; If we were passed in something like a tag list, or other boring + ;; searchable item, then instead do the regular thing without caching. + (semanticdb-find-translate-path-includes--internal path)))) + +(defvar semanticdb-find-lost-includes nil + "Include files that we cannot find associated with this buffer.") +(make-variable-buffer-local 'semanticdb-find-lost-includes) + +(defvar semanticdb-find-scanned-include-tags nil + "All include tags scanned, plus action taken on the tag. +Each entry is an alist: + (ACTION . TAG) +where ACTION is one of 'scanned, 'duplicate, 'lost. +and TAG is a clone of the include tag that was found.") +(make-variable-buffer-local 'semanticdb-find-scanned-include-tags) + +(defvar semanticdb-implied-include-tags nil + "Include tags implied for all files of a given mode. +Set this variable with `defvar-mode-local' for a particular mode so +that any symbols that exist for all files for that mode are included. + +Note: This could be used as a way to write a file in a langauge +to declare all the built-ins for that language.") + +(defun semanticdb-find-translate-path-includes--internal (path) + "Internal implementation of `semanticdb-find-translate-path-includes-default'. +This routine does not depend on the cache, but will always derive +a new path from the provided PATH." + (let ((includetags nil) + (curtable nil) + (matchedtables (list semanticdb-current-table)) + (matchedincludes nil) + (lostincludes nil) + (scannedincludes nil) + (incfname nil) + nexttable) + (cond ((null path) + (semantic-refresh-tags-safe) + (setq includetags (append + (semantic-find-tags-included (current-buffer)) + semanticdb-implied-include-tags) + curtable semanticdb-current-table + incfname (buffer-file-name)) + ) + ((semanticdb-table-p path) + (setq includetags (semantic-find-tags-included path) + curtable path + incfname (semanticdb-full-filename path)) + ) + ((bufferp path) + (save-excursion + (set-buffer path) + (semantic-refresh-tags-safe)) + (setq includetags (semantic-find-tags-included path) + curtable (save-excursion (set-buffer path) + semanticdb-current-table) + incfname (buffer-file-name path))) + (t + (setq includetags (semantic-find-tags-included path)) + (when includetags + ;; If we have some tags, derive a table from them. + ;; else we will do nothing, so the table is useless. + + ;; @todo - derive some tables + (message "Need to derive tables for %S in translate-path-includes--default." + path) + ))) + + ;; Make sure each found include tag has an originating file name associated + ;; with it. + (when incfname + (dolist (it includetags) + (semantic--tag-put-property it :filename incfname))) + + ;; Loop over all include tags adding to matchedtables + (while includetags + (semantic-throw-on-input 'semantic-find-translate-path-includes-default) + + ;; If we've seen this include string before, lets skip it. + (if (member (semantic-tag-name (car includetags)) matchedincludes) + (progn + (setq nexttable nil) + (push (cons 'duplicate (semantic-tag-clone (car includetags))) + scannedincludes) + ) + (setq nexttable (semanticdb-find-table-for-include (car includetags) curtable)) + (when (not nexttable) + ;; Save the lost include. + (push (car includetags) lostincludes) + (push (cons 'lost (semantic-tag-clone (car includetags))) + scannedincludes) + ) + ) + + ;; Push the include file, so if we can't find it, we only + ;; can't find it once. + (push (semantic-tag-name (car includetags)) matchedincludes) + + ;; (message "Scanning %s" (semantic-tag-name (car includetags))) + (when (and nexttable + (not (memq nexttable matchedtables)) + (semanticdb-equivalent-mode-for-search nexttable + (current-buffer)) + ) + ;; Add to list of tables + (push nexttable matchedtables) + + ;; Queue new includes to list + (if (semanticdb-find-throttle-active-p 'recursive) + ;; @todo - recursive includes need to have the originating + ;; buffer's location added to the path. + (let ((newtags + (cond + ((semanticdb-table-p nexttable) + (semanticdb-refresh-table nexttable) + ;; Use the method directly, or we will recurse + ;; into ourselves here. + (semanticdb-find-tags-by-class-method + nexttable 'include)) + (t ;; @todo - is this ever possible??? + (message "semanticdb-ftp - how did you do that?") + (semantic-find-tags-included + (semanticdb-get-tags nexttable))) + )) + (newincfname (semanticdb-full-filename nexttable)) + ) + + (push (cons 'scanned (semantic-tag-clone (car includetags))) + scannedincludes) + + ;; Setup new tags so we know where they are. + (dolist (it newtags) + (semantic--tag-put-property it :filename + newincfname)) + + (setq includetags (nconc includetags newtags))) + ;; ELSE - not recursive throttle + (push (cons 'scanned-no-recurse + (semantic-tag-clone (car includetags))) + scannedincludes) + ) + ) + (setq includetags (cdr includetags))) + + (setq semanticdb-find-lost-includes lostincludes) + (setq semanticdb-find-scanned-include-tags (reverse scannedincludes)) + + ;; Find all the omniscient databases for this major mode, and + ;; add them if needed + (when (and (semanticdb-find-throttle-active-p 'omniscience) + semanticdb-search-system-databases) + ;; We can append any mode-specific omniscience databases into + ;; our search list here. + (let ((systemdb semanticdb-project-system-databases) + (ans nil)) + (while systemdb + (setq ans (semanticdb-file-table + (car systemdb) + ;; I would expect most omniscient to return the same + ;; thing reguardless of filename, but we may have + ;; one that can return a table of all things the + ;; current file needs. + (buffer-file-name (current-buffer)))) + (when (not (memq ans matchedtables)) + (setq matchedtables (cons ans matchedtables))) + (setq systemdb (cdr systemdb)))) + ) + (nreverse matchedtables))) + +(define-overloadable-function semanticdb-find-load-unloaded (filename) + "Create a database table for FILENAME if it hasn't been parsed yet. +Assumes that FILENAME exists as a source file. +Assumes that a preexisting table does not exist, even if it +isn't in memory yet." + (if (semanticdb-find-throttle-active-p 'unloaded) + (:override) + (semanticdb-file-table-object filename t))) + +(defun semanticdb-find-load-unloaded-default (filename) + "Load an unloaded file in FILENAME using the default semanticdb loader." + (semanticdb-file-table-object filename)) + +;; The creation of the overload occurs above. +(defun semanticdb-find-table-for-include-default (includetag &optional table) + "Default implementation of `semanticdb-find-table-for-include'. +Uses `semanticdb-current-database-list' as the search path. +INCLUDETAG and TABLE are documented in `semanticdb-find-table-for-include'. +Included databases are filtered based on `semanticdb-find-default-throttle'." + (if (not (eq (semantic-tag-class includetag) 'include)) + (signal 'wrong-type-argument (list includetag 'include))) + + (let ((name + ;; Note, some languages (like Emacs or Java) use include tag names + ;; that don't represent files! We want to have file names. + (semantic-tag-include-filename includetag)) + (originfiledir nil) + (roots nil) + (tmp nil) + (ans nil)) + + ;; INCLUDETAG should have some way to reference where it came + ;; from! If not, TABLE should provide the way. Each time we + ;; look up a tag, we may need to find it in some relative way + ;; and must set our current buffer eto the origin of includetag + ;; or nothing may work. + (setq originfiledir + (cond ((semantic-tag-file-name includetag) + ;; A tag may have a buffer, or a :filename property. + (file-name-directory (semantic-tag-file-name includetag))) + (table + (file-name-directory (semanticdb-full-filename table))) + (t + ;; @todo - what to do here? Throw an error maybe + ;; and fix usage bugs? + default-directory))) + + (cond + ;; Step 1: Relative path name + ;; + ;; If the name is relative, then it should be findable as relative + ;; to the source file that this tag originated in, and be fast. + ;; + ((and (semanticdb-find-throttle-active-p 'local) + (file-exists-p (expand-file-name name originfiledir))) + + (setq ans (semanticdb-find-load-unloaded + (expand-file-name name originfiledir))) + ) + ;; Step 2: System or Project level includes + ;; + ((or + ;; First, if it a system include, we can investigate that tags + ;; dependency file + (and (semanticdb-find-throttle-active-p 'system) + + ;; Sadly, not all languages make this distinction. + ;;(semantic-tag-include-system-p includetag) + + ;; Here, we get local and system files. + (setq tmp (semantic-dependency-tag-file includetag)) + ) + ;; Second, project files are active, we and we have EDE, + ;; we can find it using the same tool. + (and (semanticdb-find-throttle-active-p 'project) + ;; Make sure EDE is available, and we have a project + (featurep 'ede) (ede-current-project originfiledir) + ;; The EDE query is hidden in this call. + (setq tmp (semantic-dependency-tag-file includetag)) + ) + ) + (setq ans (semanticdb-find-load-unloaded tmp)) + ) + ;; Somewhere in our project hierarchy + ;; + ;; Remember: Roots includes system databases which can create + ;; specialized tables we can search. + ;; + ;; NOTE: Not used if EDE is active! + ((and (semanticdb-find-throttle-active-p 'project) + ;; And dont do this if it is a system include. Not supported by all languages, + ;; but when it is, this is a nice fast way to skip this step. + (not (semantic-tag-include-system-p includetag)) + ;; Don't do this if we have an EDE project. + (not (and (featurep 'ede) + ;; Note: We don't use originfiledir here because + ;; we want to know about the source file we are + ;; starting from. + (ede-current-project))) + ) + + (setq roots (semanticdb-current-database-list)) + + (while (and (not ans) roots) + (let* ((ref (if (slot-boundp (car roots) 'reference-directory) + (oref (car roots) reference-directory))) + (fname (cond ((null ref) nil) + ((file-exists-p (expand-file-name name ref)) + (expand-file-name name ref)) + ((file-exists-p (expand-file-name (file-name-nondirectory name) ref)) + (expand-file-name (file-name-nondirectory name) ref))))) + (when (and ref fname) + ;; There is an actual file. Grab it. + (setq ans (semanticdb-find-load-unloaded fname))) + + ;; ELSE + ;; + ;; NOTE: We used to look up omniscient databases here, but that + ;; is now handled one layer up. + ;; + ;; Missing: a database that knows where missing files are. Hmm. + ;; perhaps I need an override function for that? + + ) + + (setq roots (cdr roots)))) + ) + ans)) + + +;;; Perform interactive tests on the path/search mechanisms. +;; +;;;###autoload +(defun semanticdb-find-test-translate-path (&optional arg) + "Call and output results of `semanticdb-find-translate-path'. +With ARG non-nil, specify a BRUTISH translation. +See `semanticdb-find-default-throttle' and `semanticdb-project-roots' +for details on how this list is derived." + (interactive "P") + (semantic-fetch-tags) + (require 'data-debug) + (let ((start (current-time)) + (p (semanticdb-find-translate-path nil arg)) + (end (current-time)) + ) + (data-debug-new-buffer "*SEMANTICDB FTP ADEBUG*") + (message "Search of tags took %.2f seconds." + (semantic-elapsed-time start end)) + + (data-debug-insert-stuff-list p "*"))) + +(defun semanticdb-find-test-translate-path-no-loading (&optional arg) + "Call and output results of `semanticdb-find-translate-path'. +With ARG non-nil, specify a BRUTISH translation. +See `semanticdb-find-default-throttle' and `semanticdb-project-roots' +for details on how this list is derived." + (interactive "P") + (semantic-fetch-tags) + (require 'data-debug) + (let* ((semanticdb-find-default-throttle + (if (featurep 'semantic/db-find) + (remq 'unloaded semanticdb-find-default-throttle) + nil)) + (start (current-time)) + (p (semanticdb-find-translate-path nil arg)) + (end (current-time)) + ) + (data-debug-new-buffer "*SEMANTICDB FTP ADEBUG*") + (message "Search of tags took %.2f seconds." + (semantic-elapsed-time start end)) + + (data-debug-insert-stuff-list p "*"))) + +;;;###autoload +(defun semanticdb-find-adebug-lost-includes () + "Translate the current path, then display the lost includes. +Examines the variable `semanticdb-find-lost-includes'." + (interactive) + (require 'data-debug) + (semanticdb-find-translate-path nil nil) + (let ((lost semanticdb-find-lost-includes) + ) + + (if (not lost) + (message "There are no unknown includes for %s" + (buffer-name)) + + (data-debug-new-buffer "*SEMANTICDB lost-includes ADEBUG*") + (data-debug-insert-tag-list lost "*") + ))) + +(defun semanticdb-find-adebug-insert-scanned-tag-cons (consdata prefix prebuttontext) + "Insert a button representing scanned include CONSDATA. +PREFIX is the text that preceeds the button. +PREBUTTONTEXT is some text between prefix and the overlay button." + (let* ((start (point)) + (end nil) + (mode (car consdata)) + (tag (cdr consdata)) + (name (semantic-tag-name tag)) + (file (semantic-tag-file-name tag)) + (str1 (format "%S %s" mode name)) + (str2 (format " : %s" file)) + (tip nil)) + (insert prefix prebuttontext str1) + (setq end (point)) + (insert str2) + (put-text-property start end 'face + (cond ((eq mode 'scanned) + 'font-lock-function-name-face) + ((eq mode 'duplicate) + 'font-lock-comment-face) + ((eq mode 'lost) + 'font-lock-variable-name-face) + ((eq mode 'scanned-no-recurse) + 'font-lock-type-face))) + (put-text-property start end 'ddebug (cdr consdata)) + (put-text-property start end 'ddebug-indent(length prefix)) + (put-text-property start end 'ddebug-prefix prefix) + (put-text-property start end 'help-echo tip) + (put-text-property start end 'ddebug-function + 'data-debug-insert-tag-parts-from-point) + (insert "\n") + ) + ) + +(defun semanticdb-find-adebug-scanned-includes () + "Translate the current path, then display the lost includes. +Examines the variable `semanticdb-find-lost-includes'." + (interactive) + (require 'data-debug) + (semanticdb-find-translate-path nil nil) + (let ((scanned semanticdb-find-scanned-include-tags) + (data-debug-thing-alist + (cons + '((lambda (thing) (and (consp thing) + (symbolp (car thing)) + (memq (car thing) + '(scanned scanned-no-recurse + lost duplicate)))) + . semanticdb-find-adebug-insert-scanned-tag-cons) + data-debug-thing-alist)) + ) + + (if (not scanned) + (message "There are no includes scanned %s" + (buffer-name)) + + (data-debug-new-buffer "*SEMANTICDB scanned-includes ADEBUG*") + (data-debug-insert-stuff-list scanned "*") + ))) + +;;; API Functions +;; +;; Once you have a search result, use these routines to operate +;; on the search results at a higher level + +;;;###autoload +(defun semanticdb-strip-find-results (results &optional find-file-match) + "Strip a semanticdb search RESULTS to exclude objects. +This makes it appear more like the results of a `semantic-find-' call. +Optional FIND-FILE-MATCH loads all files associated with RESULTS +into buffers. This has the side effect of enabling `semantic-tag-buffer' to +return a value. +If FIND-FILE-MATCH is 'name, then only the filename is stored +in each tag instead of loading each file into a buffer. +If the input RESULTS are not going to be used again, and if +FIND-FILE-MATCH is nil, you can use `semanticdb-fast-strip-find-results' +instead." + (if find-file-match + ;; Load all files associated with RESULTS. + (let ((tmp results) + (output nil)) + (while tmp + (let ((tab (car (car tmp))) + (tags (cdr (car tmp)))) + (dolist (T tags) + ;; Normilzation gives specialty database tables a chance + ;; to convert into a more stable tag format. + (let* ((norm (semanticdb-normalize-one-tag tab T)) + (ntab (car norm)) + (ntag (cdr norm)) + (nametable ntab)) + + ;; If it didn't normalize, use what we had. + (if (not norm) + (setq nametable tab) + (setq output (append output (list ntag)))) + + ;; Find-file-match allows a tool to make sure the tag is + ;; 'live', somewhere in a buffer. + (cond ((eq find-file-match 'name) + (let ((f (semanticdb-full-filename nametable))) + (semantic--tag-put-property ntag :filename f))) + ((and find-file-match ntab) + (semanticdb-get-buffer ntab)) + ) + )) + ) + (setq tmp (cdr tmp))) + output) + ;; @todo - I could use nconc, but I don't know what the caller may do with + ;; RESULTS after this is called. Right now semantic-complete will + ;; recycling the input after calling this routine. + (apply #'append (mapcar #'cdr results)))) + +(defun semanticdb-fast-strip-find-results (results) + "Destructively strip a semanticdb search RESULTS to exclude objects. +This makes it appear more like the results of a `semantic-find-' call. +This is like `semanticdb-strip-find-results', except the input list RESULTS +will be changed." + (apply #'nconc (mapcar #'cdr results))) + +(defun semanticdb-find-results-p (resultp) + "Non-nil if RESULTP is in the form of a semanticdb search result. +This query only really tests the first entry in the list that is RESULTP, +but should be good enough for debugging assertions." + (and (listp resultp) + (listp (car resultp)) + (semanticdb-abstract-table-child-p (car (car resultp))) + (or (semantic-tag-p (car (cdr (car resultp)))) + (null (car (cdr (car resultp))))))) + +(defun semanticdb-find-result-prin1-to-string (result) + "Presuming RESULT satisfies `semanticdb-find-results-p', provide a short PRIN1 output." + (if (< (length result) 2) + (concat "#<FIND RESULT " + (mapconcat (lambda (a) + (concat "(" (object-name (car a) ) " . " + "#<TAG LIST " (number-to-string (length (cdr a))) ">)")) + result + " ") + ">") + ;; Longer results should have an abreviated form. + (format "#<FIND RESULT %d TAGS in %d FILES>" + (semanticdb-find-result-length result) + (length result)))) + +(defun semanticdb-find-result-with-nil-p (resultp) + "Non-nil of RESULTP is in the form of a semanticdb search result. +nil is a valid value where a TABLE usually is, but only if the TAG +results include overlays. +This query only really tests the first entry in the list that is RESULTP, +but should be good enough for debugging assertions." + (and (listp resultp) + (listp (car resultp)) + (let ((tag-to-test (car-safe (cdr (car resultp))))) + (or (and (semanticdb-abstract-table-child-p (car (car resultp))) + (or (semantic-tag-p tag-to-test) + (null tag-to-test))) + (and (null (car (car resultp))) + (or (semantic-tag-with-position-p tag-to-test) + (null tag-to-test)))) + ))) + +;;;###autoload +(defun semanticdb-find-result-length (result) + "Number of tags found in RESULT." + (let ((count 0)) + (mapc (lambda (onetable) + (setq count (+ count (1- (length onetable))))) + result) + count)) + +;;;###autoload +(defun semanticdb-find-result-nth (result n) + "In RESULT, return the Nth search result. +This is a 0 based search result, with the first match being element 0. + +The returned value is a cons cell: (TAG . TABLE) where TAG +is the tag at the Nth position. TABLE is the semanticdb table where +the TAG was found. Sometimes TABLE can be nil." + (let ((ans nil) + (anstable nil)) + ;; Loop over each single table hit. + (while (and (not ans) result) + ;; For each table result, get local length, and modify + ;; N to be that much less. + (let ((ll (length (cdr (car result))))) ;; local length + (if (> ll n) + ;; We have a local match. + (setq ans (nth n (cdr (car result))) + anstable (car (car result))) + ;; More to go. Decrement N. + (setq n (- n ll)))) + ;; Keep moving. + (setq result (cdr result))) + (cons ans anstable))) + +(defun semanticdb-find-result-test (result) + "Test RESULT by accessing all the tags in the list." + (if (not (semanticdb-find-results-p result)) + (error "Does not pass `semanticdb-find-results-p.\n")) + (let ((len (semanticdb-find-result-length result)) + (i 0)) + (while (< i len) + (let ((tag (semanticdb-find-result-nth result i))) + (if (not (semantic-tag-p (car tag))) + (error "%d entry is not a tag" i))) + (setq i (1+ i))))) + +;;;###autoload +(defun semanticdb-find-result-nth-in-buffer (result n) + "In RESULT, return the Nth search result. +Like `semanticdb-find-result-nth', except that only the TAG +is returned, and the buffer it is found it will be made current. +If the result tag has no position information, the originating buffer +is still made current." + (let* ((ret (semanticdb-find-result-nth result n)) + (ans (car ret)) + (anstable (cdr ret))) + ;; If we have a hit, double-check the find-file + ;; entry. If the file must be loaded, then gat that table's + ;; source file into a buffer. + + (if anstable + (let ((norm (semanticdb-normalize-one-tag anstable ans))) + (when norm + ;; The normalized tags can now be found based on that + ;; tags table. + (semanticdb-set-buffer (car norm)) + ;; Now reset ans + (setq ans (cdr norm)) + )) + ) + ;; Return the tag. + ans)) + +(defun semanticdb-find-result-mapc (fcn result) + "Apply FCN to each element of find RESULT for side-effects only. +FCN takes two arguments. The first is a TAG, and the +second is a DB from wence TAG originated. +Returns result." + (mapc (lambda (sublst) + (mapc (lambda (tag) + (funcall fcn tag (car sublst))) + (cdr sublst))) + result) + result) + +;;; Search Logging +;; +;; Basic logging to see what the search routines are doing. +(defvar semanticdb-find-log-flag nil + "Non-nil means log the process of searches.") + +(defvar semanticdb-find-log-buffer-name "*SemanticDB Find Log*" + "The name of the logging buffer.") + +(defun semanticdb-find-toggle-logging () + "Toggle sematnicdb logging." + (interactive) + (setq semanticdb-find-log-flag (null semanticdb-find-log-flag)) + (message "Semanticdb find logging is %sabled" + (if semanticdb-find-log-flag "en" "dis"))) + +(defun semanticdb-reset-log () + "Reset the log buffer." + (interactive) + (when semanticdb-find-log-flag + (save-excursion + (set-buffer (get-buffer-create semanticdb-find-log-buffer-name)) + (erase-buffer) + ))) + +(defun semanticdb-find-log-move-to-end () + "Move to the end of the semantic log." + (let ((cb (current-buffer)) + (cw (selected-window))) + (unwind-protect + (progn + (set-buffer semanticdb-find-log-buffer-name) + (if (get-buffer-window (current-buffer) 'visible) + (select-window (get-buffer-window (current-buffer) 'visible))) + (goto-char (point-max))) + (if cw (select-window cw)) + (set-buffer cb)))) + +(defun semanticdb-find-log-new-search (forwhat) + "Start a new search FORWHAT." + (when semanticdb-find-log-flag + (save-excursion + (set-buffer (get-buffer-create semanticdb-find-log-buffer-name)) + (insert (format "New Search: %S\n" forwhat)) + ) + (semanticdb-find-log-move-to-end))) + +(defun semanticdb-find-log-activity (table result) + "Log that TABLE has been searched and RESULT was found." + (when semanticdb-find-log-flag + (save-excursion + (set-buffer semanticdb-find-log-buffer-name) + (insert "Table: " (object-print table) + " Result: " (int-to-string (length result)) " tags" + "\n") + ) + (semanticdb-find-log-move-to-end))) + +;;; Semanticdb find API functions +;; These are the routines actually used to perform searches. +;; +(defun semanticdb-find-tags-collector (function &optional path find-file-match + brutish) + "Collect all tags returned by FUNCTION over PATH. +The FUNCTION must take two arguments. The first is TABLE, +which is a semanticdb table containing tags. The second argument +to FUNCTION is TAGS. TAGS may be a list of tags. If TAGS is non-nil, then +FUNCTION should search the TAG list, not through TABLE. + +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer. + +Note: You should leave FIND-FILE-MATCH as nil. It is far more +efficient to take the results from any search and use +`semanticdb-strip-find-results' instead. This argument is here +for backward compatibility. + +If optional argument BRUTISH is non-nil, then ignore include statements, +and search all tables in this project tree." + (let (found match) + (save-excursion + ;; If path is a buffer, set ourselves up in that buffer + ;; so that the override methods work correctly. + (when (bufferp path) (set-buffer path)) + (if (semanticdb-find-results-p path) + ;; When we get find results, loop over that. + (dolist (tableandtags path) + (semantic-throw-on-input 'semantic-find-translate-path) + ;; If FIND-FILE-MATCH is non-nil, skip tables of class + ;; `semanticdb-search-results-table', since those are system + ;; databases and not associated with a file. + (unless (and find-file-match + (obj-of-class-p + (car tableandtags) semanticdb-search-results-table)) + (when (setq match (funcall function + (car tableandtags) (cdr tableandtags))) + (when find-file-match + (save-excursion (semanticdb-set-buffer (car tableandtags)))) + (push (cons (car tableandtags) match) found))) + ) + ;; Only log searches across data bases. + (semanticdb-find-log-new-search nil) + ;; If we get something else, scan the list of tables resulting + ;; from translating it into a list of objects. + (dolist (table (semanticdb-find-translate-path path brutish)) + (semantic-throw-on-input 'semantic-find-translate-path) + ;; If FIND-FILE-MATCH is non-nil, skip tables of class + ;; `semanticdb-search-results-table', since those are system + ;; databases and not associated with a file. + (unless (and find-file-match + (obj-of-class-p table semanticdb-search-results-table)) + (when (and table (setq match (funcall function table nil))) + (semanticdb-find-log-activity table match) + (when find-file-match + (save-excursion (semanticdb-set-buffer table))) + (push (cons table match) found)))))) + ;; At this point, FOUND has had items pushed onto it. + ;; This means items are being returned in REVERSE order + ;; of the tables searched, so if you just get th CAR, then + ;; too-bad, you may have some system-tag that has no + ;; buffer associated with it. + + ;; It must be reversed. + (nreverse found))) + +;;;###autoload +(defun semanticdb-find-tags-by-name (name &optional path find-file-match) + "Search for all tags matching NAME on PATH. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-by-name-method table name tags)) + path find-file-match)) + +;;;###autoload +(defun semanticdb-find-tags-by-name-regexp (regexp &optional path find-file-match) + "Search for all tags matching REGEXP on PATH. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-by-name-regexp-method table regexp tags)) + path find-file-match)) + +;;;###autoload +(defun semanticdb-find-tags-for-completion (prefix &optional path find-file-match) + "Search for all tags matching PREFIX on PATH. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-for-completion-method table prefix tags)) + path find-file-match)) + +;;;###autoload +(defun semanticdb-find-tags-by-class (class &optional path find-file-match) + "Search for all tags of CLASS on PATH. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-by-class-method table class tags)) + path find-file-match)) + +;;; Deep Searches +(defun semanticdb-deep-find-tags-by-name (name &optional path find-file-match) + "Search for all tags matching NAME on PATH. +Search also in all components of top level tags founds. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-deep-find-tags-by-name-method table name tags)) + path find-file-match)) + +(defun semanticdb-deep-find-tags-by-name-regexp (regexp &optional path find-file-match) + "Search for all tags matching REGEXP on PATH. +Search also in all components of top level tags founds. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-deep-find-tags-by-name-regexp-method table regexp tags)) + path find-file-match)) + +(defun semanticdb-deep-find-tags-for-completion (prefix &optional path find-file-match) + "Search for all tags matching PREFIX on PATH. +Search also in all components of top level tags founds. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-deep-find-tags-for-completion-method table prefix tags)) + path find-file-match)) + +;;; Brutish Search Routines +;; +(defun semanticdb-brute-deep-find-tags-by-name (name &optional path find-file-match) + "Search for all tags matching NAME on PATH. +See `semanticdb-find-translate-path' for details on PATH. +The argument BRUTISH will be set so that searching includes all tables +in the current project. +FIND-FILE-MATCH indicates that any time a matchi is found, the file +associated wit that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-deep-find-tags-by-name-method table name tags)) + path find-file-match t)) + +(defun semanticdb-brute-deep-find-tags-for-completion (prefix &optional path find-file-match) + "Search for all tags matching PREFIX on PATH. +See `semanticdb-find-translate-path' for details on PATH. +The argument BRUTISH will be set so that searching includes all tables +in the current project. +FIND-FILE-MATCH indicates that any time a matchi is found, the file +associated wit that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-deep-find-tags-for-completion-method table prefix tags)) + path find-file-match t)) + +(defun semanticdb-brute-find-tags-by-class (class &optional path find-file-match) + "Search for all tags of CLASS on PATH. +See `semanticdb-find-translate-path' for details on PATH. +The argument BRUTISH will be set so that searching includes all tables +in the current project. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-by-class-method table class tags)) + path find-file-match t)) + +;;; Specialty Search Routines +(defun semanticdb-find-tags-external-children-of-type + (type &optional path find-file-match) + "Search for all tags defined outside of TYPE w/ TYPE as a parent. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-external-children-of-type-method table type tags)) + path find-file-match)) + +(defun semanticdb-find-tags-subclasses-of-type + (type &optional path find-file-match) + "Search for all tags of class type defined that subclass TYPE. +See `semanticdb-find-translate-path' for details on PATH. +FIND-FILE-MATCH indicates that any time a match is found, the file +associated with that tag should be loaded into a buffer." + (semanticdb-find-tags-collector + (lambda (table tags) + (semanticdb-find-tags-subclasses-of-type-method table type tags)) + path find-file-match t)) + +;;; METHODS +;; +;; Default methods for semanticdb database and table objects. +;; Override these with system databases to as new types of back ends. + +;;; Top level Searches +(defmethod semanticdb-find-tags-by-name-method ((table semanticdb-abstract-table) name &optional tags) + "In TABLE, find all occurances of tags with NAME. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (semantic-find-tags-by-name name (or tags (semanticdb-get-tags table)))) + +(defmethod semanticdb-find-tags-by-name-regexp-method ((table semanticdb-abstract-table) regexp &optional tags) + "In TABLE, find all occurances of tags matching REGEXP. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (semantic-find-tags-by-name-regexp regexp (or tags (semanticdb-get-tags table)))) + +(defmethod semanticdb-find-tags-for-completion-method ((table semanticdb-abstract-table) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (semantic-find-tags-for-completion prefix (or tags (semanticdb-get-tags table)))) + +(defmethod semanticdb-find-tags-by-class-method ((table semanticdb-abstract-table) class &optional tags) + "In TABLE, find all occurances of tags of CLASS. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (semantic-find-tags-by-class class (or tags (semanticdb-get-tags table)))) + +(defmethod semanticdb-find-tags-external-children-of-type-method ((table semanticdb-abstract-table) parent &optional tags) + "In TABLE, find all occurances of tags whose parent is the PARENT type. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (require 'semantic/find) + (semantic-find-tags-external-children-of-type parent (or tags (semanticdb-get-tags table)))) + +(defmethod semanticdb-find-tags-subclasses-of-type-method ((table semanticdb-abstract-table) parent &optional tags) + "In TABLE, find all occurances of tags whose parent is the PARENT type. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (require 'semantic/find) + (semantic-find-tags-subclasses-of-type parent (or tags (semanticdb-get-tags table)))) + +;;; Deep Searches +(defmethod semanticdb-deep-find-tags-by-name-method ((table semanticdb-abstract-table) name &optional tags) + "In TABLE, find all occurances of tags with NAME. +Search in all tags in TABLE, and all components of top level tags in +TABLE. +Optional argument TAGS is a list of tags to search. +Return a table of all matching tags." + (semantic-find-tags-by-name name (semantic-flatten-tags-table (or tags (semanticdb-get-tags table))))) + +(defmethod semanticdb-deep-find-tags-by-name-regexp-method ((table semanticdb-abstract-table) regexp &optional tags) + "In TABLE, find all occurances of tags matching REGEXP. +Search in all tags in TABLE, and all components of top level tags in +TABLE. +Optional argument TAGS is a list of tags to search. +Return a table of all matching tags." + (semantic-find-tags-by-name-regexp regexp (semantic-flatten-tags-table (or tags (semanticdb-get-tags table))))) + +(defmethod semanticdb-deep-find-tags-for-completion-method ((table semanticdb-abstract-table) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Search in all tags in TABLE, and all components of top level tags in +TABLE. +Optional argument TAGS is a list of tags to search. +Return a table of all matching tags." + (semantic-find-tags-for-completion prefix (semantic-flatten-tags-table (or tags (semanticdb-get-tags table))))) + +(provide 'semantic/db-find) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/db-find" +;; End: + +;;; semantic/db-find.el ends here diff --git a/lisp/cedet/semantic/db-global.el b/lisp/cedet/semantic/db-global.el new file mode 100644 index 00000000000..7e13e8c7054 --- /dev/null +++ b/lisp/cedet/semantic/db-global.el @@ -0,0 +1,227 @@ +;;; semantic/db-global.el --- Semantic database extensions for GLOBAL + +;;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: tags + +;; 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: +;; +;; Use GNU Global for by-name database searches. +;; +;; This will work as an "omniscient" database for a given project. +;; + +(require 'cedet-global) +(require 'semantic/db-find) +(require 'semantic/symref/global) + +(eval-when-compile + ;; For generic function searching. + (require 'eieio) + (require 'eieio-opt) + ) + +;;; Code: + +(declare-function data-debug-new-buffer "data-debug") +(declare-function data-debug-insert-thing result "data-debug") + +;;;###autoload +(defun semanticdb-enable-gnu-global-databases (mode) + "Enable the use of the GNU Global SemanticDB back end for all files of MODE. +This will add an instance of a GNU Global database to each buffer +in a GNU Global supported hierarchy." + (interactive + (list (completing-read + "Emable in Mode: " obarray + #'(lambda (s) (get s 'mode-local-symbol-table)) + t (symbol-name major-mode)))) + + ;; First, make sure the version is ok. + (cedet-gnu-global-version-check) + + ;; Make sure mode is a symbol. + (when (stringp mode) + (setq mode (intern mode))) + + (let ((ih (mode-local-value mode 'semantic-init-mode-hook))) + (eval `(setq-mode-local + ,mode semantic-init-mode-hook + (cons 'semanticdb-enable-gnu-global-hook ih)))) + + ) + +(defun semanticdb-enable-gnu-global-hook () + "Add support for GNU Global in the current buffer via semantic-init-hook. +MODE is the major mode to support." + (semanticdb-enable-gnu-global-in-buffer t)) + +(defclass semanticdb-project-database-global + ;; @todo - convert to one DB per directory. + (semanticdb-project-database eieio-instance-tracker) + () + "Database representing a GNU Global tags file.") + +(defun semanticdb-enable-gnu-global-in-buffer (&optional dont-err-if-not-available) + "Enable a GNU Global database in the current buffer. +Argument DONT-ERR-IF-NOT-AVAILABLE will throw an error if GNU Global +is not available for this directory." + (interactive "P") + (if (cedet-gnu-global-root) + (setq + ;; Add to the system database list. + semanticdb-project-system-databases + (cons (semanticdb-project-database-global "global") + semanticdb-project-system-databases) + ;; Apply the throttle. + semanticdb-find-default-throttle + (append semanticdb-find-default-throttle + '(omniscience)) + ) + (if dont-err-if-not-available + (message "No Global support in %s" default-directory) + (error "No Global support in %s" default-directory)) + )) + +;;; Classes: +(defclass semanticdb-table-global (semanticdb-search-results-table) + ((major-mode :initform nil) + ) + "A table for returning search results from GNU Global.") + +(defmethod semanticdb-equivalent-mode ((table semanticdb-table-global) &optional buffer) + "Return t, pretend that this table's mode is equivalent to BUFFER. +Equivalent modes are specified by by `semantic-equivalent-major-modes' +local variable." + ;; @todo - hack alert! + t) + +;;; Filename based methods +;; +(defmethod semanticdb-get-database-tables ((obj semanticdb-project-database-global)) + "For a global database, there are no explicit tables. +For each file hit, get the traditional semantic table from that file." + ;; We need to return something since there is always the "master table" + ;; The table can then answer file name type questions. + (when (not (slot-boundp obj 'tables)) + (let ((newtable (semanticdb-table-global "GNU Global Search Table"))) + (oset obj tables (list newtable)) + (oset newtable parent-db obj) + (oset newtable tags nil) + )) + + (call-next-method)) + +(defmethod semanticdb-file-table ((obj semanticdb-project-database-global) filename) + "From OBJ, return FILENAME's associated table object." + ;; We pass in "don't load". I wonder if we need to avoid that or not? + (car (semanticdb-get-database-tables obj)) + ) + +;;; Search Overrides +;; +;; Only NAME based searches work with GLOBAL as that is all it tracks. +;; +(defmethod semanticdb-find-tags-by-name-method + ((table semanticdb-table-global) name &optional tags) + "Find all tags named NAME in TABLE. +Return a list of tags." + (if tags + ;; If TAGS are passed in, then we don't need to do work here. + (call-next-method) + ;; Call out to GNU Global for some results. + (let* ((semantic-symref-tool 'global) + (result (semantic-symref-find-tags-by-name name 'project)) + ) + (when result + ;; We could ask to keep the buffer open, but that annoys + ;; people. + (semantic-symref-result-get-tags result)) + ))) + +(defmethod semanticdb-find-tags-by-name-regexp-method + ((table semanticdb-table-global) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Return a list of tags." + (if tags (call-next-method) + (let* ((semantic-symref-tool 'global) + (result (semantic-symref-find-tags-by-regexp regex 'project)) + ) + (when result + (semantic-symref-result-get-tags result)) + ))) + +(defmethod semanticdb-find-tags-for-completion-method + ((table semanticdb-table-global) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (if tags (call-next-method) + (let* ((semantic-symref-tool 'global) + (result (semantic-symref-find-tags-by-completion prefix 'project)) + (faketags nil) + ) + (when result + (dolist (T (oref result :hit-text)) + ;; We should look up each tag one at a time, but I'm lazy! + ;; Doing this may be good enough. + (setq faketags (cons + (semantic-tag T 'function :faux t) + faketags)) + ) + faketags)))) + +;;; Deep Searches +;; +;; If your language does not have a `deep' concept, these can be left +;; alone, otherwise replace with implementations similar to those +;; above. +;; +(defmethod semanticdb-deep-find-tags-by-name-method + ((table semanticdb-table-global) name &optional tags) + "Find all tags name NAME in TABLE. +Optional argument TAGS is a list of tags t +Like `semanticdb-find-tags-by-name-method' for global." + (semanticdb-find-tags-by-name-method table name tags)) + +(defmethod semanticdb-deep-find-tags-by-name-regexp-method + ((table semanticdb-table-global) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-by-name-method' for global." + (semanticdb-find-tags-by-name-regexp-method table regex tags)) + +(defmethod semanticdb-deep-find-tags-for-completion-method + ((table semanticdb-table-global) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-for-completion-method' for global." + (semanticdb-find-tags-for-completion-method table prefix tags)) + +(provide 'semantic/db-global) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/db-global" +;; End: + +;;; semantic/db-global.el ends here diff --git a/lisp/cedet/semantic/db-javascript.el b/lisp/cedet/semantic/db-javascript.el new file mode 100644 index 00000000000..e9d3794558d --- /dev/null +++ b/lisp/cedet/semantic/db-javascript.el @@ -0,0 +1,311 @@ +;;; semantic/db-javascript.el --- Semantic database extensions for javascript + +;;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 +;;; Free Software Foundation, Inc. + +;; Author: Joakim Verona + +;; 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: +;; +;; Semanticdb database for Javascript. +;; +;; This is an omniscient database with a hard-coded list of symbols for +;; Javascript. See the doc at the end of this file for adding or modifying +;; the list of tags. +;; + +(require 'semantic/db) +(require 'semantic/db-find) + +(eval-when-compile + ;; For generic function searching. + (require 'eieio) + (require 'eieio-opt)) + +;;; Code: +(defvar semanticdb-javascript-tags + '(("eval" function + (:arguments + (("x" variable nil nil nil))) + nil nil) + ("parseInt" function + (:arguments + (("string" variable nil nil nil) + ("radix" variable nil nil nil))) + nil nil) + ("parseFloat" function + (:arguments + (("string" variable nil nil nil))) + nil nil) + ("isNaN" function + (:arguments + (("number" variable nil nil nil))) + nil nil) + ("isFinite" function + (:arguments + (("number" variable nil nil nil))) + nil nil) + ("decodeURI" function + (:arguments + (("encodedURI" variable nil nil nil))) + nil nil) + ("decodeURIComponent" function + (:arguments + (("encodedURIComponent" variable nil nil nil))) + nil nil) + ("encodeURI" function + (:arguments + (("uri" variable nil nil nil))) + nil nil) + ("encodeURIComponent" function + (:arguments + (("uriComponent" variable nil nil nil))) + nil nil)) + "Hard-coded list of javascript tags for semanticdb. +See bottom of this file for instruction on managing this list.") + +;;; Classes: +(defclass semanticdb-table-javascript (semanticdb-search-results-table) + ((major-mode :initform javascript-mode) + ) + "A table for returning search results from javascript.") + +(defclass semanticdb-project-database-javascript + (semanticdb-project-database + eieio-singleton ;this db is for js globals, so singleton is apropriate + ) + ((new-table-class :initform semanticdb-table-javascript + :type class + :documentation + "New tables created for this database are of this class.") + ) + "Database representing javascript.") + +;; Create the database, and add it to searchable databases for javascript mode. +(defvar-mode-local javascript-mode semanticdb-project-system-databases + (list + (semanticdb-project-database-javascript "Javascript")) + "Search javascript for symbols.") + +;; NOTE: Be sure to modify this to the best advantage of your +;; language. +(defvar-mode-local javascript-mode semanticdb-find-default-throttle + '(project omniscience) + "Search project files, then search this omniscience database. +It is not necessary to to system or recursive searching because of +the omniscience database.") + +;;; Filename based methods +;; +(defmethod semanticdb-get-database-tables ((obj semanticdb-project-database-javascript)) + "For a javascript database, there are no explicit tables. +Create one of our special tables that can act as an intermediary." + ;; NOTE: This method overrides an accessor for the `tables' slot in + ;; a database. You can either construct your own (like tmp here + ;; or you can manage any number of tables. + + ;; We need to return something since there is always the "master table" + ;; The table can then answer file name type questions. + (when (not (slot-boundp obj 'tables)) + (let ((newtable (semanticdb-table-javascript "tmp"))) + (oset obj tables (list newtable)) + (oset newtable parent-db obj) + (oset newtable tags nil) + )) + (call-next-method) + ) + +(defmethod semanticdb-file-table ((obj semanticdb-project-database-javascript) filename) + "From OBJ, return FILENAME's associated table object." + ;; NOTE: See not for `semanticdb-get-database-tables'. + (car (semanticdb-get-database-tables obj)) + ) + +(defmethod semanticdb-get-tags ((table semanticdb-table-javascript )) + "Return the list of tags belonging to TABLE." + ;; NOTE: Omniscient databases probably don't want to keep large tabes + ;; lolly-gagging about. Keep internal Emacs tables empty and + ;; refer to alternate databases when you need something. + semanticdb-javascript-tags) + +(defmethod semanticdb-equivalent-mode ((table semanticdb-table-javascript) &optional buffer) + "Return non-nil if TABLE's mode is equivalent to BUFFER. +Equivalent modes are specified by by `semantic-equivalent-major-modes' +local variable." + (save-excursion + (set-buffer buffer) + (eq (or mode-local-active-mode major-mode) 'javascript-mode))) + +;;; Usage +;; +;; Unlike other tables, an omniscent database does not need to +;; be associated with a path. Use this routine to always add ourselves +;; to a search list. +(define-mode-local-override semanticdb-find-translate-path javascript-mode + (path brutish) + "Return a list of semanticdb tables asociated with PATH. +If brutish, do the default action. +If not brutish, do the default action, and append the system +database (if available.)" + (let ((default + ;; When we recurse, disable searching of system databases + ;; so that our Javascript database only shows up once when + ;; we append it in this iteration. + (let ((semanticdb-search-system-databases nil) + ) + (semanticdb-find-translate-path-default path brutish)))) + ;; Don't add anything if BRUTISH is on (it will be added in that fcn) + ;; or if we aren't supposed to search the system. + (if (or brutish (not semanticdb-search-system-databases)) + default + (let ((tables (apply #'append + (mapcar + (lambda (db) (semanticdb-get-database-tables db)) + semanticdb-project-system-databases)))) + (append default tables))))) + +;;; Search Overrides +;; +;; NOTE WHEN IMPLEMENTING: Be sure to add doc-string updates explaining +;; how your new search routines are implemented. +;; +(defun semanticdb-javascript-regexp-search (regexp) + "Search for REGEXP in our fixed list of javascript tags." + (let* ((tags semanticdb-javascript-tags) + (result nil)) + (while tags + (if (string-match regexp (caar tags)) + (setq result (cons (car tags) result))) + (setq tags (cdr tags))) + result)) + +(defmethod semanticdb-find-tags-by-name-method + ((table semanticdb-table-javascript) name &optional tags) + "Find all tags named NAME in TABLE. +Return a list of tags." + (if tags + ;; If TAGS are passed in, then we don't need to do work here. + (call-next-method) + (assoc-string name semanticdb-javascript-tags) + )) + +(defmethod semanticdb-find-tags-by-name-regexp-method + ((table semanticdb-table-javascript) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Return a list of tags." + (if tags (call-next-method) + ;; YOUR IMPLEMENTATION HERE + (semanticdb-javascript-regexp-search regex) + + )) + +(defmethod semanticdb-find-tags-for-completion-method + ((table semanticdb-table-javascript) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (if tags (call-next-method) + ;; YOUR IMPLEMENTATION HERE + (semanticdb-javascript-regexp-search (concat "^" prefix ".*")) + )) + +(defmethod semanticdb-find-tags-by-class-method + ((table semanticdb-table-javascript) class &optional tags) + "In TABLE, find all occurances of tags of CLASS. +Optional argument TAGS is a list of tags to search. +Returns a table of all matching tags." + (if tags (call-next-method) + ;; YOUR IMPLEMENTATION HERE + ;; + ;; Note: This search method could be considered optional in an + ;; omniscient database. It may be unwise to return all tags + ;; that exist for a language that are a variable or function. + ;; + ;; If it is optional, you can just delete this method. + nil)) + +;;; Deep Searches +;; +;; If your language does not have a `deep' concept, these can be left +;; alone, otherwise replace with implementations similar to those +;; above. +;; +(defmethod semanticdb-deep-find-tags-by-name-method + ((table semanticdb-table-javascript) name &optional tags) + "Find all tags name NAME in TABLE. +Optional argument TAGS is a list of tags t +Like `semanticdb-find-tags-by-name-method' for javascript." + (semanticdb-find-tags-by-name-method table name tags)) + +(defmethod semanticdb-deep-find-tags-by-name-regexp-method + ((table semanticdb-table-javascript) regex &optional tags) + "Find all tags with name matching REGEX in TABLE. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-by-name-method' for javascript." + (semanticdb-find-tags-by-name-regexp-method table regex tags)) + +(defmethod semanticdb-deep-find-tags-for-completion-method + ((table semanticdb-table-javascript) prefix &optional tags) + "In TABLE, find all occurances of tags matching PREFIX. +Optional argument TAGS is a list of tags to search. +Like `semanticdb-find-tags-for-completion-method' for javascript." + (semanticdb-find-tags-for-completion-method table prefix tags)) + +;;; Advanced Searches +;; +(defmethod semanticdb-find-tags-external-children-of-type-method + ((table semanticdb-table-javascript) type &optional tags) + "Find all nonterminals which are child elements of TYPE +Optional argument TAGS is a list of tags to search. +Return a list of tags." + (if tags (call-next-method) + ;; YOUR IMPLEMENTATION HERE + ;; + ;; OPTIONAL: This could be considered an optional function. It is + ;; used for `semantic-adopt-external-members' and may not + ;; be possible to do in your language. + ;; + ;; If it is optional, you can just delete this method. + )) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defun semanticdb-javascript-strip-tags (tags) + "Strip TAGS from overlays and reparse symbols." + (cond ((and (consp tags) (eq 'reparse-symbol (car tags))) + nil) + ((overlayp tags) nil) + ((atom tags) tags) + (t (cons (semanticdb-javascript-strip-tags + (car tags)) (semanticdb-javascript-strip-tags + (cdr tags)))))) + +;this list was made from a javascript file, and the above function +;; function eval(x){} +;; function parseInt(string,radix){} +;; function parseFloat(string){} +;; function isNaN(number){} +;; function isFinite(number){} +;; function decodeURI(encodedURI){} +;; function decodeURIComponent (encodedURIComponent){} +;; function encodeURI (uri){} +;; function encodeURIComponent (uriComponent){} + +(provide 'semantic/db-javascript) + +;;; semantic/db-javascript.el ends here diff --git a/lisp/cedet/semantic/db-mode.el b/lisp/cedet/semantic/db-mode.el new file mode 100644 index 00000000000..c526515f248 --- /dev/null +++ b/lisp/cedet/semantic/db-mode.el @@ -0,0 +1,221 @@ +;;; semantic/db-mode.el --- Semanticdb Minor Mode + +;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Major mode for managing Semantic Databases automatically. + +;;; Code: + +(require 'semantic/db) + +(declare-function semantic-lex-spp-set-dynamic-table "semantic/lex-spp") + +;;; Start/Stop database use +;; +(defvar semanticdb-hooks + '((semanticdb-semantic-init-hook-fcn semantic-init-db-hook) + (semanticdb-synchronize-table semantic-after-toplevel-cache-change-hook) + (semanticdb-partial-synchronize-table semantic-after-partial-cache-change-hook) + (semanticdb-revert-hook before-revert-hook) + (semanticdb-kill-hook kill-buffer-hook) + (semanticdb-kill-hook change-major-mode-hook) ;; Not really a kill, but we need the same effect. + (semanticdb-kill-emacs-hook kill-emacs-hook) + (semanticdb-save-all-db-idle auto-save-hook) + ) + "List of hooks and values to add/remove when configuring semanticdb.") + +;;; SEMANTICDB-MODE +;; +;;;###autoload +(defun semanticdb-minor-mode-p () + "Return non-nil if `semanticdb-minor-mode' is active." + (member (car (car semanticdb-hooks)) + (symbol-value (car (cdr (car semanticdb-hooks)))))) + +;;;###autoload +(define-minor-mode global-semanticdb-minor-mode + "Toggle Semantic DB mode. +With ARG, turn Semantic DB mode on if ARG is positive, off otherwise. + +In Semantic DB mode, Semantic parsers store results in a +database, which can be saved for future Emacs sessions." + :global t + :group 'semantic + (if global-semanticdb-minor-mode + ;; Enable + (dolist (elt semanticdb-hooks) + (add-hook (cadr elt) (car elt))) + ;; Disable + (dolist (elt semanticdb-hooks) + (add-hook (cadr elt) (car elt))))) + +(defvaralias 'semanticdb-mode-hook 'global-semanticdb-minor-mode-hook) +(defvaralias 'semanticdb-global-mode 'global-semanticdb-minor-mode) +(semantic-varalias-obsolete 'semanticdb-mode-hooks + 'global-semanticdb-minor-mode-hook) + + +(defun semanticdb-toggle-global-mode () + "Toggle use of the Semantic Database feature. +Update the environment of Semantic enabled buffers accordingly." + (interactive) + (if (semanticdb-minor-mode-p) + ;; Save databases before disabling semanticdb. + (semanticdb-save-all-db)) + ;; Toggle semanticdb minor mode. + (global-semanticdb-minor-mode)) + +;;; Hook Functions: +;; +;; Functions used in hooks to keep SemanticDB operating. +;; +(defun semanticdb-semantic-init-hook-fcn () + "Function saved in `semantic-init-db-hook'. +Sets up the semanticdb environment." + ;; Only initialize semanticdb if we have a file name. + ;; There is no reason to cache a tag table if there is no + ;; way to load it back in later. + (when (buffer-file-name) + (let* ((ans (semanticdb-create-table-for-file (buffer-file-name))) + (cdb (car ans)) + (ctbl (cdr ans)) + ) + ;; Get the current DB for this directory + (setq semanticdb-current-database cdb) + ;; We set the major mode because we know what it is. + (oset ctbl major-mode major-mode) + ;; Local state + (setq semanticdb-current-table ctbl) + ;; Try to swap in saved tags + (if (or (not (slot-boundp ctbl 'tags)) (not (oref ctbl tags)) + (/= (or (oref ctbl pointmax) 0) (point-max)) + ) + (semantic-clear-toplevel-cache) + ;; Unmatched syntax + (condition-case nil + (semantic-set-unmatched-syntax-cache + (oref ctbl unmatched-syntax)) + (unbound-slot + ;; Old version of the semanticdb table can miss the unmatched + ;; syntax slot. If so, just clear the unmatched syntax cache. + (semantic-clear-unmatched-syntax-cache) + ;; Make sure it has a value. + (oset ctbl unmatched-syntax nil) + )) + ;; Keep lexical tables up to date. Don't load + ;; semantic-spp if it isn't needed. + (let ((lt (oref ctbl lexical-table))) + (when lt + (require 'semantic/lex-spp) + (semantic-lex-spp-set-dynamic-table lt))) + ;; Set the main tag cache. + ;; This must happen after setting up buffer local variables + ;; since this will turn around and re-save those variables. + (semantic--set-buffer-cache (oref ctbl tags)) + ;; Don't need it to be dirty. Set dirty due to hooks from above. + (oset ctbl dirty nil) ;; Special case here. + (oset ctbl buffer (current-buffer)) + ;; Bind into the buffer. + (semantic--tag-link-cache-to-buffer) + ) + ))) + +(defun semanticdb-revert-hook () + "Hook run before a revert buffer. +We can't track incremental changes due to a revert, so just clear the cache. +This will prevent the next batch of hooks from wasting time parsing things +that don't need to be parsed." + (if (and (semantic-active-p) + semantic--buffer-cache + semanticdb-current-table) + (semantic-clear-toplevel-cache))) + +(defun semanticdb-kill-hook () + "Function run when a buffer is killed. +If there is a semantic cache, slurp out the overlays, and store +it in our database. If that buffer has no cache, ignore it, we'll +handle it later if need be." + (when (and (semantic-active-p) + semantic--buffer-cache + semanticdb-current-table) + + ;; Try to get a fast update. + (semantic-fetch-tags-fast) + + ;; If the buffer is in a bad state, don't save anything... + (if (semantic-parse-tree-needs-rebuild-p) + ;; If this is the case, don't save anything. + (progn + (semantic-clear-toplevel-cache) + (oset semanticdb-current-table pointmax 0) + (oset semanticdb-current-table fsize 0) + (oset semanticdb-current-table lastmodtime nil) + ) + ;; We have a clean buffer, save it off. + (condition-case nil + (progn + (semantic--tag-unlink-cache-from-buffer) + ;; Set pointmax only if we had some success in the unlink. + (oset semanticdb-current-table pointmax (point-max)) + (let ((fattr (file-attributes + (semanticdb-full-filename + semanticdb-current-table)))) + (oset semanticdb-current-table fsize (nth 7 fattr)) + (oset semanticdb-current-table lastmodtime (nth 5 fattr)) + (oset semanticdb-current-table buffer nil) + )) + ;; If this messes up, just clear the system + (error + (semantic-clear-toplevel-cache) + (message "semanticdb: Failed to deoverlay tag cache."))) + ) + )) + +(defun semanticdb-kill-emacs-hook () + "Function called when Emacs is killed. +Save all the databases." + (semanticdb-save-all-db)) + +;;; SYNCHRONIZATION HOOKS +;; +(defun semanticdb-synchronize-table (new-table) + "Function run after parsing. +Argument NEW-TABLE is the new table of tags." + (when semanticdb-current-table + (semanticdb-synchronize semanticdb-current-table new-table))) + +(defun semanticdb-partial-synchronize-table (new-table) + "Function run after parsing. +Argument NEW-TABLE is the new table of tags." + (when semanticdb-current-table + (semanticdb-partial-synchronize semanticdb-current-table new-table))) + + +(provide 'semantic/db-mode) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/db-mode" +;; End: + +;;; semantic/db-mode.el ends here diff --git a/lisp/cedet/semantic/db-ref.el b/lisp/cedet/semantic/db-ref.el new file mode 100644 index 00000000000..2c8dd7729c4 --- /dev/null +++ b/lisp/cedet/semantic/db-ref.el @@ -0,0 +1,173 @@ +;;; semantic/db-ref.el --- Handle cross-db file references + +;;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Handle cross-database file references. +;; +;; Any given database may be referred to by some other database. For +;; example, if a .cpp file has a #include in a header, then that +;; header file should have a reference to the .cpp file that included +;; it. +;; +;; This is critical for purposes where a file (such as a .cpp file) +;; needs to have its caches flushed because of changes in the +;; header. Changing a header may cause a referring file to be +;; reparsed due to account for changes in defined macros, or perhaps +;; a change to files the header includes. + + +;;; Code: +(require 'eieio) +(require 'semantic) +(require 'semantic/db) +(require 'semantic/tag) + +;; For the semantic-find-tags-by-name-regexp macro. +(eval-when-compile (require 'semantic/find)) + +(defmethod semanticdb-add-reference ((dbt semanticdb-abstract-table) + include-tag) + "Add a reference for the database table DBT based on INCLUDE-TAG. +DBT is the database table that owns the INCLUDE-TAG. The reference +will be added to the database that INCLUDE-TAG refers to." + ;; NOTE: I should add a check to make sure include-tag is in DB. + ;; but I'm too lazy. + (let* ((semanticdb-find-default-throttle + (if (featurep 'semantic/db-find) + (remq 'unloaded semanticdb-find-default-throttle) + nil)) + (refdbt (semanticdb-find-table-for-include include-tag dbt)) + ;;(fullfile (semanticdb-full-filename dbt)) + ) + (when refdbt + ;; Add our filename (full path) + ;; (object-add-to-list refdbt 'file-refs fullfile) + + ;; Add our database. + (object-add-to-list refdbt 'db-refs dbt) + t))) + +(defmethod semanticdb-check-references ((dbt semanticdb-abstract-table)) + "Check and cleanup references in the database DBT. +Abstract tables would be difficult to reference." + ;; Not sure how an abstract table can have references. + nil) + +(defmethod semanticdb-includes-in-table ((dbt semanticdb-abstract-table)) + "Return a list of direct includes in table DBT." + (semantic-find-tags-by-class 'include (semanticdb-get-tags dbt))) + + +(defmethod semanticdb-check-references ((dbt semanticdb-table)) + "Check and cleanup references in the database DBT. +Any reference to a file that cannot be found, or whos file no longer +refers to DBT will be removed." + (let ((refs (oref dbt db-refs)) + (myexpr (concat "\\<" (oref dbt file))) + ) + (while refs + (let* ((ok t) + (db (car refs)) + (f (when (semanticdb-table-child-p db) + (semanticdb-full-filename db))) + ) + + ;; The file was deleted + (when (and f (not (file-exists-p f))) + (setq ok nil)) + + ;; The reference no longer includes the textual reference? + (let* ((refs (semanticdb-includes-in-table db)) + (inc (semantic-find-tags-by-name-regexp + myexpr refs))) + (when (not inc) + (setq ok nil))) + + ;; Remove not-ok databases from the list. + (when (not ok) + (object-remove-from-list dbt 'db-refs db) + )) + (setq refs (cdr refs))))) + +(defmethod semanticdb-refresh-references ((dbt semanticdb-abstract-table)) + "Refresh references to DBT in other files." + ;; alternate tables can't be edited, so can't be changed. + nil + ) + +(defmethod semanticdb-refresh-references ((dbt semanticdb-table)) + "Refresh references to DBT in other files." + (let ((refs (semanticdb-includes-in-table dbt)) + ) + (while refs + (if (semanticdb-add-reference dbt (car refs)) + nil + ;; If we succeeded, then do... nothing? + nil + ) + (setq refs (cdr refs))) + )) + +(defmethod semanticdb-notify-references ((dbt semanticdb-table) + method) + "Notify all references of the table DBT using method. +METHOD takes two arguments. + (METHOD TABLE-TO-NOTIFY DBT) +TABLE-TO-NOTIFY is a semanticdb-table which is being notified. +DBT, the second argument is DBT." + (mapc (lambda (R) (funcall method R dbt)) + (oref dbt db-refs))) + +;;; DEBUG +;; +(defclass semanticdb-ref-adebug () + ((i-depend-on :initarg :i-depend-on) + (local-table :initarg :local-table) + (i-include :initarg :i-include)) + "Simple class to allow ADEBUG to show a nice list.") + +(declare-function data-debug-new-buffer "data-debug") +(declare-function data-debug-insert-object-slots "eieio-datadebug") + +(defun semanticdb-ref-test (refresh) + "Dump out the list of references for the current buffer. +If REFRESH is non-nil, cause the current table to have its references +refreshed before dumping the result." + (interactive "p") + (require 'eieio-datadebug) + ;; If we need to refresh... then do so. + (when refresh + (semanticdb-refresh-references semanticdb-current-table)) + ;; Do the debug system + (let* ((tab semanticdb-current-table) + (myrefs (oref tab db-refs)) + (myinc (semanticdb-includes-in-table tab)) + (adbc (semanticdb-ref-adebug "DEBUG" + :i-depend-on myrefs + :local-table tab + :i-include myinc))) + (data-debug-new-buffer "*References ADEBUG*") + (data-debug-insert-object-slots adbc "!")) + ) + +(provide 'semantic/db-ref) +;;; semantic/db-ref.el ends here diff --git a/lisp/cedet/semantic/db-typecache.el b/lisp/cedet/semantic/db-typecache.el new file mode 100644 index 00000000000..02d77327c69 --- /dev/null +++ b/lisp/cedet/semantic/db-typecache.el @@ -0,0 +1,606 @@ +;;; db-typecache.el --- Manage Datatypes + +;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Manage a datatype cache. +;; +;; For typed languages like C++ collect all known types from various +;; headers, merge namespaces, and expunge duplicates. +;; +;; It is likely this feature will only be needed for C/C++. + +(require 'semantic) +(require 'semantic/db) +(require 'semantic/db-find) +(require 'semantic/analyze/fcn) + +;; For semantic-find-tags-by-* macros +(eval-when-compile (require 'semantic/find)) + +(declare-function data-debug-insert-thing "data-debug") +(declare-function data-debug-new-buffer "data-debug") +(declare-function semantic-sort-tags-by-name-then-type-increasing "semantic/sort") +(declare-function semantic-scope-tag-clone-with-scope "semantic/scope") + +;;; Code: + + +;;; TABLE TYPECACHE +;;;###autoload +(defclass semanticdb-typecache () + ((filestream :initform nil + :documentation + "Fully sorted/merged list of tags within this buffer.") + (includestream :initform nil + :documentation + "Fully sorted/merged list of tags from this file's includes list.") + (stream :initform nil + :documentation + "The searchable tag stream for this cache. +NOTE: Can I get rid of this? Use a hashtable instead?") + (dependants :initform nil + :documentation + "Any other object that is dependent on typecache results. +Said object must support `semantic-reset' methods.") + ;; @todo - add some sort of fast-hash. + ;; @note - Rebuilds in large projects already take a while, and the + ;; actual searches are pretty fast. Really needed? + ) + "Structure for maintaining a typecache.") + +(defmethod semantic-reset ((tc semanticdb-typecache)) + "Reset the object IDX." + (oset tc filestream nil) + (oset tc includestream nil) + + (oset tc stream nil) + + (mapc 'semantic-reset (oref tc dependants)) + (oset tc dependants nil) + ) + +(defmethod semanticdb-typecache-notify-reset ((tc semanticdb-typecache)) + "Do a reset from a notify from a table we depend on." + (oset tc includestream nil) + (mapc 'semantic-reset (oref tc dependants)) + (oset tc dependants nil) + ) + +(defmethod semanticdb-partial-synchronize ((tc semanticdb-typecache) + new-tags) + "Reset the typecache based on a partial reparse." + (when (semantic-find-tags-by-class 'include new-tags) + (oset tc includestream nil) + (mapc 'semantic-reset (oref tc dependants)) + (oset tc dependants nil) + ) + + (when (semantic-find-tags-by-class 'type new-tags) + ;; Reset our index + (oset tc filestream nil) + t ;; Return true, our core file tags have changed in a relavant way. + ) + + ;; NO CODE HERE + ) + +(defun semanticdb-typecache-add-dependant (dep) + "Add into the local typecache a dependant DEP." + (let* ((table semanticdb-current-table) + ;;(idx (semanticdb-get-table-index table)) + (cache (semanticdb-get-typecache table)) + ) + (object-add-to-list cache 'dependants dep))) + +(defun semanticdb-typecache-length(thing) + "How long is THING? +Debugging function." + (cond ((semanticdb-typecache-child-p thing) + (length (oref thing stream))) + ((semantic-tag-p thing) + (length (semantic-tag-type-members thing))) + ((and (listp thing) (semantic-tag-p (car thing))) + (length thing)) + ((null thing) + 0) + (t -1) )) + + +(defmethod semanticdb-get-typecache ((table semanticdb-abstract-table)) + "Retrieve the typecache from the semanticdb TABLE. +If there is no table, create one, and fill it in." + (semanticdb-refresh-table table) + (let* ((idx (semanticdb-get-table-index table)) + (cache (oref idx type-cache)) + ) + + ;; Make sure we have a cache object in the DB index. + (when (not cache) + ;; The object won't change as we fill it with stuff. + (setq cache (semanticdb-typecache (semanticdb-full-filename table))) + (oset idx type-cache cache)) + + cache)) + +(defmethod semanticdb-have-typecache-p ((table semanticdb-abstract-table)) + "Return non-nil (the typecache) if TABLE has a pre-calculated typecache." + (let* ((idx (semanticdb-get-table-index table))) + (oref idx type-cache))) + + +;;; DATABASE TYPECACHE +;; +;; A full database can cache the types across its files. +;; +;; Unlike file based caches, this one is a bit simpler, and just needs +;; to get reset when a table gets updated. + +;;;###autoload +(defclass semanticdb-database-typecache (semanticdb-abstract-db-cache) + ((stream :initform nil + :documentation + "The searchable tag stream for this cache.") + ) + "Structure for maintaining a typecache.") + +(defmethod semantic-reset ((tc semanticdb-database-typecache)) + "Reset the object IDX." + (oset tc stream nil) + ) + +(defmethod semanticdb-synchronize ((cache semanticdb-database-typecache) + new-tags) + "Synchronize a CACHE with some NEW-TAGS." + ) + +(defmethod semanticdb-partial-synchronize ((cache semanticdb-database-typecache) + new-tags) + "Synchronize a CACHE with some changed NEW-TAGS." + ) + +(defmethod semanticdb-get-typecache ((db semanticdb-project-database)) + "Retrieve the typecache from the semantic database DB. +If there is no table, create one, and fill it in." + (semanticdb-cache-get db semanticdb-database-typecache) + ) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; MERGING +;; +;; Managing long streams of tags representing data types. +;; +(defun semanticdb-typecache-apply-filename (file stream) + "Apply the filename FILE to all tags in STREAM." + (let ((new nil)) + (while stream + (setq new (cons (semantic-tag-copy (car stream) nil file) + new)) + ;The below is handled by the tag-copy fcn. + ;(semantic--tag-put-property (car new) :filename file) + (setq stream (cdr stream))) + (nreverse new))) + + +(defsubst semanticdb-typecache-safe-tag-members (tag) + "Return a list of members for TAG that are safe to permute." + (let ((mem (semantic-tag-type-members tag)) + (fname (semantic-tag-file-name tag))) + (if fname + (setq mem (semanticdb-typecache-apply-filename fname mem)) + (copy-sequence mem)))) + +(defsubst semanticdb-typecache-safe-tag-list (tags table) + "Make the tag list TAGS found in TABLE safe for the typecache. +Adds a filename and copies the tags." + (semanticdb-typecache-apply-filename + (semanticdb-full-filename table) + tags)) + +(defun semanticdb-typecache-merge-streams (cache1 cache2) + "Merge into CACHE1 and CACHE2 together. The Caches will be merged in place." + (if (or (and (not cache1) (not cache2)) + (and (not (cdr cache1)) (not cache2)) + (and (not cache1) (not (cdr cache2)))) + ;; If all caches are empty OR + ;; cache1 is length 1 and no cache2 OR + ;; no cache1 and length 1 cache2 + ;; + ;; then just return the cache, and skip all this merging stuff. + (or cache1 cache2) + + ;; Assume we always have datatypes, as this typecache isn't really + ;; useful without a typed language. + (require 'semantic/sort) + (let ((S (semantic-sort-tags-by-name-then-type-increasing + ;; I used to use append, but it copied cache1 but not cache2. + ;; Since sort was permuting cache2, I already had to make sure + ;; the caches were permute-safe. Might as well use nconc here. + (nconc cache1 cache2))) + (ans nil) + (next nil) + (prev nil) + (type nil)) + ;; With all the tags in order, we can loop over them, and when + ;; two have the same name, we can either throw one away, or construct + ;; a fresh new tag merging the items together. + (while S + (setq prev (car ans)) + (setq next (car S)) + (if (or + ;; CASE 1 - First item + (null prev) + ;; CASE 2 - New name + (not (string= (semantic-tag-name next) + (semantic-tag-name prev)))) + (setq ans (cons next ans)) + ;; ELSE - We have a NAME match. + (setq type (semantic-tag-type next)) + (if (semantic-tag-of-type-p prev type) ; Are they the same datatype + ;; Same Class, we can do a merge. + (cond + ((and (semantic-tag-of-class-p next 'type) + (string= type "namespace")) + ;; Namespaces - merge the children together. + (setcar ans + (semantic-tag-new-type + (semantic-tag-name prev) ; - they are the same + "namespace" ; - we know this as fact + (semanticdb-typecache-merge-streams + (semanticdb-typecache-safe-tag-members prev) + (semanticdb-typecache-safe-tag-members next)) + nil ; - no attributes + )) + ;; Make sure we mark this as a fake tag. + (semantic-tag-set-faux (car ans)) + ) + ((semantic-tag-prototype-p next) + ;; NEXT is a prototype... so keep previous. + nil ; - keep prev, do nothing + ) + ((semantic-tag-prototype-p prev) + ;; PREV is a prototype, but not next.. so keep NEXT. + ;; setcar - set by side-effect on top of prev + (setcar ans next) + ) + (t + ;;(message "Don't know how to merge %s. Keeping first entry." (semantic-tag-name next)) + )) + ;; Not same class... but same name + ;(message "Same name, different type: %s, %s!=%s" + ; (semantic-tag-name next) + ; (semantic-tag-type next) + ; (semantic-tag-type prev)) + (setq ans (cons next ans)) + )) + (setq S (cdr S))) + (nreverse ans)))) + +;;; Refresh / Query API +;; +;; Queries that can be made for the typecache. +(defmethod semanticdb-typecache-file-tags ((table semanticdb-abstract-table)) + "No tags available from non-file based tables." + nil) + +(defmethod semanticdb-typecache-file-tags ((table semanticdb-table)) + "Update the typecache for TABLE, and return the file-tags. +File-tags are those that belong to this file only, and excludes +all included files." + (let* (;(idx (semanticdb-get-table-index table)) + (cache (semanticdb-get-typecache table)) + ) + + ;; Make sure our file-tags list is up to date. + (when (not (oref cache filestream)) + (let ((tags (semantic-find-tags-by-class 'type table))) + (when tags + (setq tags (semanticdb-typecache-safe-tag-list tags table)) + (oset cache filestream (semanticdb-typecache-merge-streams tags nil))))) + + ;; Return our cache. + (oref cache filestream) + )) + +(defmethod semanticdb-typecache-include-tags ((table semanticdb-abstract-table)) + "No tags available from non-file based tables." + nil) + +(defmethod semanticdb-typecache-include-tags ((table semanticdb-table)) + "Update the typecache for TABLE, and return the merged types from the include tags. +Include-tags are the tags brought in via includes, all merged together into +a master list." + (let* ((cache (semanticdb-get-typecache table)) + ) + + ;; Make sure our file-tags list is up to date. + (when (not (oref cache includestream)) + (let (;; Calc the path first. This will have a nice side -effect of + ;; getting the cache refreshed if a refresh is needed. Most of the + ;; time this value is itself cached, so the query is fast. + (incpath (semanticdb-find-translate-path table nil)) + (incstream nil)) + ;; Get the translated path, and extract all the type tags, then merge + ;; them all together. + (dolist (i incpath) + ;; don't include ourselves in this crazy list. + (when (and i (not (eq i table)) + ;; @todo - This eieio fcn can be slow! Do I need it? + ;; (semanticdb-table-child-p i) + ) + (setq incstream + (semanticdb-typecache-merge-streams + incstream + ;; Getting the cache from this table will also cause this + ;; file to update it's cache from it's decendants. + ;; + ;; In theory, caches are only built for most includes + ;; only once (in the loop before this one), so this ends + ;; up being super fast as we edit our file. + (copy-sequence + (semanticdb-typecache-file-tags i)))) + )) + + ;; Save... + (oset cache includestream incstream))) + + ;; Return our cache. + (oref cache includestream) + )) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Search Routines +;;;###autoload +(define-overloadable-function semanticdb-typecache-find (type &optional path find-file-match) + "Search the typecache for TYPE in PATH. +If type is a string, split the string, and search for the parts. +If type is a list, treat the type as a pre-split string. +PATH can be nil for the current buffer, or a semanticdb table. +FIND-FILE-MATCH is non-nil to force all found tags to be loaded into a buffer.") + +(defun semanticdb-typecache-find-default (type &optional path find-file-match) + "Default implementation of `semanticdb-typecache-find'. +TYPE is the datatype to find. +PATH is the search path.. which should be one table object. +If FIND-FILE-MATCH is non-nil, then force the file belonging to the +found tag to be loaded." + (semanticdb-typecache-find-method (or path semanticdb-current-table) + type find-file-match)) + +(defun semanticdb-typecache-find-by-name-helper (name table) + "Find the tag with NAME in TABLE, which is from a typecache. +If more than one tag has NAME in TABLE, we will prefer the tag that +is of class 'type." + (let* ((names (semantic-find-tags-by-name name table)) + (types (semantic-find-tags-by-class 'type names))) + (or (car-safe types) (car-safe names)))) + +(defmethod semanticdb-typecache-find-method ((table semanticdb-abstract-table) + type find-file-match) + "Search the typecache in TABLE for the datatype TYPE. +If type is a string, split the string, and search for the parts. +If type is a list, treat the type as a pre-split string. +If FIND-FILE-MATCH is non-nil, then force the file belonging to the +found tag to be loaded." + ;; convert string to a list. + (when (stringp type) (setq type (semantic-analyze-split-name type))) + (when (stringp type) (setq type (list type))) + + ;; Search for the list in our typecache. + (let* ((file (semanticdb-typecache-file-tags table)) + (inc (semanticdb-typecache-include-tags table)) + (stream nil) + (f-ans nil) + (i-ans nil) + (ans nil) + (notdone t) + (lastfile nil) + (thisfile nil) + (lastans nil) + (calculated-scope nil) + ) + ;; 1) Find first symbol in the two master lists and then merge + ;; the found streams. + + ;; We stripped duplicates, so these will be super-fast! + (setq f-ans (semantic-find-first-tag-by-name (car type) file)) + (setq i-ans (semantic-find-first-tag-by-name (car type) inc)) + (if (and f-ans i-ans) + (progn + ;; This trick merges the two identified tags, making sure our lists are + ;; complete. The second find then gets the new 'master' from the list of 2. + (setq ans (semanticdb-typecache-merge-streams (list f-ans) (list i-ans))) + (setq ans (semantic-find-first-tag-by-name (car type) ans)) + ) + + ;; The answers are already sorted and merged, so if one misses, + ;; no need to do any special work. + (setq ans (or f-ans i-ans))) + + ;; 2) Loop over the remaining parts. + (while (and type notdone) + + ;; For pass > 1, stream will be non-nil, so do a search, otherwise + ;; ans is from outside the loop. + (when stream + (setq ans (semanticdb-typecache-find-by-name-helper (car type) stream)) + + ;; NOTE: The below test to make sure we get a type is only relevant + ;; for the SECOND pass or later. The first pass can only ever + ;; find a type/namespace because everything else is excluded. + + ;; If this is not the last entry from the list, then it + ;; must be a type or a namespace. Lets double check. + (when (cdr type) + + ;; From above, there is only one tag in ans, and we prefer + ;; types. + (when (not (semantic-tag-of-class-p ans 'type)) + + (setq ans nil))) + ) + + (push ans calculated-scope) + + ;; Track most recent file. + (setq thisfile (semantic-tag-file-name ans)) + (when (and thisfile (stringp thisfile)) + (setq lastfile thisfile)) + + ;; If we have a miss, exit, otherwise, update the stream to + ;; the next set of members. + (if (not ans) + (setq notdone nil) + (setq stream (semantic-tag-type-members ans))) + + (setq lastans ans + ans nil + type (cdr type))) + + (if (or type (not notdone)) + ;; If there is stuff left over, then we failed. Just return + ;; nothing. + nil + + ;; We finished, so return everything. + + (if (and find-file-match lastfile) + ;; This won't liven up the tag since we have a copy, but + ;; we ought to be able to get there and go to the right line. + (find-file-noselect lastfile) + ;; We don't want to find-file match, so instead lets + ;; push the filename onto the return tag. + (when lastans + (setq lastans (semantic-tag-copy lastans nil lastfile)) + ;; We used to do the below, but we would erroneously be putting + ;; attributes on tags being shred with other lists. + ;;(semantic--tag-put-property lastans :filename lastfile) + ) + ) + + (if (and lastans calculated-scope) + + ;; Put our discovered scope into the tag if we have a tag + (progn + (require 'semantic/scope) + (semantic-scope-tag-clone-with-scope + lastans (reverse (cdr calculated-scope)))) + + ;; Else, just return + lastans + )))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; BRUTISH Typecache +;; +;; Routines for a typecache that crosses all tables in a given database +;; for a matching major-mode. +(defmethod semanticdb-typecache-for-database ((db semanticdb-project-database) + &optional mode) + "Return the typecache for the project database DB. +If there isn't one, create it. +" + (let ((lmode (or mode major-mode)) + (cache (semanticdb-get-typecache db)) + (stream nil) + ) + (dolist (table (semanticdb-get-database-tables db)) + (when (eq lmode (oref table :major-mode)) + (setq stream + (semanticdb-typecache-merge-streams + stream + (copy-sequence + (semanticdb-typecache-file-tags table)))) + )) + (oset cache stream stream) + cache)) + +(defun semanticdb-typecache-refresh-for-buffer (buffer) + "Refresh the typecache for BUFFER." + (save-excursion + (set-buffer buffer) + (let* ((tab semanticdb-current-table) + ;(idx (semanticdb-get-table-index tab)) + (tc (semanticdb-get-typecache tab))) + (semanticdb-typecache-file-tags tab) + (semanticdb-typecache-include-tags tab) + tc))) + + +;;; DEBUG +;; +(defun semanticdb-typecache-complete-flush () + "Flush all typecaches referenced by the current buffer." + (interactive) + (let* ((path (semanticdb-find-translate-path nil nil))) + (dolist (P path) + (oset P pointmax nil) + (semantic-reset (semanticdb-get-typecache P))))) + +(defun semanticdb-typecache-dump () + "Dump the typecache for the current buffer." + (interactive) + (require 'data-debug) + (let* ((start (current-time)) + (tc (semanticdb-typecache-refresh-for-buffer (current-buffer))) + (end (current-time)) + ) + (data-debug-new-buffer "*TypeCache ADEBUG*") + (message "Calculating Cache took %.2f seconds." + (semantic-elapsed-time start end)) + + (data-debug-insert-thing tc "]" "") + + )) + +(defun semanticdb-db-typecache-dump () + "Dump the typecache for the current buffer's database." + (interactive) + (require 'data-debug) + (let* ((tab semanticdb-current-table) + (idx (semanticdb-get-table-index tab)) + (junk (oset idx type-cache nil)) ;; flush! + (start (current-time)) + (tc (semanticdb-typecache-for-database (oref tab parent-db))) + (end (current-time)) + ) + (data-debug-new-buffer "*TypeCache ADEBUG*") + (message "Calculating Cache took %.2f seconds." + (semantic-elapsed-time start end)) + + (data-debug-insert-thing tc "]" "") + + )) + +(provide 'semantic/db-typecache) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/db-typecache" +;; End: + +;;; semanticdb-typecache.el ends here diff --git a/lisp/cedet/semantic/db.el b/lisp/cedet/semantic/db.el new file mode 100644 index 00000000000..bc25d31f19e --- /dev/null +++ b/lisp/cedet/semantic/db.el @@ -0,0 +1,1026 @@ +;;; semantic/db.el --- Semantic tag database manager + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, +;;; 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: tags + +;; 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: +;; +;; Maintain a database of tags for a group of files and enable +;; queries into the database. +;; +;; By default, assume one database per directory. +;; + +;;; Code: + +(require 'eieio-base) +(require 'semantic) + +(declare-function semantic-lex-spp-save-table "semantic/lex-spp") + +;;; Variables: +(defgroup semanticdb nil + "Parser Generator Persistent Database interface." + :group 'semantic) + +(defvar semanticdb-database-list nil + "List of all active databases.") + +(defvar semanticdb-new-database-class 'semanticdb-project-database-file + "The default type of database created for new files. +This can be changed on a per file basis, so that some directories +are saved using one mechanism, and some directories via a different +mechanism.") +(make-variable-buffer-local 'semanticdb-new-database-class) + +(defvar semanticdb-default-find-index-class 'semanticdb-find-search-index + "The default type of search index to use for a `semanticdb-table's. +This can be changed to try out new types of search indicies.") +(make-variable-buffer-local 'semanticdb-default-find=index-class) + +;;;###autoload +(defvar semanticdb-current-database nil + "For a given buffer, this is the currently active database.") +(make-variable-buffer-local 'semanticdb-current-database) + +;;;###autoload +(defvar semanticdb-current-table nil + "For a given buffer, this is the currently active database table.") +(make-variable-buffer-local 'semanticdb-current-table) + +;;; ABSTRACT CLASSES +;; +(defclass semanticdb-abstract-table () + ((parent-db ;; :initarg :parent-db + ;; Do not set an initarg, or you get circular writes to disk. + :documentation "Database Object containing this table.") + (major-mode :initarg :major-mode + :initform nil + :documentation "Major mode this table belongs to. +Sometimes it is important for a program to know if a given table has the +same major mode as the current buffer.") + (tags :initarg :tags + :accessor semanticdb-get-tags + :printer semantic-tag-write-list-slot-value + :documentation "The tags belonging to this table.") + (index :type semanticdb-abstract-search-index + :documentation "The search index. +Used by semanticdb-find to store additional information about +this table for searching purposes. + +Note: This index will not be saved in a persistent file.") + (cache :type list + :initform nil + :documentation "List of cache information for tools. +Any particular tool can cache data to a database at runtime +with `semanticdb-cache-get'. + +Using a semanticdb cache does not save any information to a file, +so your cache will need to be recalculated at runtime. Caches can be +referenced even when the file is not in a buffer. + +Note: This index will not be saved in a persistent file.") + ) + "A simple table for semantic tags. +This table is the root of tables, and contains the minimum needed +for a new table not associated with a buffer." + :abstract t) + +(defmethod semanticdb-in-buffer-p ((obj semanticdb-abstract-table)) + "Return a nil, meaning abstract table OBJ is not in a buffer." + nil) + +(defmethod semanticdb-get-buffer ((obj semanticdb-abstract-table)) + "Return a buffer associated with OBJ. +If the buffer is not in memory, load it with `find-file-noselect'." + nil) + +(defmethod semanticdb-full-filename ((obj semanticdb-abstract-table)) + "Fetch the full filename that OBJ refers to. +Abstract tables do not have file names associated with them." + nil) + +(defmethod semanticdb-dirty-p ((obj semanticdb-abstract-table)) + "Return non-nil if OBJ is 'dirty'." + nil) + +(defmethod semanticdb-set-dirty ((obj semanticdb-abstract-table)) + "Mark the abstract table OBJ dirty. +Abstract tables can not be marked dirty, as there is nothing +for them to synchronize against." + ;; The abstract table can not be dirty. + nil) + +(defmethod semanticdb-normalize-tags ((obj semanticdb-abstract-table) tags) + "For the table OBJ, convert a list of TAGS, into standardized form. +The default is to return TAGS. +Some databases may default to searching and providing simplified tags +based on whichever technique used. This method provides a hook for +them to convert TAG into a more complete form." + tags) + +(defmethod semanticdb-normalize-one-tag ((obj semanticdb-abstract-table) tag) + "For the table OBJ, convert a TAG, into standardized form. +This method returns a list of the form (DATABASE . NEWTAG). + +The default is to just return (OBJ TAG). + +Some databases may default to searching and providing simplified tags +based on whichever technique used. This method provides a hook for +them to convert TAG into a more complete form." + (cons obj tag)) + +(defmethod object-print ((obj semanticdb-abstract-table) &rest strings) + "Pretty printer extension for `semanticdb-table'. +Adds the number of tags in this file to the object print name." + (apply 'call-next-method obj + (cons (format " (%d tags)" + (length (semanticdb-get-tags obj)) + ) + strings))) + +;;; Index Cache +;; +(defclass semanticdb-abstract-search-index () + ((table :initarg :table + :type semanticdb-abstract-table + :documentation "XRef to the table this belongs to.") + ) + "A place where semanticdb-find can store search index information. +The search index will store data about which other tables might be +needed, or perhaps create hash or index tables for the current buffer." + :abstract t) + +(defmethod semanticdb-get-table-index ((obj semanticdb-abstract-table)) + "Return the search index for the table OBJ. +If one doesn't exist, create it." + (if (slot-boundp obj 'index) + (oref obj index) + (let ((idx nil)) + (setq idx (funcall semanticdb-default-find-index-class + (concat (object-name obj) " index") + ;; Fill in the defaults + :table obj + )) + (oset obj index idx) + idx))) + +(defmethod semanticdb-synchronize ((idx semanticdb-abstract-search-index) + new-tags) + "Synchronize the search index IDX with some NEW-TAGS." + ;; The abstract class will do... NOTHING! + ) + +(defmethod semanticdb-partial-synchronize ((idx semanticdb-abstract-search-index) + new-tags) + "Synchronize the search index IDX with some changed NEW-TAGS." + ;; The abstract class will do... NOTHING! + ) + + +;;; SEARCH RESULTS TABLE +;; +;; Needed for system databases that may not provide +;; a semanticdb-table associated with a file. +;; +(defclass semanticdb-search-results-table (semanticdb-abstract-table) + ( + ) + "Table used for search results when there is no file or table association. +Examples include search results from external sources such as from +Emacs' own symbol table, or from external libraries.") + +(defmethod semanticdb-refresh-table ((obj semanticdb-search-results-table) &optional force) + "If the tag list associated with OBJ is loaded, refresh it. +This will call `semantic-fetch-tags' if that file is in memory." + nil) + +;;; CONCRETE TABLE CLASSES +;; +(defclass semanticdb-table (semanticdb-abstract-table) + ((file :initarg :file + :documentation "File name relative to the parent database. +This is for the file whose tags are stored in this TABLE object.") + (buffer :initform nil + :documentation "The buffer associated with this table. +If nil, the table's buffer is no in Emacs. If it has a value, then +it is in Emacs.") + (dirty :initform nil + :documentation + "Non nil if this table needs to be `Saved'.") + (db-refs :initform nil + :documentation + "List of `semanticdb-table' objects refering to this one. +These aren't saved, but are instead recalculated after load. +See the file semanticdb-ref.el for how this slot is used.") + (pointmax :initarg :pointmax + :initform nil + :documentation "Size of buffer when written to disk. +Checked on retrieval to make sure the file is the same.") + (fsize :initarg :fsize + :initform nil + :documentation "Size of the file when it was last referenced. +Checked when deciding if a loaded table needs updating from changes +outside of Semantic's control.") + (lastmodtime :initarg :lastmodtime + :initform nil + :documentation "Last modification time of the file referenced. +Checked when deciding if a loaded table needs updating from changes outside of +Semantic's control.") + ;; @todo - need to add `last parsed time', so we can also have + ;; refresh checks if spp tables or the parser gets rebuilt. + (unmatched-syntax :initarg :unmatched-syntax + :documentation + "List of vectors specifying unmatched syntax.") + + (lexical-table :initarg :lexical-table + :initform nil + :printer semantic-lex-spp-table-write-slot-value + :documentation + "Table that might be needed by the lexical analyzer. +For C/C++, the C preprocessor macros can be saved here.") + ) + "A single table of tags derived from file.") + +(defmethod semanticdb-in-buffer-p ((obj semanticdb-table)) + "Return a buffer associated with OBJ. +If the buffer is in memory, return that buffer." + (let ((buff (oref obj buffer))) + (if (buffer-live-p buff) + buff + (oset obj buffer nil)))) + +(defmethod semanticdb-get-buffer ((obj semanticdb-table)) + "Return a buffer associated with OBJ. +If the buffer is in memory, return that buffer. +If the buffer is not in memory, load it with `find-file-noselect'." + (or (semanticdb-in-buffer-p obj) + ;; Save match data to protect against odd stuff in mode hooks. + (save-match-data + (find-file-noselect (semanticdb-full-filename obj) t)))) + +(defmethod semanticdb-set-buffer ((obj semanticdb-table)) + "Set the current buffer to be a buffer owned by OBJ. +If OBJ's file is not loaded, read it in first." + (set-buffer (semanticdb-get-buffer obj))) + +(defmethod semanticdb-full-filename ((obj semanticdb-table)) + "Fetch the full filename that OBJ refers to." + (expand-file-name (oref obj file) + (oref (oref obj parent-db) reference-directory))) + +(defmethod semanticdb-dirty-p ((obj semanticdb-table)) + "Return non-nil if OBJ is 'dirty'." + (oref obj dirty)) + +(defmethod semanticdb-set-dirty ((obj semanticdb-table)) + "Mark the abstract table OBJ dirty." + (oset obj dirty t) + ) + +(defmethod object-print ((obj semanticdb-table) &rest strings) + "Pretty printer extension for `semanticdb-table'. +Adds the number of tags in this file to the object print name." + (apply 'call-next-method obj + (cons (if (oref obj dirty) ", DIRTY" "") strings))) + +;;; DATABASE BASE CLASS +;; +(defclass semanticdb-project-database (eieio-instance-tracker) + ((tracking-symbol :initform semanticdb-database-list) + (reference-directory :type string + :documentation "Directory this database refers to. +When a cache directory is specified, then this refers to the directory +this database contains symbols for.") + (new-table-class :initform semanticdb-table + :type class + :documentation + "New tables created for this database are of this class.") + (cache :type list + :initform nil + :documentation "List of cache information for tools. +Any particular tool can cache data to a database at runtime +with `semanticdb-cache-get'. + +Using a semanticdb cache does not save any information to a file, +so your cache will need to be recalculated at runtime. + +Note: This index will not be saved in a persistent file.") + (tables :initarg :tables + :type list + ;; Need this protection so apps don't try to access + ;; the tables without using the accessor. + :accessor semanticdb-get-database-tables + :protection :protected + :documentation "List of `semantic-db-table' objects.")) + "Database of file tables.") + +(defmethod semanticdb-full-filename ((obj semanticdb-project-database)) + "Fetch the full filename that OBJ refers to. +Abstract tables do not have file names associated with them." + nil) + +(defmethod semanticdb-dirty-p ((DB semanticdb-project-database)) + "Return non-nil if DB is 'dirty'. +A database is dirty if the state of the database changed in a way +where it may need to resynchronize with some persistent storage." + (let ((dirty nil) + (tabs (oref DB tables))) + (while (and (not dirty) tabs) + (setq dirty (semanticdb-dirty-p (car tabs))) + (setq tabs (cdr tabs))) + dirty)) + +(defmethod object-print ((obj semanticdb-project-database) &rest strings) + "Pretty printer extension for `semanticdb-project-database'. +Adds the number of tables in this file to the object print name." + (apply 'call-next-method obj + (cons (format " (%d tables%s)" + (length (semanticdb-get-database-tables obj)) + (if (semanticdb-dirty-p obj) + " DIRTY" "") + ) + strings))) + +(defmethod semanticdb-create-database :STATIC ((dbc semanticdb-project-database) directory) + "Create a new semantic database of class DBC for DIRECTORY and return it. +If a database for DIRECTORY has already been created, return it. +If DIRECTORY doesn't exist, create a new one." + (let ((db (semanticdb-directory-loaded-p directory))) + (unless db + (setq db (semanticdb-project-database + (file-name-nondirectory directory) + :tables nil)) + ;; Set this up here. We can't put it in the constructor because it + ;; would be saved, and we want DB files to be portable. + (oset db reference-directory (file-truename directory))) + db)) + +(defmethod semanticdb-flush-database-tables ((db semanticdb-project-database)) + "Reset the tables in DB to be empty." + (oset db tables nil)) + +(defmethod semanticdb-create-table ((db semanticdb-project-database) file) + "Create a new table in DB for FILE and return it. +The class of DB contains the class name for the type of table to create. +If the table for FILE exists, return it. +If the table for FILE does not exist, create one." + (let ((newtab (semanticdb-file-table db file))) + (unless newtab + ;; This implementation will satisfy autoloaded classes + ;; for tables. + (setq newtab (funcall (oref db new-table-class) + (file-name-nondirectory file) + :file (file-name-nondirectory file) + )) + (oset newtab parent-db db) + (object-add-to-list db 'tables newtab t)) + newtab)) + +(defmethod semanticdb-file-table ((obj semanticdb-project-database) filename) + "From OBJ, return FILENAME's associated table object." + (object-assoc (file-relative-name (file-truename filename) + (oref obj reference-directory)) + 'file (oref obj tables))) + +;; DATABASE FUNCTIONS +(defun semanticdb-get-database (filename) + "Get a database for FILENAME. +If one isn't found, create one." + (semanticdb-create-database semanticdb-new-database-class (file-truename filename))) + +(defun semanticdb-directory-loaded-p (path) + "Return the project belonging to PATH if it was already loaded." + (eieio-instance-tracker-find path 'reference-directory 'semanticdb-database-list)) + +(defun semanticdb-create-table-for-file (filename) + "Initialize a database table for FILENAME, and return it. +If FILENAME exists in the database already, return that. +If there is no database for the table to live in, create one." + (let ((cdb nil) + (tbl nil) + (dd (file-name-directory filename)) + ) + ;; Allow a database override function + (setq cdb (semanticdb-create-database semanticdb-new-database-class + dd)) + ;; Get a table for this file. + (setq tbl (semanticdb-create-table cdb filename)) + + ;; Return the pair. + (cons cdb tbl) + )) + +;;; Cache Cache. +;; +(defclass semanticdb-abstract-cache () + ((table :initarg :table + :type semanticdb-abstract-table + :documentation + "Cross reference to the table this belongs to.") + ) + "Abstract baseclass for tools to use to cache information in semanticdb. +Tools needing a per-file cache must subclass this, and then get one as +needed. Cache objects are identified in semanticdb by subclass. +In order to keep your cache up to date, be sure to implement +`semanticdb-synchronize', and `semanticdb-partial-synchronize'. +See the file semantic-scope.el for an example." + :abstract t) + +(defmethod semanticdb-cache-get ((table semanticdb-abstract-table) + desired-class) + "Get a cache object on TABLE of class DESIRED-CLASS. +This method will create one if none exists with no init arguments +other than :table." + (assert (child-of-class-p desired-class 'semanticdb-abstract-cache)) + (let ((cache (oref table cache)) + (obj nil)) + (while (and (not obj) cache) + (if (eq (object-class-fast (car cache)) desired-class) + (setq obj (car cache))) + (setq cache (cdr cache))) + (if obj + obj ;; Just return it. + ;; No object, lets create a new one and return that. + (setq obj (funcall desired-class "Cache" :table table)) + (object-add-to-list table 'cache obj) + obj))) + +(defmethod semanticdb-cache-remove ((table semanticdb-abstract-table) + cache) + "Remove from TABLE the cache object CACHE." + (object-remove-from-list table 'cache cache)) + +(defmethod semanticdb-synchronize ((cache semanticdb-abstract-cache) + new-tags) + "Synchronize a CACHE with some NEW-TAGS." + ;; The abstract class will do... NOTHING! + ) + +(defmethod semanticdb-partial-synchronize ((cache semanticdb-abstract-cache) + new-tags) + "Synchronize a CACHE with some changed NEW-TAGS." + ;; The abstract class will do... NOTHING! + ) + +(defclass semanticdb-abstract-db-cache () + ((db :initarg :db + :type semanticdb-project-database + :documentation + "Cross reference to the database this belongs to.") + ) + "Abstract baseclass for tools to use to cache information in semanticdb. +Tools needing a database cache must subclass this, and then get one as +needed. Cache objects are identified in semanticdb by subclass. +In order to keep your cache up to date, be sure to implement +`semanticdb-synchronize', and `semanticdb-partial-synchronize'. +See the file semantic-scope.el for an example." + :abstract t) + +(defmethod semanticdb-cache-get ((db semanticdb-project-database) + desired-class) + "Get a cache object on DB of class DESIRED-CLASS. +This method will create one if none exists with no init arguments +other than :table." + (assert (child-of-class-p desired-class 'semanticdb-abstract-db-cache)) + (let ((cache (oref db cache)) + (obj nil)) + (while (and (not obj) cache) + (if (eq (object-class-fast (car cache)) desired-class) + (setq obj (car cache))) + (setq cache (cdr cache))) + (if obj + obj ;; Just return it. + ;; No object, lets create a new one and return that. + (setq obj (funcall desired-class "Cache" :db db)) + (object-add-to-list db 'cache obj) + obj))) + +(defmethod semanticdb-cache-remove ((db semanticdb-project-database) + cache) + "Remove from TABLE the cache object CACHE." + (object-remove-from-list db 'cache cache)) + + +(defmethod semanticdb-synchronize ((cache semanticdb-abstract-db-cache) + new-tags) + "Synchronize a CACHE with some NEW-TAGS." + ;; The abstract class will do... NOTHING! + ) + +(defmethod semanticdb-partial-synchronize ((cache semanticdb-abstract-db-cache) + new-tags) + "Synchronize a CACHE with some changed NEW-TAGS." + ;; The abstract class will do... NOTHING! + ) + +;;; REFRESH + +(defmethod semanticdb-refresh-table ((obj semanticdb-table) &optional force) + "If the tag list associated with OBJ is loaded, refresh it. +Optional argument FORCE will force a refresh even if the file in question +is not in a buffer. Avoid using FORCE for most uses, as an old cache +may be sufficient for the general case. Forced updates can be slow. +This will call `semantic-fetch-tags' if that file is in memory." + (when (or (semanticdb-in-buffer-p obj) force) + (save-excursion + (semanticdb-set-buffer obj) + (semantic-fetch-tags)))) + +(defmethod semanticdb-needs-refresh-p ((obj semanticdb-table)) + "Return non-nil of OBJ's tag list is out of date. +The file associated with OBJ does not need to be in a buffer." + (let* ((ff (semanticdb-full-filename obj)) + (buff (semanticdb-in-buffer-p obj)) + ) + (if buff + (save-excursion + (set-buffer buff) + ;; Use semantic's magic tracker to determine of the buffer is up + ;; to date or not. + (not (semantic-parse-tree-up-to-date-p)) + ;; We assume that semanticdb is keeping itself up to date. + ;; via all the clever hooks + ) + ;; Buffer isn't loaded. The only clue we have is if the file + ;; is somehow different from our mark in the semanticdb table. + (let* ((stats (file-attributes ff)) + (actualsize (nth 7 stats)) + (actualmod (nth 5 stats)) + ) + + (or (not (slot-boundp obj 'tags)) + ;; (not (oref obj tags)) --> not needed anymore? + (/= (or (oref obj fsize) 0) actualsize) + (not (equal (oref obj lastmodtime) actualmod)) + ) + )))) + + +;;; Synchronization +;; +(defmethod semanticdb-synchronize ((table semanticdb-abstract-table) + new-tags) + "Synchronize the table TABLE with some NEW-TAGS." + (oset table tags new-tags) + (oset table pointmax (point-max)) + (let ((fattr (file-attributes (semanticdb-full-filename table)))) + (oset table fsize (nth 7 fattr)) + (oset table lastmodtime (nth 5 fattr)) + ) + ;; Assume it is now up to date. + (oset table unmatched-syntax semantic-unmatched-syntax-cache) + ;; The lexical table should be good too. + (when (featurep 'semantic/lex-spp) + (oset table lexical-table (semantic-lex-spp-save-table))) + ;; this implies dirtyness + (semanticdb-set-dirty table) + + ;; Synchronize the index + (when (slot-boundp table 'index) + (let ((idx (oref table index))) + (when idx (semanticdb-synchronize idx new-tags)))) + + ;; Synchronize application caches. + (dolist (C (oref table cache)) + (semanticdb-synchronize C new-tags) + ) + + ;; Update cross references + ;; (semanticdb-refresh-references table) + ) + +(defmethod semanticdb-partial-synchronize ((table semanticdb-abstract-table) + new-tags) + "Synchronize the table TABLE where some NEW-TAGS changed." + ;; You might think we need to reset the tags, but since the partial + ;; parser splices the lists, we don't need to do anything + ;;(oset table tags new-tags) + ;; We do need to mark ourselves dirty. + (semanticdb-set-dirty table) + + ;; The lexical table may be modified. + (when (featurep 'semantic/lex-spp) + (oset table lexical-table (semantic-lex-spp-save-table))) + + ;; Incremental parser doesn't mokey around with this. + (oset table unmatched-syntax semantic-unmatched-syntax-cache) + + ;; Synchronize the index + (when (slot-boundp table 'index) + (let ((idx (oref table index))) + (when idx (semanticdb-partial-synchronize idx new-tags)))) + + ;; Synchronize application caches. + (dolist (C (oref table cache)) + (semanticdb-synchronize C new-tags) + ) + + ;; Update cross references + ;;(when (semantic-find-tags-by-class 'include new-tags) + ;; (semanticdb-refresh-references table)) + ) + +;;; SAVE/LOAD +;; +(defmethod semanticdb-save-db ((DB semanticdb-project-database) + &optional supress-questions) + "Cause a database to save itself. +The database base class does not save itself persistently. +Subclasses could save themselves to a file, or to a database, or other +form." + nil) + +(defun semanticdb-save-current-db () + "Save the current tag database." + (interactive) + (message "Saving current tag summaries...") + (semanticdb-save-db semanticdb-current-database) + (message "Saving current tag summaries...done")) + +;; This prevents Semanticdb from querying multiple times if the users +;; answers "no" to creating the Semanticdb directory. +(defvar semanticdb--inhibit-create-file-directory) + +(defun semanticdb-save-all-db () + "Save all semantic tag databases." + (interactive) + (message "Saving tag summaries...") + (let ((semanticdb--inhibit-make-directory nil)) + (mapc 'semanticdb-save-db semanticdb-database-list)) + (message "Saving tag summaries...done")) + +(defun semanticdb-save-all-db-idle () + "Save all semantic tag databases from idle time. +Exit the save between databases if there is user input." + (semantic-safe "Auto-DB Save: %S" + (semantic-exit-on-input 'semanticdb-idle-save + (mapc (lambda (db) + (semantic-throw-on-input 'semanticdb-idle-save) + (semanticdb-save-db db t)) + semanticdb-database-list)) + )) + +;;; Directory Project support +;; +(defvar semanticdb-project-predicate-functions nil + "List of predicates to try that indicate a directory belongs to a project. +This list is used when `semanticdb-persistent-path' contains the value +'project. If the predicate list is nil, then presume all paths are valid. + +Project Management software (such as EDE and JDE) should add their own +predicates with `add-hook' to this variable, and semanticdb will save tag +caches in directories controlled by them.") + +(defmethod semanticdb-write-directory-p ((obj semanticdb-project-database)) + "Return non-nil if OBJ should be written to disk. +Uses `semanticdb-persistent-path' to determine the return value." + nil) + +;;; Utilities +;; +;; What is the current database, are two tables of an equivalent mode, +;; and what databases are a part of the same project. +(defun semanticdb-current-database () + "Return the currently active database." + (or semanticdb-current-database + (and default-directory + (semanticdb-create-database semanticdb-new-database-class + default-directory) + ) + nil)) + +(defvar semanticdb-match-any-mode nil + "Non-nil to temporarilly search any major mode for a tag. +If a particular major mode wants to search any mode, put the +`semantic-match-any-mode' symbol onto the symbol of that major mode. +Do not set the value of this variable permanently.") + +(defmacro semanticdb-with-match-any-mode (&rest body) + "A Semanticdb search occuring withing BODY will search tags in all modes. +This temporarilly sets `semanticdb-match-any-mode' while executing BODY." + `(let ((semanticdb-match-any-mode t)) + ,@body)) +(put 'semanticdb-with-match-any-mode 'lisp-indent-function 0) + +(defmethod semanticdb-equivalent-mode-for-search (table &optional buffer) + "Return non-nil if TABLE's mode is equivalent to BUFFER. +See `semanticdb-equivalent-mode' for details. +This version is used during searches. Major-modes that opt +to set the `semantic-match-any-mode' property will be able to search +all files of any type." + (or (get major-mode 'semantic-match-any-mode) + semanticdb-match-any-mode + (semanticdb-equivalent-mode table buffer)) + ) + +(defmethod semanticdb-equivalent-mode ((table semanticdb-abstract-table) &optional buffer) + "Return non-nil if TABLE's mode is equivalent to BUFFER. +Equivalent modes are specified by by `semantic-equivalent-major-modes' +local variable." + nil) + +(defmethod semanticdb-equivalent-mode ((table semanticdb-table) &optional buffer) + "Return non-nil if TABLE's mode is equivalent to BUFFER. +Equivalent modes are specified by by `semantic-equivalent-major-modes' +local variable." + (save-excursion + (if buffer (set-buffer buffer)) + (or + ;; nil major mode in table means we don't know yet. Assume yes for now? + (null (oref table major-mode)) + ;; nil means the same as major-mode + (and (not semantic-equivalent-major-modes) + (mode-local-use-bindings-p major-mode (oref table major-mode))) + (and semantic-equivalent-major-modes + (member (oref table major-mode) semantic-equivalent-major-modes)) + ) + )) + + +;;; Associations +;; +;; These routines determine associations between a file, and multiple +;; associated databases. + +(defcustom semanticdb-project-roots nil + "*List of directories, where each directory is the root of some project. +All subdirectories of a root project are considered a part of one project. +Values in this string can be overriden by project management programs +via the `semanticdb-project-root-functions' variable." + :group 'semanticdb + :type '(repeat string)) + +(defvar semanticdb-project-root-functions nil + "List of functions used to determine a given directories project root. +Functions in this variable can override `semanticdb-project-roots'. +Functions set in the variable are given one argument (a directory) and +must return a string, (the root directory) or a list of strings (multiple +root directories in a more complex system). This variable should be used +by project management programs like EDE or JDE.") + +(defvar semanticdb-project-system-databases nil + "List of databases containing system library information. +Mode authors can create their own system databases which know +detailed information about the system libraries for querying purposes. +Put those into this variable as a buffer-local, or mode-local +value.") +(make-variable-buffer-local 'semanticdb-project-system-databases) + +(defvar semanticdb-search-system-databases t + "Non nil if search routines are to include a system database.") + +(defun semanticdb-current-database-list (&optional dir) + "Return a list of databases associated with the current buffer. +If optional argument DIR is non-nil, then use DIR as the starting directory. +If this buffer has a database, but doesn't have a project associated +with it, return nil. +First, it checks `semanticdb-project-root-functions', and if that +has no results, it checks `semanticdb-project-roots'. If that fails, +it returns the results of function `semanticdb-current-database'. +Always append `semanticdb-project-system-databases' if +`semanticdb-search-system' is non-nil." + (let ((root nil) ; found root directory + (dbs nil) ; collected databases + (roots semanticdb-project-roots) ;all user roots + (dir (file-truename (or dir default-directory))) + ) + ;; Find the root based on project functions. + (setq root (run-hook-with-args-until-success + 'semanticdb-project-root-functions + dir)) + ;; Find roots based on strings + (while (and roots (not root)) + (let ((r (file-truename (car roots)))) + (if (string-match (concat "^" (regexp-quote r)) dir) + (setq root r))) + (setq roots (cdr roots))) + + ;; If no roots are found, use this directory. + (unless root (setq root dir)) + + ;; Find databases based on the root directory. + (when root + ;; The rootlist allows the root functions to possibly + ;; return several roots which are in different areas but + ;; all apart of the same system. + (let ((regexp (concat "^" (regexp-quote root))) + (adb semanticdb-database-list) ; all databases + ) + (while adb + ;; I don't like this part, but close enough. + (if (and (slot-boundp (car adb) 'reference-directory) + (string-match regexp (oref (car adb) reference-directory))) + (setq dbs (cons (car adb) dbs))) + (setq adb (cdr adb)))) + ) + ;; Add in system databases + (when semanticdb-search-system-databases + (setq dbs (nconc dbs semanticdb-project-system-databases))) + ;; Return + dbs)) + + +;;; Generic Accessor Routines +;; +;; These routines can be used to get at tags in files w/out +;; having to know a lot about semanticDB. +(defvar semanticdb-file-table-hash (make-hash-table :test 'equal) + "Hash table mapping file names to database tables.") + +(defun semanticdb-file-table-object-from-hash (file) + "Retrieve a DB table from the hash for FILE. +Does not use `file-truename'." + (gethash file semanticdb-file-table-hash 'no-hit)) + +(defun semanticdb-file-table-object-put-hash (file dbtable) + "For FILE, associate DBTABLE in the hash table." + (puthash file dbtable semanticdb-file-table-hash)) + +;;;###autoload +(defun semanticdb-file-table-object (file &optional dontload) + "Return a semanticdb table belonging to FILE, make it up to date. +If file has database tags available in the database, return it. +If file does not have tags available, and DONTLOAD is nil, +then load the tags for FILE, and create a new table object for it. +DONTLOAD does not affect the creation of new database objects." + ;; (message "Object Translate: %s" file) + (when (file-exists-p file) + (let* ((default-directory (file-name-directory file)) + (tab (semanticdb-file-table-object-from-hash file)) + (fullfile nil)) + + ;; If it is not in the cache, then extract the more traditional + ;; way by getting the database, and finding a table in that database. + ;; Once we have a table, add it to the hash. + (when (eq tab 'no-hit) + (setq fullfile (file-truename file)) + (let ((db (or ;; This line will pick up system databases. + (semanticdb-directory-loaded-p default-directory) + ;; this line will make a new one if needed. + (semanticdb-get-database default-directory)))) + (setq tab (semanticdb-file-table db fullfile)) + (when tab + (semanticdb-file-table-object-put-hash file tab) + (when (not (string= fullfile file)) + (semanticdb-file-table-object-put-hash fullfile tab) + )) + )) + + (cond + ((and tab + ;; Is this in a buffer? + ;;(find-buffer-visiting (semanticdb-full-filename tab)) + (semanticdb-in-buffer-p tab) + ) + (save-excursion + ;;(set-buffer (find-buffer-visiting (semanticdb-full-filename tab))) + (semanticdb-set-buffer tab) + (semantic-fetch-tags) + ;; Return the table. + tab)) + ((and tab dontload) + ;; If we have table, and we don't want to load it, just return it. + tab) + ((and tab + ;; Is table fully loaded, or just a proxy? + (number-or-marker-p (oref tab pointmax)) + ;; Is this table up to date with the file? + (not (semanticdb-needs-refresh-p tab))) + ;; A-ok! + tab) + ((or (and fullfile (get-file-buffer fullfile)) + (get-file-buffer file)) + ;; are these two calls this faster than `find-buffer-visiting'? + + ;; If FILE is being visited, but none of the above state is + ;; true (meaning, there is no table object associated with it) + ;; then it is a file not supported by Semantic, and can be safely + ;; ignored. + nil) + ((not dontload) ;; We must load the file. + ;; Full file should have been set by now. Debug why not? + (when (and (not tab) (not fullfile)) + ;; This case is if a 'nil is erroneously put into the hash table. This + ;; would need fixing + (setq fullfile (file-truename file)) + ) + + ;; If we have a table, but no fullfile, that's ok. Lets get the filename + ;; from the table which is pre-truenamed. + (when (and (not fullfile) tab) + (setq fullfile (semanticdb-full-filename tab))) + + (setq tab (semanticdb-create-table-for-file-not-in-buffer fullfile)) + + ;; Save the new table. + (semanticdb-file-table-object-put-hash file tab) + (when (not (string= fullfile file)) + (semanticdb-file-table-object-put-hash fullfile tab) + ) + ;; Done! + tab) + (t + ;; Full file should have been set by now. Debug why not? + ;; One person found this. Is it a file that failed to parse + ;; in the past? + (when (not fullfile) + (setq fullfile (file-truename file))) + + ;; We were asked not to load the file in and parse it. + ;; Instead just create a database table with no tags + ;; and a claim of being empty. + ;; + ;; This will give us a starting point for storing + ;; database cross-references so when it is loaded, + ;; the cross-references will fire and caches will + ;; be cleaned. + (let ((ans (semanticdb-create-table-for-file file))) + (setq tab (cdr ans)) + + ;; Save the new table. + (semanticdb-file-table-object-put-hash file tab) + (when (not (string= fullfile file)) + (semanticdb-file-table-object-put-hash fullfile tab) + ) + ;; Done! + tab)) + ) + ))) + +(defvar semanticdb-out-of-buffer-create-table-fcn nil + "When non-nil, a function for creating a semanticdb table. +This should take a filename to be parsed.") +(make-variable-buffer-local 'semanticdb-out-of-buffer-create-table-fcn) + +(defun semanticdb-create-table-for-file-not-in-buffer (filename) + "Create a table for the file FILENAME. +If there are no language specific configurations, this +function will read in the buffer, parse it, and kill the buffer." + (if (and semanticdb-out-of-buffer-create-table-fcn + (not (file-remote-p filename))) + ;; Use external parser only of the file is accessible to the + ;; local file system. + (funcall semanticdb-out-of-buffer-create-table-fcn filename) + (save-excursion + (let* ( ;; Remember the buffer to kill + (kill-buffer-flag (find-buffer-visiting filename)) + (buffer-to-kill (or kill-buffer-flag + (semantic-find-file-noselect filename t)))) + + ;; This shouldn't ever be set. Debug some issue here? + ;; (when kill-buffer-flag (debug)) + + (set-buffer buffer-to-kill) + ;; Find file should automatically do this for us. + ;; Sometimes the DB table doesn't contains tags and needs + ;; a refresh. For example, when the file is loaded for + ;; the first time, and the idle scheduler didn't get a + ;; chance to trigger a parse before the file buffer is + ;; killed. + (when semanticdb-current-table + (semantic-fetch-tags)) + (prog1 + semanticdb-current-table + (when (not kill-buffer-flag) + ;; If we had to find the file, then we should kill it + ;; to keep the master buffer list clean. + (kill-buffer buffer-to-kill) + ))))) + ) + +(defun semanticdb-file-stream (file) + "Return a list of tags belonging to FILE. +If file has database tags available in the database, return them. +If file does not have tags available, then load the file, and create them." + (let ((table (semanticdb-file-table-object file))) + (when table + (semanticdb-get-tags table)))) + +(provide 'semantic/db) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/db" +;; End: + +;;; semantic/db.el ends here diff --git a/lisp/cedet/semantic/debug.el b/lisp/cedet/semantic/debug.el new file mode 100644 index 00000000000..3a348f2a9e9 --- /dev/null +++ b/lisp/cedet/semantic/debug.el @@ -0,0 +1,576 @@ +;;; semantic/debug.el --- Language Debugger framework + +;;; Copyright (C) 2003, 2004, 2005, 2008 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; To provide better support for debugging parsers, this framework +;; provides the interface for debugging. The work of parsing and +;; controlling and stepping through the parsing work must be implemented +;; by the parser. +;; +;; Fortunatly, the nature of language support files means that the parser +;; may not need to be instrumented first. +;; +;; The debugger uses EIEIO objects. One object controls the user +;; interface, including stepping, data-view, queries. A second +;; object implemented here represents the parser itself. A third represents +;; a parser independent frame which knows how to highlight the parser buffer. +;; Each parser must implement the interface and override any methods as needed. +;; + +(require 'semantic) +(require 'eieio) +(eval-when-compile (require 'semantic/find)) + +;;; Code: + +;;;###autoload +(defvar semantic-debug-parser-source nil + "For any buffer, the file name (no path) of the parser. +This would be a parser for a specific language, not the source +to one of the parser generators.") +;;;###autoload +(make-variable-buffer-local 'semantic-debug-parser-source) + +;;;###autoload +(defvar semantic-debug-parser-class nil + "Class to create when building a debug parser object.") +;;;###autoload +(make-variable-buffer-local 'semantic-debug-parser-class) + +(defvar semantic-debug-enabled nil + "Non-nil when debugging a parser.") + +;;; Variables used during a debug session. +(defvar semantic-debug-current-interface nil + "The debugger interface currently active for this buffer.") + +(defvar semantic-debug-current-parser nil + "The parser current active for this buffer.") + +;;; User Interface Portion +;; +(defclass semantic-debug-interface () + ((parser-buffer :initarg :parser-buffer + :type buffer + :documentation + "The buffer containing the parser we are debugging.") + (parser-local-map :initarg :parser-local-map + :type keymap + :documentation + "The local keymap originally in the PARSER buffer.") + (parser-location :type marker + :documentation + "A marker representing where we are in the parser buffer.") + (source-buffer :initarg :source-buffer + :type buffer + :documentation + "The buffer containing the source we are parsing. +The :parser-buffer defines a parser that can parse the text in the +:source-buffer.") + (source-local-map :initarg :source-local-map + :type keymap + :documentation + "The local keymap originally in the SOURCE buffer.") + (source-location :type marker + :documentation + "A marker representing where we are in the parser buffer.") + (data-buffer :initarg :data-buffer + :type buffer + :documentation + "Buffer being used to display some useful data. +These buffers are brought into view when layout occurs.") + (current-frame :type semantic-debug-frame + :documentation + "The currently displayed frame.") + (overlays :type list + :initarg nil + :documentation + "Any active overlays being used to show the debug position.") + ) + "Controls action when in `semantic-debug-mode'") + +;; Methods +(defmethod semantic-debug-set-frame ((iface semantic-debug-interface) frame) + "Set the current frame on IFACE to FRAME." + (if frame + (oset iface current-frame frame) + (slot-makeunbound iface 'current-frame))) + +(defmethod semantic-debug-set-parser-location ((iface semantic-debug-interface) point) + "Set the parser location in IFACE to POINT." + (save-excursion + (set-buffer (oref iface parser-buffer)) + (if (not (slot-boundp iface 'parser-location)) + (oset iface parser-location (make-marker))) + (move-marker (oref iface parser-location) point)) + ) + +(defmethod semantic-debug-set-source-location ((iface semantic-debug-interface) point) + "Set the source location in IFACE to POINT." + (save-excursion + (set-buffer (oref iface source-buffer)) + (if (not (slot-boundp iface 'source-location)) + (oset iface source-location (make-marker))) + (move-marker (oref iface source-location) point)) + ) + +(defmethod semantic-debug-interface-layout ((iface semantic-debug-interface)) + "Layout windows in the current frame to facilitate debugging." + (delete-other-windows) + ;; Deal with the data buffer + (when (slot-boundp iface 'data-buffer) + (let ((lines (/ (frame-height (selected-frame)) 3)) + (cnt (save-excursion + (set-buffer (oref iface data-buffer)) + (count-lines (point-min) (point-max)))) + ) + ;; Set the number of lines to 1/3, or the size of the data buffer. + (if (< cnt lines) (setq cnt lines)) + + (split-window-vertically cnt) + (switch-to-buffer (oref iface data-buffer)) + ) + (other-window 1)) + ;; Parser + (switch-to-buffer (oref iface parser-buffer)) + (when (slot-boundp iface 'parser-location) + (goto-char (oref iface parser-location))) + (split-window-vertically) + (other-window 1) + ;; Source + (switch-to-buffer (oref iface source-buffer)) + (when (slot-boundp iface 'source-location) + (goto-char (oref iface source-location))) + ) + +(defmethod semantic-debug-highlight-lexical-token ((iface semantic-debug-interface) token) + "For IFACE, highlight TOKEN in the source buffer . +TOKEN is a lexical token." + (set-buffer (oref iface :source-buffer)) + + (object-add-to-list iface 'overlays + (semantic-lex-highlight-token token)) + + (semantic-debug-set-source-location iface (semantic-lex-token-start token)) + ) + +(defmethod semantic-debug-highlight-rule ((iface semantic-debug-interface) nonterm &optional rule match) + "For IFACE, highlight NONTERM in the parser buffer. +NONTERM is the name of the rule currently being processed that shows up +as a nonterminal (or tag) in the source buffer. +If RULE and MATCH indicies are specified, highlight those also." + (set-buffer (oref iface :parser-buffer)) + + (let* ((rules (semantic-find-tags-by-class 'nonterminal (current-buffer))) + (nt (semantic-find-first-tag-by-name nonterm rules)) + (o nil) + ) + (when nt + ;; I know it is the first symbol appearing in the body of this token. + (goto-char (semantic-tag-start nt)) + + (setq o (semantic-make-overlay (point) (progn (forward-sexp 1) (point)))) + (semantic-overlay-put o 'face 'highlight) + + (object-add-to-list iface 'overlays o) + + (semantic-debug-set-parser-location iface (semantic-overlay-start o)) + + (when (and rule match) + + ;; Rule, an int, is the rule inside the nonterminal we are following. + (re-search-forward ":\\s-*") + (while (/= 0 rule) + (re-search-forward "^\\s-*|\\s-*") + (setq rule (1- rule))) + + ;; Now find the match inside the rule + (while (/= 0 match) + (forward-sexp 1) + (skip-chars-forward " \t") + (setq match (1- match))) + + ;; Now highlight the thingy we find there. + (setq o (semantic-make-overlay (point) (progn (forward-sexp 1) (point)))) + (semantic-overlay-put o 'face 'highlight) + + (object-add-to-list iface 'overlays o) + + ;; If we have a match for a sub-rule, have the parser position + ;; move so we can see it in the output window for very long rules. + (semantic-debug-set-parser-location iface (semantic-overlay-start o)) + + )))) + +(defmethod semantic-debug-unhighlight ((iface semantic-debug-interface)) + "Remove all debugging overlays." + (mapc 'semantic-overlay-delete (oref iface overlays)) + (oset iface overlays nil)) + +;; Call from the parser at a breakpoint +(defvar semantic-debug-user-command nil + "The command the user is requesting.") + +(defun semantic-debug-break (frame) + "Stop parsing now at FRAME. +FRAME is an object that represents the parser's view of the +current state of the world. +This function enters a recursive edit. It returns +on an `exit-recursive-edit', or if someone uses one +of the `semantic-debug-mode' commands. +It returns the command specified. Parsers need to take action +on different types of return values." + (save-window-excursion + ;; Set up displaying information + (semantic-debug-mode t) + (unwind-protect + (progn + (semantic-debug-frame-highlight frame) + (semantic-debug-interface-layout semantic-debug-current-interface) + (condition-case nil + ;; Enter recursive edit... wait for user command. + (recursive-edit) + (error nil))) + (semantic-debug-unhighlight semantic-debug-current-interface) + (semantic-debug-mode nil)) + ;; Find the requested user state. Do something. + (let ((returnstate semantic-debug-user-command)) + (setq semantic-debug-user-command nil) + returnstate) + )) + +;;; Frame +;; +;; A frame can represent the state at a break point. +(defclass semantic-debug-frame () + ( + ) + "One frame representation.") + +(defmethod semantic-debug-frame-highlight ((frame semantic-debug-frame)) + "Highlight one parser frame." + + ) + +(defmethod semantic-debug-frame-info ((frame semantic-debug-frame)) + "Display info about this one parser frame." + + ) + +;;; Major Mode +;; +(defvar semantic-debug-mode-map + (let ((km (make-sparse-keymap))) + (define-key km "n" 'semantic-debug-next) + (define-key km " " 'semantic-debug-next) + (define-key km "s" 'semantic-debug-step) + (define-key km "u" 'semantic-debug-up) + (define-key km "d" 'semantic-debug-down) + (define-key km "f" 'semantic-debug-fail-match) + (define-key km "h" 'semantic-debug-print-state) + (define-key km "s" 'semantic-debug-jump-to-source) + (define-key km "p" 'semantic-debug-jump-to-parser) + (define-key km "q" 'semantic-debug-quit) + (define-key km "a" 'semantic-debug-abort) + (define-key km "g" 'semantic-debug-go) + (define-key km "b" 'semantic-debug-set-breakpoint) + ;; Some boring bindings. + (define-key km "e" 'eval-expression) + + km) + "Keymap used when in semantic-debug-node.") + +(defun semantic-debug-mode (onoff) + "Turn `semantic-debug-mode' on and off. +Argument ONOFF is non-nil when we are entering debug mode. +\\{semantic-debug-mode-map}" + (let ((iface semantic-debug-current-interface)) + (if onoff + ;; Turn it on + (save-excursion + (set-buffer (oref iface parser-buffer)) + ;; Install our map onto this buffer + (use-local-map semantic-debug-mode-map) + ;; Make the buffer read only + (toggle-read-only 1) + + (set-buffer (oref iface source-buffer)) + ;; Use our map in the source buffer also + (use-local-map semantic-debug-mode-map) + ;; Make the buffer read only + (toggle-read-only 1) + ;; Hooks + (run-hooks 'semantic-debug-mode-hook) + ) + ;; Restore old mode information + (save-excursion + (set-buffer + (oref semantic-debug-current-interface parser-buffer)) + (use-local-map + (oref semantic-debug-current-interface parser-local-map)) + ) + (save-excursion + (set-buffer + (oref semantic-debug-current-interface source-buffer)) + (use-local-map + (oref semantic-debug-current-interface source-local-map)) + ) + (run-hooks 'semantic-debug-exit-hook) + ))) + +(defun semantic-debug () + "Parse the current buffer and run in debug mode." + (interactive) + (if semantic-debug-current-interface + (error "You are already in a debug session")) + (if (not semantic-debug-parser-class) + (error "This major mode does not support parser debugging")) + ;; Clear the cache to force a full reparse. + (semantic-clear-toplevel-cache) + ;; Do the parse + (let ((semantic-debug-enabled t) + ;; Create an interface + (semantic-debug-current-interface + (let ((parserb (semantic-debug-find-parser-source))) + (semantic-debug-interface + "Debug Interface" + :parser-buffer parserb + :parser-local-map (save-excursion + (set-buffer parserb) + (current-local-map)) + :source-buffer (current-buffer) + :source-local-map (current-local-map) + ))) + ;; Create a parser debug interface + (semantic-debug-current-parser + (funcall semantic-debug-parser-class "parser")) + ) + ;; We could recurse into a parser while debugging. + ;; Is that a problem? + (semantic-fetch-tags) + ;; We should turn the auto-parser back on, but don't do it for + ;; now until the debugger is working well. + )) + +(defun semantic-debug-find-parser-source () + "Return a buffer containing the parser source file for the current buffer. +The parser needs to be on the load path, or this routine returns nil." + (if (not semantic-debug-parser-source) + (error "No parser is associated with this buffer")) + (let ((parser (locate-library semantic-debug-parser-source t))) + (if parser + (find-file-noselect parser) + (error "Cannot find parser source. It should be on the load-path")))) + +;;; Debugger commands +;; +(defun semantic-debug-next () + "Perform one parser operation. +In the recursive parser, this steps past one match rule. +In other parsers, this may be just like `semantic-debug-step'." + (interactive) + (let ((parser semantic-debug-current-parser)) + (semantic-debug-parser-next parser) + (exit-recursive-edit) + ) + ) + +(defun semantic-debug-step () + "Perform one parser operation." + (interactive) + (let ((parser semantic-debug-current-parser)) + (semantic-debug-parser-step parser) + (exit-recursive-edit) + ) + ) + +(defun semantic-debug-up () + "Move highlighting representation up one level." + (interactive) + (message "Not implemented yet.") + ) + +(defun semantic-debug-down () + "Move highlighting representation down one level." + (interactive) + (message "Not implemented yet.") + ) + +(defun semantic-debug-fail-match () + "Artificially fail the current match." + (interactive) + (let ((parser semantic-debug-current-parser)) + (semantic-debug-parser-fail parser) + (exit-recursive-edit) + ) + ) + +(defun semantic-debug-print-state () + "Show interesting parser state." + (interactive) + (let ((parser semantic-debug-current-parser)) + (semantic-debug-parser-print-state parser) + ) + ) + +(defun semantic-debug-jump-to-source () + "Move cursor to the source code being parsed at the current lexical token." + (interactive) + (let* ((interface semantic-debug-current-interface) + (buf (oref interface source-buffer))) + (if (get-buffer-window buf) + (progn + (select-frame (window-frame (get-buffer-window buf))) + (select-window (get-buffer-window buf))) + ;; Technically, this should do a window layout operation + (switch-to-buffer buf)) + ) + ) + +(defun semantic-debug-jump-to-parser () + "Move cursor to the parser being debugged." + (interactive) + (let* ((interface semantic-debug-current-interface) + (buf (oref interface parser-buffer))) + (if (get-buffer-window buf) + (progn + (select-frame (window-frame (get-buffer-window buf))) + (select-window (get-buffer-window buf))) + ;; Technically, this should do a window layout operation + (switch-to-buffer buf)) + ) + ) + +(defun semantic-debug-quit () + "Exit debug mode, blowing all stack, and leaving the parse incomplete. +Do not update any tokens already parsed." + (interactive) + (let ((parser semantic-debug-current-parser)) + (semantic-debug-parser-quit parser) + (exit-recursive-edit) + ) + ) + +(defun semantic-debug-abort () + "Abort one level of debug mode, blowing all stack." + (interactive) + (let ((parser semantic-debug-current-parser)) + (semantic-debug-parser-abort parser) + (exit-recursive-edit) + ) + ) + +(defun semantic-debug-go () + "Continue parsing till finish or breakpoint." + (interactive) + (let ((parser semantic-debug-current-parser)) + (semantic-debug-parser-go parser) + (exit-recursive-edit) + ) + ) + +(defun semantic-debug-set-breakpoint () + "Set a breakpoint at the current rule location." + (interactive) + (let ((parser semantic-debug-current-parser) + ;; Get the location as semantic tokens. + (location (semantic-current-tag)) + ) + (if location + (semantic-debug-parser-break parser location) + (error "Not on a rule")) + ) + ) + + +;;; Debugger superclass +;; +(defclass semantic-debug-parser () + ( + ) + "Represents a parser and its state. +When implementing the debug parser you can add extra functionality +by overriding one of the command methods. Be sure to use +`call-next-method' so that the debug command is saved, and passed +down to your parser later." + :abstract t) + +(defmethod semantic-debug-parser-next ((parser semantic-debug-parser)) + "Execute next for this PARSER." + (setq semantic-debug-user-command 'next) + ) + +(defmethod semantic-debug-parser-step ((parser semantic-debug-parser)) + "Execute a step for this PARSER." + (setq semantic-debug-user-command 'step) + ) + +(defmethod semantic-debug-parser-go ((parser semantic-debug-parser)) + "Continue executiong in this PARSER until the next breakpoint." + (setq semantic-debug-user-command 'go) + ) + +(defmethod semantic-debug-parser-fail ((parser semantic-debug-parser)) + "Continue executiong in this PARSER until the next breakpoint." + (setq semantic-debug-user-command 'fail) + ) + +(defmethod semantic-debug-parser-quit ((parser semantic-debug-parser)) + "Continue executiong in this PARSER until the next breakpoint." + (setq semantic-debug-user-command 'quit) + ) + +(defmethod semantic-debug-parser-abort ((parser semantic-debug-parser)) + "Continue executiong in this PARSER until the next breakpoint." + (setq semantic-debug-user-command 'abort) + ) + +(defmethod semantic-debug-parser-print-state ((parser semantic-debug-parser)) + "Print state for this PARSER at the current breakpoint." + (with-slots (current-frame) semantic-debug-current-interface + (when current-frame + (semantic-debug-frame-info current-frame) + ))) + +(defmethod semantic-debug-parser-break ((parser semantic-debug-parser)) + "Set a breakpoint for this PARSER." + ) + +;; Stack stuff +(defmethod semantic-debug-parser-frames ((parser semantic-debug-parser)) + "Return a list of frames for the current parser. +A frame is of the form: + ( .. .what ? .. ) +" + (error "Parser has not implemented frame values") + ) + + +(provide 'semantic/debug) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/debug" +;; End: + +;;; semantic/debug.el ends here diff --git a/lisp/cedet/semantic/decorate.el b/lisp/cedet/semantic/decorate.el new file mode 100644 index 00000000000..70c082e4e98 --- /dev/null +++ b/lisp/cedet/semantic/decorate.el @@ -0,0 +1,299 @@ +;;; semantic/decorate.el --- Utilities for decorating/highlighting tokens. + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 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: +;; +;; Text representing a semantic tag is wrapped in an overlay. +;; This overlay can be used for highlighting, or setting other +;; editing properties on a tag, such as "read only." +;; + +(require 'semantic) +(require 'pulse) + +;;; Code: + +;;; Highlighting Basics +(defun semantic-highlight-tag (tag &optional face) + "Specify that TAG should be highlighted. +Optional FACE specifies the face to use." + (let ((o (semantic-tag-overlay tag))) + (semantic-overlay-put o 'old-face + (cons (semantic-overlay-get o 'face) + (semantic-overlay-get o 'old-face))) + (semantic-overlay-put o 'face (or face 'semantic-tag-highlight-face)) + )) + +(defun semantic-unhighlight-tag (tag) + "Unhighlight TAG, restoring it's previous face." + (let ((o (semantic-tag-overlay tag))) + (semantic-overlay-put o 'face (car (semantic-overlay-get o 'old-face))) + (semantic-overlay-put o 'old-face (cdr (semantic-overlay-get o 'old-face))) + )) + +;;; Momentary Highlighting - One line +(defun semantic-momentary-highlight-one-tag-line (tag &optional face) + "Highlight the first line of TAG, unhighlighting before next command. +Optional argument FACE specifies the face to do the highlighting." + (save-excursion + ;; Go to first line in tag + (semantic-go-to-tag tag) + (pulse-momentary-highlight-one-line (point)))) + +;;; Momentary Highlighting - Whole Tag +(defun semantic-momentary-highlight-tag (tag &optional face) + "Highlight TAG, removing highlighting when the user hits a key. +Optional argument FACE is the face to use for highlighting. +If FACE is not specified, then `highlight' will be used." + (when (semantic-tag-with-position-p tag) + (if (not (semantic-overlay-p (semantic-tag-overlay tag))) + ;; No overlay, but a position. Highlight the first line only. + (semantic-momentary-highlight-one-tag-line tag face) + ;; The tag has an overlay, highlight the whole thing + (pulse-momentary-highlight-overlay (semantic-tag-overlay tag) + face) + ))) + +(defun semantic-set-tag-face (tag face) + "Specify that TAG should use FACE for display." + (semantic-overlay-put (semantic-tag-overlay tag) 'face face)) + +(defun semantic-set-tag-invisible (tag &optional visible) + "Enable the text in TAG to be made invisible. +If VISIBLE is non-nil, make the text visible." + (semantic-overlay-put (semantic-tag-overlay tag) 'invisible + (not visible))) + +(defun semantic-tag-invisible-p (tag) + "Return non-nil if TAG is invisible." + (semantic-overlay-get (semantic-tag-overlay tag) 'invisible)) + +(defun semantic-set-tag-intangible (tag &optional tangible) + "Enable the text in TAG to be made intangible. +If TANGIBLE is non-nil, make the text visible. +This function does not have meaning in XEmacs because it seems that +the extent 'intangible' property does not exist." + (semantic-overlay-put (semantic-tag-overlay tag) 'intangible + (not tangible))) + +(defun semantic-tag-intangible-p (tag) + "Return non-nil if TAG is intangible. +This function does not have meaning in XEmacs because it seems that +the extent 'intangible' property does not exist." + (semantic-overlay-get (semantic-tag-overlay tag) 'intangible)) + +(defun semantic-overlay-signal-read-only + (overlay after start end &optional len) + "Hook used in modification hooks to prevent modification. +Allows deletion of the entire text. +Argument OVERLAY, AFTER, START, END, and LEN are passed in by the system." + ;; Stolen blithly from cpp.el in Emacs 21.1 + (if (and (not after) + (or (< (semantic-overlay-start overlay) start) + (> (semantic-overlay-end overlay) end))) + (error "This text is read only"))) + +(defun semantic-set-tag-read-only (tag &optional writable) + "Enable the text in TAG to be made read-only. +Optional argument WRITABLE should be non-nil to make the text writable +instead of read-only." + (let ((o (semantic-tag-overlay tag)) + (hook (if writable nil '(semantic-overlay-signal-read-only)))) + (if (featurep 'xemacs) + ;; XEmacs extents have a 'read-only' property. + (semantic-overlay-put o 'read-only (not writable)) + (semantic-overlay-put o 'modification-hooks hook) + (semantic-overlay-put o 'insert-in-front-hooks hook) + (semantic-overlay-put o 'insert-behind-hooks hook)))) + +(defun semantic-tag-read-only-p (tag) + "Return non-nil if the current TAG is marked read only." + (let ((o (semantic-tag-overlay tag))) + (if (featurep 'xemacs) + ;; XEmacs extents have a 'read-only' property. + (semantic-overlay-get o 'read-only) + (member 'semantic-overlay-signal-read-only + (semantic-overlay-get o 'modification-hooks))))) + +;;; Secondary overlays +;; +;; Some types of decoration require a second overlay to be made. +;; It could be for images, arrows, or whatever. +;; We need a way to create such an overlay, and make sure it +;; gets whacked, but doesn't show up in the master list +;; of overlays used for searching. +(defun semantic-tag-secondary-overlays (tag) + "Return a list of secondary overlays active on TAG." + (semantic--tag-get-property tag 'secondary-overlays)) + +(defun semantic-tag-create-secondary-overlay (tag &optional link-hook) + "Create a secondary overlay for TAG. +Returns an overlay. The overlay is also saved in TAG. +LINK-HOOK is a function called whenever TAG is to be linked into +a buffer. It should take TAG and OVERLAY as arguments. +The LINK-HOOK should be used to position and set properties on the +generated secondary overlay." + (if (not (semantic-tag-overlay tag)) + ;; do nothing if there is no overlay + nil + (let* ((os (semantic-tag-start tag)) + (oe (semantic-tag-end tag)) + (o (semantic-make-overlay os oe (semantic-tag-buffer tag) t)) + (attr (semantic-tag-secondary-overlays tag)) + ) + (semantic--tag-put-property tag 'secondary-overlays (cons o attr)) + (semantic-overlay-put o 'semantic-secondary t) + (semantic-overlay-put o 'semantic-link-hook link-hook) + (semantic-tag-add-hook tag 'link-hook 'semantic--tag-link-secondary-overlays) + (semantic-tag-add-hook tag 'unlink-hook 'semantic--tag-unlink-secondary-overlays) + (semantic-tag-add-hook tag 'unlink-copy-hook 'semantic--tag-unlink-copy-secondary-overlays) + (run-hook-with-args link-hook tag o) + o))) + +(defun semantic-tag-get-secondary-overlay (tag property) + "Return secondary overlays from TAG with PROPERTY. +PROPERTY is a symbol and all overlays with that symbol are returned.." + (let* ((olsearch (semantic-tag-secondary-overlays tag)) + (o nil)) + (while olsearch + (when (semantic-overlay-get (car olsearch) property) + (setq o (cons (car olsearch) o))) + (setq olsearch (cdr olsearch))) + o)) + +(defun semantic-tag-delete-secondary-overlay (tag overlay-or-property) + "Delete from TAG the secondary overlay OVERLAY-OR-PROPERTY. +If OVERLAY-OR-PROPERTY is an overlay, delete that overlay. +If OVERLAY-OR-PROPERTY is a symbol, find the overlay with that property." + (let* ((o overlay-or-property)) + (if (semantic-overlay-p o) + (setq o (list o)) + (setq o (semantic-tag-get-secondary-overlay tag overlay-or-property))) + (while (semantic-overlay-p (car o)) + ;; We don't really need to worry about the hooks. + ;; They will clean themselves up eventually ?? + (semantic--tag-put-property + tag 'secondary-overlays + (delete (car o) (semantic-tag-secondary-overlays tag))) + (semantic-overlay-delete (car o)) + (setq o (cdr o))))) + +(defun semantic--tag-unlink-copy-secondary-overlays (tag) + "Unlink secondary overlays from TAG which is a copy. +This means we don't destroy the overlays, only remove reference +from them in TAG." + (let ((ol (semantic-tag-secondary-overlays tag))) + (while ol + ;; Else, remove all traces of ourself from the tag + ;; Note to self: Does this prevent multiple types of secondary + ;; overlays per tag? + (semantic-tag-remove-hook tag 'link-hook 'semantic--tag-link-secondary-overlays) + (semantic-tag-remove-hook tag 'unlink-hook 'semantic--tag-unlink-secondary-overlays) + (semantic-tag-remove-hook tag 'unlink-copy-hook 'semantic--tag-unlink-copy-secondary-overlays) + ;; Next! + (setq ol (cdr ol))) + (semantic--tag-put-property tag 'secondary-overlays nil) + )) + +(defun semantic--tag-unlink-secondary-overlays (tag) + "Unlink secondary overlays from TAG." + (let ((ol (semantic-tag-secondary-overlays tag)) + (nl nil)) + (while ol + (if (semantic-overlay-get (car ol) 'semantic-link-hook) + ;; Only put in a proxy if there is a link-hook. If there is no link-hook + ;; the decorating mode must know when tags are unlinked on its own. + (setq nl (cons (semantic-overlay-get (car ol) 'semantic-link-hook) + nl)) + ;; Else, remove all traces of ourself from the tag + ;; Note to self: Does this prevent multiple types of secondary + ;; overlays per tag? + (semantic-tag-remove-hook tag 'link-hook 'semantic--tag-link-secondary-overlays) + (semantic-tag-remove-hook tag 'unlink-hook 'semantic--tag-unlink-secondary-overlays) + (semantic-tag-remove-hook tag 'unlink-copy-hook 'semantic--tag-unlink-copy-secondary-overlays) + ) + (semantic-overlay-delete (car ol)) + (setq ol (cdr ol))) + (semantic--tag-put-property tag 'secondary-overlays (nreverse nl)) + )) + +(defun semantic--tag-link-secondary-overlays (tag) + "Unlink secondary overlays from TAG." + (let ((ol (semantic-tag-secondary-overlays tag))) + ;; Wipe out old values. + (semantic--tag-put-property tag 'secondary-overlays nil) + ;; Run all the link hooks. + (while ol + (semantic-tag-create-secondary-overlay tag (car ol)) + (setq ol (cdr ol))) + )) + +;;; Secondary Overlay Uses +;; +;; States to put on tags that depend on a secondary overlay. +(defun semantic-set-tag-folded (tag &optional folded) + "Fold TAG, such that only the first line of text is shown. +Optional argument FOLDED should be non-nil to fold the tag. +nil implies the tag should be fully shown." + ;; If they are different, do the deed. + (let ((o (semantic-tag-folded-p tag))) + (if (not folded) + ;; We unfold. + (when o + (semantic-tag-delete-secondary-overlay tag 'semantic-folded)) + (unless o + ;; Add the foldn + (setq o (semantic-tag-create-secondary-overlay tag)) + ;; mark as folded + (semantic-overlay-put o 'semantic-folded t) + ;; Move to cover end of tag + (save-excursion + (goto-char (semantic-tag-start tag)) + (end-of-line) + (semantic-overlay-move o (point) (semantic-tag-end tag))) + ;; We need to modify the invisibility spec for this to + ;; work. + (if (or (eq buffer-invisibility-spec t) + (not (assoc 'semantic-fold buffer-invisibility-spec))) + (add-to-invisibility-spec '(semantic-fold . t))) + (semantic-overlay-put o 'invisible 'semantic-fold) + (overlay-put o 'isearch-open-invisible + 'semantic-set-tag-folded-isearch))) + )) + +(declare-function semantic-current-tag "semantic/find") + +(defun semantic-set-tag-folded-isearch (overlay) + "Called by isearch if it discovers text in the folded region. +OVERLAY is passed in by isearch." + (semantic-set-tag-folded (semantic-current-tag) nil) + ) + +(defun semantic-tag-folded-p (tag) + "Non-nil if TAG is currently folded." + (semantic-tag-get-secondary-overlay tag 'semantic-folded) + ) + +(provide 'semantic/decorate) + +;;; semantic/decorate.el ends here diff --git a/lisp/cedet/semantic/decorate/include.el b/lisp/cedet/semantic/decorate/include.el new file mode 100644 index 00000000000..7acae52a58d --- /dev/null +++ b/lisp/cedet/semantic/decorate/include.el @@ -0,0 +1,774 @@ +;;; semantic/decorate/include.el --- Decoration modes for include statements + +;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Highlight any include that is in a state the user may care about. +;; The basic idea is to have the state be highly visible so users will +;; as 'what is this?" and get the info they need to fix problems that +;; are otherwises transparent when trying to get smart completion +;; working. + +(require 'semantic/decorate/mode) +(require 'semantic/db) +(require 'semantic/db-ref) +(require 'semantic/db-find) + +(eval-when-compile + (require 'semantic/find)) + +(defvar semantic-dependency-system-include-path) +(declare-function ede-get-locator-object "ede/files") +(declare-function ede-system-include-path "ede/cpp-root") + +;;; Code: + +;;; FACES AND KEYMAPS +(defvar semantic-decoratiton-mouse-3 (if (featurep 'xemacs) [ button3 ] [ mouse-3 ]) + "The keybinding lisp object to use for binding the right mouse button.") + +;;; Includes that that are in a happy state! +;; +(defface semantic-decoration-on-includes + nil + "*Overlay Face used on includes that are not in some other state. +Used by the decoration style: `semantic-decoration-on-includes'." + :group 'semantic-faces) + +(defvar semantic-decoration-on-include-map + (let ((km (make-sparse-keymap))) + (define-key km semantic-decoratiton-mouse-3 'semantic-decoration-include-menu) + km) + "Keymap used on includes.") + + +(defvar semantic-decoration-on-include-menu nil + "Menu used for include headers.") + +(easy-menu-define + semantic-decoration-on-include-menu + semantic-decoration-on-include-map + "Include Menu" + (list + "Include" + (semantic-menu-item + ["What Is This?" semantic-decoration-include-describe + :active t + :help "Describe why this include has been marked this way." ]) + (semantic-menu-item + ["Visit This Include" semantic-decoration-include-visit + :active t + :help "Visit this include file." ]) + "---" + (semantic-menu-item + ["Summarize includes current buffer" semantic-decoration-all-include-summary + :active t + :help "Show a summary for the current buffer containing this include." ]) + (semantic-menu-item + ["List found includes (load unparsed)" semanticdb-find-test-translate-path + :active t + :help "List all includes found for this file, and parse unparsed files." ]) + (semantic-menu-item + ["List found includes (no loading)" semanticdb-find-test-translate-path-no-loading + :active t + :help "List all includes found for this file, do not parse unparsed files." ]) + (semantic-menu-item + ["List all unknown includes" semanticdb-find-adebug-lost-includes + :active t + :help "Show a list of all includes semantic cannot find for this file." ]) + "---" + (semantic-menu-item + ["Customize System Include Path" semantic-customize-system-include-path + :active (get 'semantic-dependency-system-include-path major-mode) + :help "Run customize for the system include path for this major mode." ]) + (semantic-menu-item + ["Add a System Include Path" semantic-add-system-include + :active t + :help "Add an include path for this session." ]) + (semantic-menu-item + ["Remove a System Include Path" semantic-remove-system-include + :active t + :help "Add an include path for this session." ]) + ;;["" semantic-decoration-include- + ;; :active t + ;; :help "" ] + )) + +;;; Unknown Includes! +;; +(defface semantic-decoration-on-unknown-includes + '((((class color) (background dark)) + (:background "#900000")) + (((class color) (background light)) + (:background "#ff5050"))) + "*Face used to show includes that cannot be found. +Used by the decoration style: `semantic-decoration-on-unknown-includes'." + :group 'semantic-faces) + +(defvar semantic-decoration-on-unknown-include-map + (let ((km (make-sparse-keymap))) + ;(define-key km [ mouse-2 ] 'semantic-decoration-unknown-include-describe) + (define-key km semantic-decoratiton-mouse-3 'semantic-decoration-unknown-include-menu) + km) + "Keymap used on unparsed includes.") + +(defvar semantic-decoration-on-unknown-include-menu nil + "Menu used for unparsed include headers.") + +(easy-menu-define + semantic-decoration-on-unknown-include-menu + semantic-decoration-on-unknown-include-map + "Unknown Include Menu" + (list + "Unknown Include" + (semantic-menu-item + ["What Is This?" semantic-decoration-unknown-include-describe + :active t + :help "Describe why this include has been marked this way." ]) + (semantic-menu-item + ["List all unknown includes" semanticdb-find-adebug-lost-includes + :active t + :help "Show a list of all includes semantic cannot find for this file." ]) + "---" + (semantic-menu-item + ["Summarize includes current buffer" semantic-decoration-all-include-summary + :active t + :help "Show a summary for the current buffer containing this include." ]) + (semantic-menu-item + ["List found includes (load unparsed)" semanticdb-find-test-translate-path + :active t + :help "List all includes found for this file, and parse unparsed files." ]) + (semantic-menu-item + ["List found includes (no loading)" semanticdb-find-test-translate-path-no-loading + :active t + :help "List all includes found for this file, do not parse unparsed files." ]) + "---" + (semantic-menu-item + ["Customize System Include Path" semantic-customize-system-include-path + :active (get 'semantic-dependency-system-include-path major-mode) + :help "Run customize for the system include path for this major mode." ]) + (semantic-menu-item + ["Add a System Include Path" semantic-add-system-include + :active t + :help "Add an include path for this session." ]) + (semantic-menu-item + ["Remove a System Include Path" semantic-remove-system-include + :active t + :help "Add an include path for this session." ]) + )) + +;;; Includes that need to be parsed. +;; +(defface semantic-decoration-on-unparsed-includes + '((((class color) (background dark)) + (:background "#555500")) + (((class color) (background light)) + (:background "#ffff55"))) + "*Face used to show includes that have not yet been parsed. +Used by the decoration style: `semantic-decoration-on-unparsed-includes'." + :group 'semantic-faces) + +(defvar semantic-decoration-on-unparsed-include-map + (let ((km (make-sparse-keymap))) + (define-key km semantic-decoratiton-mouse-3 'semantic-decoration-unparsed-include-menu) + km) + "Keymap used on unparsed includes.") + + +(defvar semantic-decoration-on-unparsed-include-menu nil + "Menu used for unparsed include headers.") + +(easy-menu-define + semantic-decoration-on-unparsed-include-menu + semantic-decoration-on-unparsed-include-map + "Unparsed Include Menu" + (list + "Unparsed Include" + (semantic-menu-item + ["What Is This?" semantic-decoration-unparsed-include-describe + :active t + :help "Describe why this include has been marked this way." ]) + (semantic-menu-item + ["Visit This Include" semantic-decoration-include-visit + :active t + :help "Visit this include file so that header file's tags can be used." ]) + (semantic-menu-item + ["Parse This Include" semantic-decoration-unparsed-include-parse-include + :active t + :help "Parse this include file so that header file's tags can be used." ]) + (semantic-menu-item + ["Parse All Includes" semantic-decoration-unparsed-include-parse-all-includes + :active t + :help "Parse all the includes so the contents can be used." ]) + "---" + (semantic-menu-item + ["Summarize includes current buffer" semantic-decoration-all-include-summary + :active t + :help "Show a summary for the current buffer containing this include." ]) + (semantic-menu-item + ["List found includes (load unparsed)" semanticdb-find-test-translate-path + :active t + :help "List all includes found for this file, and parse unparsed files." ]) + (semantic-menu-item + ["List found includes (no loading)" semanticdb-find-test-translate-path-no-loading + :active t + :help "List all includes found for this file, do not parse unparsed files." ]) + (semantic-menu-item + ["List all unknown includes" semanticdb-find-adebug-lost-includes + :active t + :help "Show a list of all includes semantic cannot find for this file." ]) + "---" + (semantic-menu-item + ["Customize System Include Path" semantic-customize-system-include-path + :active (get 'semantic-dependency-system-include-path major-mode) + :help "Run customize for the system include path for this major mode." ]) + (semantic-menu-item + ["Add a System Include Path" semantic-add-system-include + :active t + :help "Add an include path for this session." ]) + (semantic-menu-item + ["Remove a System Include Path" semantic-remove-system-include + :active t + :help "Add an include path for this session." ]) + ;;["" semantic-decoration-unparsed-include- + ;; :active t + ;; :help "" ] + )) + + +;;; MODES + +;;; Include statement Decorate Mode +;; +;; This mode handles the three states of an include statements +;; +(define-semantic-decoration-style semantic-decoration-on-includes + "Highlight class members that are includes. +This mode provides a nice context menu on the include statements." + :enabled t) + +(defun semantic-decoration-on-includes-p-default (tag) + "Return non-nil if TAG has is an includes that can't be found." + (semantic-tag-of-class-p tag 'include)) + +(defun semantic-decoration-on-includes-highlight-default (tag) + "Highlight the include TAG to show that semantic can't find it." + (let* ((file (semantic-dependency-tag-file tag)) + (table (when file + (semanticdb-file-table-object file t))) + (face nil) + (map nil) + ) + (cond + ((not file) + ;; Cannot find this header. + (setq face 'semantic-decoration-on-unknown-includes + map semantic-decoration-on-unknown-include-map) + ) + ((and table (number-or-marker-p (oref table pointmax))) + ;; A found and parsed file. + (setq face 'semantic-decoration-on-includes + map semantic-decoration-on-include-map) + ) + (t + ;; An unparsed file. + (setq face 'semantic-decoration-on-unparsed-includes + map semantic-decoration-on-unparsed-include-map) + (when table + ;; Set ourselves up for synchronization + (semanticdb-cache-get + table 'semantic-decoration-unparsed-include-cache) + ;; Add a dependancy. + (let ((table semanticdb-current-table)) + (semanticdb-add-reference table tag)) + ) + )) + + (let ((ol (semantic-decorate-tag tag + (semantic-tag-start tag) + (semantic-tag-end tag) + face)) + ) + (semantic-overlay-put ol 'mouse-face 'highlight) + (semantic-overlay-put ol 'keymap map) + (semantic-overlay-put ol 'help-echo + "Header File : mouse-3 - Context menu") + ))) + +;;; Regular Include Functions +;; +(defun semantic-decoration-include-describe () + "Describe what unparsed includes are in the current buffer. +Argument EVENT is the mouse clicked event." + (interactive) + (let* ((tag (or (semantic-current-tag) + (error "No tag under point"))) + (file (semantic-dependency-tag-file tag)) + (table (when file + (semanticdb-file-table-object file t)))) + (with-output-to-temp-buffer (help-buffer) ; "*Help*" + (help-setup-xref (list #'semantic-decoration-include-describe) + (interactive-p)) + (princ "Include File: ") + (princ (semantic-format-tag-name tag nil t)) + (princ "\n") + (princ "This include file was found at:\n ") + (princ (semantic-dependency-tag-file tag)) + (princ "\n\n") + (princ "Semantic knows where this include file is, and has parsed +its contents. + +") + (let ((inc (semantic-find-tags-by-class 'include table)) + (ok 0) + (unknown 0) + (unparsed 0) + (all 0)) + (dolist (i inc) + (let* ((fileinner (semantic-dependency-tag-file i)) + ) + (cond ((not fileinner) + (setq unknown (1+ unknown))) + ((number-or-marker-p (oref table pointmax)) + (setq ok (1+ ok))) + (t + (setq unparsed (1+ unparsed)))))) + (setq all (+ ok unknown unparsed)) + (if (= 0 all) + (princ "There are no other includes in this file.\n") + (princ (format "There are %d more includes in this file.\n" + all)) + (princ (format " Unknown Includes: %d\n" unknown)) + (princ (format " Unparsed Includes: %d\n" unparsed)) + (princ (format " Parsed Includes: %d\n" ok))) + ) + ;; Get the semanticdb statement, and display it's contents. + (princ "\nDetails for header file...\n") + (princ "\nMajor Mode: ") + (princ (oref table :major-mode)) + (princ "\nTags: ") + (princ (format "%s entries" (length (oref table :tags)))) + (princ "\nFile Size: ") + (princ (format "%s chars" (oref table :pointmax))) + (princ "\nSave State: ") + (cond ((oref table dirty) + (princ "Table needs to be saved.")) + (t + (princ "Table is saved on disk.")) + ) + (princ "\nExternal References:") + (dolist (r (oref table db-refs)) + (princ "\n ") + (princ (oref r file))) + ))) + +;;;###autoload +(defun semantic-decoration-include-visit () + "Visit the included file at point." + (interactive) + (let ((tag (semantic-current-tag))) + (unless (eq (semantic-tag-class tag) 'include) + (error "Point is not on an include tag")) + (let ((file (semantic-dependency-tag-file tag))) + (cond + ((or (not file) (not (file-exists-p file))) + (error "Could not location include %s" + (semantic-tag-name tag))) + ((get-file-buffer file) + (switch-to-buffer (get-file-buffer file))) + ((stringp file) + (find-file file)) + )))) + +(defun semantic-decoration-include-menu (event) + "Popup a menu that can help a user understand unparsed includes. +Argument EVENT describes the event that caused this function to be called." + (interactive "e") + (let* ((startwin (selected-window)) + (win (semantic-event-window event)) + ) + (select-window win t) + (save-excursion + ;(goto-char (window-start win)) + (mouse-set-point event) + (sit-for 0) + (semantic-popup-menu semantic-decoration-on-include-menu) + ) + (select-window startwin))) + + +;;; Unknown Include functions +;; +(defun semantic-decoration-unknown-include-describe () + "Describe what unknown includes are in the current buffer. +Argument EVENT is the mouse clicked event." + (interactive) + (let ((tag (semantic-current-tag)) + (mm major-mode)) + (with-output-to-temp-buffer (help-buffer) ; "*Help*" + (help-setup-xref (list #'semantic-decoration-unknown-include-describe) + (interactive-p)) + (princ "Include File: ") + (princ (semantic-format-tag-name tag nil t)) + (princ "\n\n") + (princ "This header file has been marked \"Unknown\". +This means that Semantic has not been able to locate this file on disk. + +When Semantic cannot find an include file, this means that the +idle summary mode and idle completion modes cannot use the contents of +that file to provide coding assistance. + +If this is a system header and you want it excluded from Semantic's +searches (which may be desirable for speed reasons) then you can +safely ignore this state. + +If this is a system header, and you want to include it in Semantic's +searches, then you will need to use: + +M-x semantic-add-system-include RET /path/to/includes RET + +or, in your .emacs file do: + + (semantic-add-system-include \"/path/to/include\" '") + (princ (symbol-name mm)) + (princ ") + +to add the path to Semantic's search. + +If this is an include file that belongs to your project, then you may +need to update `semanticdb-project-roots' or better yet, use `ede' +to manage your project. See the ede manual for projects that will +wrap existing project code for Semantic's benifit. +") + + (when (or (eq mm 'c++-mode) (eq mm 'c-mode)) + (princ " +For C/C++ includes located within a a project, you can use a special +EDE project that will wrap an existing build system. You can do that +like this in your .emacs file: + + (ede-cpp-root-project \"NAME\" :file \"FILENAME\" :locate-fcn 'MYFCN) + +See the CEDET manual, the EDE manual, or the commentary in +ede-cpp-root.el for more. + +If you think this header tag is marked in error, you may need to do: + +C-u M-x bovinate RET + +to refresh the tags in this buffer, and recalculate the state.")) + + (princ " +See the Semantic manual node on SemanticDB for more about search paths.") + ))) + +(defun semantic-decoration-unknown-include-menu (event) + "Popup a menu that can help a user understand unparsed includes. +Argument EVENT describes the event that caused this function to be called." + (interactive "e") + (let* ((startwin (selected-window)) + ;; This line has an issue in XEmacs. + (win (semantic-event-window event)) + ) + (select-window win t) + (save-excursion + ;(goto-char (window-start win)) + (mouse-set-point event) + (sit-for 0) + (semantic-popup-menu semantic-decoration-on-unknown-include-menu) + ) + (select-window startwin))) + + +;;; Interactive parts of unparsed includes +;; +(defun semantic-decoration-unparsed-include-describe () + "Describe what unparsed includes are in the current buffer. +Argument EVENT is the mouse clicked event." + (interactive) + (let ((tag (semantic-current-tag))) + (with-output-to-temp-buffer (help-buffer); "*Help*" + (help-setup-xref (list #'semantic-decoration-unparsed-include-describe) + (interactive-p)) + + (princ "Include File: ") + (princ (semantic-format-tag-name tag nil t)) + (princ "\n") + (princ "This include file was found at:\n ") + (princ (semantic-dependency-tag-file tag)) + (princ "\n\n") + (princ "This header file has been marked \"Unparsed\". +This means that Semantic has located this header file on disk +but has not yet opened and parsed this file. + +So long as this header file is unparsed, idle summary and +idle completion will not be able to reference the details in this +header. + +To resolve this, use the context menu to parse this include file, +or all include files referred to in ") + (princ (buffer-name)) + (princ ". +This can take a while in large projects. + +Alternately, you can call: + +M-x semanticdb-find-test-translate-path RET + +to search path Semantic uses to perform completion. + + +If you think this header tag is marked in error, you may need to do: + +C-u M-x bovinate RET + +to refresh the tags in this buffer, and recalculate the state. +If you find a repeatable case where a header is marked in error, +report it to cedet-devel@lists.sf.net.") ))) + + +(defun semantic-decoration-unparsed-include-menu (event) + "Popup a menu that can help a user understand unparsed includes. +Argument EVENT describes the event that caused this function to be called." + (interactive "e") + (let* ((startwin (selected-window)) + (win (semantic-event-window event)) + ) + (select-window win t) + (save-excursion + ;(goto-char (window-start win)) + (mouse-set-point event) + (sit-for 0) + (semantic-popup-menu semantic-decoration-on-unparsed-include-menu) + ) + (select-window startwin))) + +(defun semantic-decoration-unparsed-include-parse-include () + "Parse the include file the user menu-selected from." + (interactive) + (let* ((file (semantic-dependency-tag-file (semantic-current-tag)))) + (semanticdb-file-table-object file) + (semantic-decoration-unparsed-include-do-reset))) + + +(defun semantic-decoration-unparsed-include-parse-all-includes () + "Parse the include file the user menu-selected from." + (interactive) + (semanticdb-find-translate-path nil nil) + ) + + +;;; General Includes Information +;; +(defun semantic-decoration-all-include-summary () + "Provide a general summary for the state of all includes." + (interactive) + (require 'semantic/dep) + (let* ((table semanticdb-current-table) + (tags (semantic-fetch-tags)) + (inc (semantic-find-tags-by-class 'include table)) + ) + (with-output-to-temp-buffer (help-buffer) ;"*Help*" + (help-setup-xref (list #'semantic-decoration-all-include-summary) + (interactive-p)) + + (princ "Include Summary for File: ") + (princ (file-truename (buffer-file-name))) + (princ "\n") + + (when (oref table db-refs) + (princ "\nExternal Database References to this buffer:") + (dolist (r (oref table db-refs)) + (princ "\n ") + (princ (oref r file))) + ) + + (princ (format "\nThis file contains %d tags, %d of which are includes.\n" + (length tags) (length inc))) + (let ((ok 0) + (unknown 0) + (unparsed 0) + (all 0)) + (dolist (i inc) + (let* ((fileinner (semantic-dependency-tag-file i)) + (tableinner (when fileinner + (semanticdb-file-table-object fileinner t)))) + (cond ((not fileinner) + (setq unknown (1+ unknown))) + ((number-or-marker-p (oref tableinner pointmax)) + (setq ok (1+ ok))) + (t + (setq unparsed (1+ unparsed)))))) + (setq all (+ ok unknown unparsed)) + (when (not (= 0 all)) + (princ (format " Unknown Includes: %d\n" unknown)) + (princ (format " Unparsed Includes: %d\n" unparsed)) + (princ (format " Parsed Includes: %d\n" ok))) + ) + + (princ "\nInclude Path Summary:\n\n") + (when (and (boundp 'ede-object) + (boundp 'ede-object-project) + ede-object) + (princ " This file's project include search is handled by the EDE object:\n") + (princ " Buffer Target: ") + (princ (object-print ede-object)) + (princ "\n") + (when (not (eq ede-object ede-object-project)) + (princ " Buffer Project: ") + (princ (object-print ede-object-project)) + (princ "\n") + ) + (when ede-object-project + (let ((loc (ede-get-locator-object ede-object-project))) + (princ " Backup in-project Locator: ") + (princ (object-print loc)) + (princ "\n"))) + (let ((syspath (ede-system-include-path ede-object-project))) + (if (not syspath) + (princ " EDE Project system include path: Empty\n") + (princ " EDE Project system include path:\n") + (dolist (dir syspath) + (princ " ") + (princ dir) + (princ "\n")) + ))) + + (princ "\n This file's system include path is:\n") + (dolist (dir semantic-dependency-system-include-path) + (princ " ") + (princ dir) + (princ "\n")) + + (let ((unk semanticdb-find-lost-includes)) + (when unk + (princ "\nAll unknown includes:\n") + (dolist (tag unk) + (princ " ") + (princ (semantic-tag-name tag)) + (princ "\n")) + )) + + (let* ((semanticdb-find-default-throttle + (if (featurep 'semantic/db-find) + (remq 'unloaded semanticdb-find-default-throttle) + nil)) + (path (semanticdb-find-translate-path nil nil))) + (if (<= (length path) (length inc)) + (princ "\nThere are currently no includes found recursively.\n") + ;; List the full include list. + (princ "\nSummary of all includes needed by ") + (princ (buffer-name)) + (dolist (p path) + (if (slot-boundp p 'tags) + (princ (format "\n %s :\t%d tags, %d are includes. %s" + (object-name-string p) + (length (oref p tags)) + (length (semantic-find-tags-by-class + 'include p)) + (cond + ((condition-case nil + (oref p dirty) + (error nil)) + " dirty.") + ((not (number-or-marker-p (oref table pointmax))) + " Needs to be parsed.") + (t "")))) + (princ (format "\n %s :\tUnparsed" + (object-name-string p)))) + ))) + ))) + + +;;; Unparsed Include Features +;; +;; This section handles changing states of unparsed include +;; decorations base on what happens in other files. +;; + +(defclass semantic-decoration-unparsed-include-cache (semanticdb-abstract-cache) + () + "Class used to reset decorated includes. +When an include's referring file is parsed, we need to undecorate +any decorated referring includes.") + + +(defmethod semantic-reset ((obj semantic-decoration-unparsed-include-cache)) + "Reset OBJ back to it's empty settings." + (let ((table (oref obj table))) + ;; This is a hack. Add in something better? + (semanticdb-notify-references + table (lambda (tab me) + (semantic-decoration-unparsed-include-refrence-reset tab) + )) + )) + +(defmethod semanticdb-partial-synchronize ((cache semantic-decoration-unparsed-include-cache) + new-tags) + "Synchronize CACHE with some NEW-TAGS." + (if (semantic-find-tags-by-class 'include new-tags) + (semantic-reset cache))) + +(defmethod semanticdb-synchronize ((cache semantic-decoration-unparsed-include-cache) + new-tags) + "Synchronize a CACHE with some NEW-TAGS." + (semantic-reset cache)) + +(defun semantic-decoration-unparsed-include-refrence-reset (table) + "Refresh any highlighting in buffers referred to by TABLE. +If TABLE is not in a buffer, do nothing." + ;; This cache removal may seem odd in that we are "creating one", but + ;; since we cant get in the fcn unless one exists, this ought to be + ;; ok. + (let ((c (semanticdb-cache-get + table 'semantic-decoration-unparsed-include-cache))) + (semanticdb-cache-remove table c)) + + (let ((buf (semanticdb-in-buffer-p table))) + (when buf + (semantic-decorate-add-pending-decoration + 'semantic-decoration-unparsed-include-do-reset + buf) + ))) + +;;;###autoload +(defun semantic-decoration-unparsed-include-do-reset () + "Do a reset of unparsed includes in the current buffer." + (let* ((style (assoc "semantic-decoration-on-includes" + semantic-decoration-styles))) + (when (cdr style) + (let ((allinc (semantic-find-tags-included + (semantic-fetch-tags-fast)))) + ;; This will do everything, but it should be speedy since it + ;; would have been done once already. + (semantic-decorate-add-decorations allinc) + )))) + + +(provide 'semantic/decorate/include) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/decorate/include" +;; End: + +;;; semantic/decorate/include.el ends here diff --git a/lisp/cedet/semantic/decorate/mode.el b/lisp/cedet/semantic/decorate/mode.el new file mode 100644 index 00000000000..66c7c1224f8 --- /dev/null +++ b/lisp/cedet/semantic/decorate/mode.el @@ -0,0 +1,567 @@ +;;; semantic/decorate/mode.el --- Minor mode for decorating tags + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008 +;;; 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: +;; +;; A minor mode for use in decorating tags. +;; +;; There are two types of decorations that can be performed on a tag. +;; You can either highlight the full tag, or you can add an +;; independent decoration on some part of the tag body. +;; +;; For independent decoration in particular, managing them so that they +;; do not get corrupted is challenging. This major mode and +;; corresponding macros will make handling those types of decorations +;; easier. +;; + +;;; Code: +(require 'semantic) +(require 'semantic/decorate) +(require 'semantic/tag-ls) +(require 'semantic/util-modes) + +;;; Styles List +;; +(defcustom semantic-decoration-styles nil + "List of active decoration styles. +It is an alist of \(NAME . FLAG) elements, where NAME is a style name +and FLAG is non-nil if the style is enabled. +See also `define-semantic-decoration-style' which will automatically +add items to this list." + :group 'semantic + :type '(repeat (cons (string :tag "Decoration Name") + (boolean :tag "Enabled"))) + ) + +;;; Misc. +;; +(defsubst semantic-decorate-style-predicate (style) + "Return the STYLE's predicate function." + (intern (format "%s-p" style))) + +(defsubst semantic-decorate-style-highlighter (style) + "Return the STYLE's highlighter function." + (intern (format "%s-highlight" style))) + +;;; Base decoration API +;; +(defsubst semantic-decoration-p (object) + "Return non-nil if OBJECT is a tag decoration." + (and (semantic-overlay-p object) + (semantic-overlay-get object 'semantic-decoration))) + +(defsubst semantic-decoration-set-property (deco property value) + "Set the DECO decoration's PROPERTY to VALUE. +Return DECO." + (assert (semantic-decoration-p deco)) + (semantic-overlay-put deco property value) + deco) + +(defsubst semantic-decoration-get-property (deco property) + "Return the DECO decoration's PROPERTY value." + (assert (semantic-decoration-p deco)) + (semantic-overlay-get deco property)) + +(defsubst semantic-decoration-set-face (deco face) + "Set the face of the decoration DECO to FACE. +Return DECO." + (semantic-decoration-set-property deco 'face face)) + +(defsubst semantic-decoration-face (deco) + "Return the face of the decoration DECO." + (semantic-decoration-get-property deco 'face)) + +(defsubst semantic-decoration-set-priority (deco priority) + "Set the priority of the decoration DECO to PRIORITY. +Return DECO." + (assert (natnump priority)) + (semantic-decoration-set-property deco 'priority priority)) + +(defsubst semantic-decoration-priority (deco) + "Return the priority of the decoration DECO." + (semantic-decoration-get-property deco 'priority)) + +(defsubst semantic-decoration-move (deco begin end) + "Move the decoration DECO on the region between BEGIN and END. +Return DECO." + (assert (semantic-decoration-p deco)) + (semantic-overlay-move deco begin end) + deco) + +;;; Tag decoration +;; +(defun semantic-decorate-tag (tag begin end &optional face) + "Add a new decoration on TAG on the region between BEGIN and END. +If optional argument FACE is non-nil, set the decoration's face to +FACE. +Return the overlay that makes up the new decoration." + (let ((deco (semantic-tag-create-secondary-overlay tag))) + ;; We do not use the unlink property because we do not want to + ;; save the highlighting information in the DB. + (semantic-overlay-put deco 'semantic-decoration t) + (semantic-decoration-move deco begin end) + (semantic-decoration-set-face deco face) + deco)) + +(defun semantic-decorate-clear-tag (tag &optional deco) + "Remove decorations from TAG. +If optional argument DECO is non-nil, remove only that decoration." + (assert (or (null deco) (semantic-decoration-p deco))) + ;; Clear primary decorations. + ;; For now, just unhighlight the tag. How to deal with other + ;; primary decorations like invisibility, etc. ? Maybe just + ;; restoring default values will suffice? + (semantic-unhighlight-tag tag) + (semantic-tag-delete-secondary-overlay + tag (or deco 'semantic-decoration))) + +(defun semantic-decorate-tag-decoration (tag) + "Return decoration found on TAG." + (semantic-tag-get-secondary-overlay tag 'semantic-decoration)) + +;;; Global setup of active decorations +;; +(defun semantic-decorate-flush-decorations (&optional buffer) + "Flush decorations found in BUFFER. +BUFFER defaults to the current buffer. +Should be used to flush decorations that might remain in BUFFER, for +example, after tags have been refreshed." + (with-current-buffer (or buffer (current-buffer)) + (dolist (o (semantic-overlays-in (point-min) (point-max))) + (and (semantic-decoration-p o) + (semantic-overlay-delete o))))) + +(defun semantic-decorate-clear-decorations (tag-list) + "Remove decorations found in tags in TAG-LIST." + (dolist (tag tag-list) + (semantic-decorate-clear-tag tag) + ;; recurse over children + (semantic-decorate-clear-decorations + (semantic-tag-components-with-overlays tag)))) + +(defun semantic-decorate-add-decorations (tag-list) + "Add decorations to tags in TAG-LIST. +Also make sure old decorations in the area are completely flushed." + (dolist (tag tag-list) + ;; Cleanup old decorations. + (when (semantic-decorate-tag-decoration tag) + ;; Note on below comment. This happens more as decorations are refreshed + ;; mid-way through their use. Remove the message. + + ;; It would be nice if this never happened, but it still does + ;; once in a while. Print a message to help flush these + ;; situations + ;;(message "Decorations still on %s" (semantic-format-tag-name tag)) + (semantic-decorate-clear-tag tag)) + ;; Add new decorations. + (dolist (style semantic-decoration-styles) + (let ((pred (semantic-decorate-style-predicate (car style))) + (high (semantic-decorate-style-highlighter (car style)))) + (and (cdr style) + (fboundp pred) + (funcall pred tag) + (fboundp high) + (funcall high tag)))) + ;; Recurse on the children of all tags + (semantic-decorate-add-decorations + (semantic-tag-components-with-overlays tag)))) + +;;; PENDING DECORATIONS +;; +;; Activities in Emacs may cause a decoration to change state. Any +;; such identified change ought to be setup as PENDING. This means +;; that the next idle step will do the decoration change, but at the +;; time of the state change, minimal work would be done. +(defvar semantic-decorate-pending-decoration-hook nil + "Normal hook run to perform pending decoration changes.") + +(semantic-varalias-obsolete 'semantic-decorate-pending-decoration-hooks + 'semantic-decorate-pending-decoration-hook) + +(defun semantic-decorate-add-pending-decoration (fcn &optional buffer) + "Add a pending decoration change represented by FCN. +Applies only to the current BUFFER. +The setting of FCN will be removed after it is run." + (save-excursion + (when buffer (set-buffer buffer)) + (semantic-make-local-hook 'semantic-decorate-flush-pending-decorations) + (add-hook 'semantic-decorate-pending-decoration-hook fcn nil t))) + +(defun semantic-decorate-flush-pending-decorations (&optional buffer) + "Flush any pending decorations for BUFFER. +Flush functions from `semantic-decorate-pending-decoration-hook'." + (save-excursion + (when buffer (set-buffer buffer)) + (run-hooks 'semantic-decorate-pending-decoration-hook) + ;; Always reset the hooks + (setq semantic-decorate-pending-decoration-hook nil))) + + +;;; DECORATION MODE +;; +;; Generic mode for handling basic highlighting and decorations. +;; + +(defcustom global-semantic-decoration-mode nil + "*If non-nil, enable global use of command `semantic-decoration-mode'. +When this mode is activated, decorations specified by +`semantic-decoration-styles'." + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic/decorate/mode + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-decoration-mode (if val 1 -1)))) + +;;;###autoload +(defun global-semantic-decoration-mode (&optional arg) + "Toggle global use of option `semantic-decoration-mode'. +Decoration mode turns on all active decorations as specified +by `semantic-decoration-styles'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-decoration-mode + (semantic-toggle-minor-mode-globally + 'semantic-decoration-mode arg))) + +(defcustom semantic-decoration-mode-hook nil + "Hook run at the end of function `semantic-decoration-mode'." + :group 'semantic + :type 'hook) + +;;;;###autoload +(defvar semantic-decoration-mode nil + "Non-nil if command `semantic-decoration-mode' is enabled. +Use the command `semantic-decoration-mode' to change this variable.") +(make-variable-buffer-local 'semantic-decoration-mode) + +(defun semantic-decoration-mode-setup () + "Setup the `semantic-decoration-mode' minor mode. +The minor mode can be turned on only if the semantic feature is available +and the current buffer was set up for parsing. Return non-nil if the +minor mode is enabled." + (if semantic-decoration-mode + (if (not (and (featurep 'semantic) (semantic-active-p))) + (progn + ;; Disable minor mode if semantic stuff not available + (setq semantic-decoration-mode nil) + (error "Buffer %s was not set up for parsing" + (buffer-name))) + ;; Add hooks + (semantic-make-local-hook 'semantic-after-partial-cache-change-hook) + (add-hook 'semantic-after-partial-cache-change-hook + 'semantic-decorate-tags-after-partial-reparse nil t) + (semantic-make-local-hook 'semantic-after-toplevel-cache-change-hook) + (add-hook 'semantic-after-toplevel-cache-change-hook + 'semantic-decorate-tags-after-full-reparse nil t) + ;; Add decorations to available tags. The above hooks ensure + ;; that new tags will be decorated when they become available. + (semantic-decorate-add-decorations (semantic-fetch-available-tags)) + ) + ;; Remove decorations from available tags. + (semantic-decorate-clear-decorations (semantic-fetch-available-tags)) + ;; Cleanup any leftover crap too. + (semantic-decorate-flush-decorations) + ;; Remove hooks + (remove-hook 'semantic-after-partial-cache-change-hook + 'semantic-decorate-tags-after-partial-reparse t) + (remove-hook 'semantic-after-toplevel-cache-change-hook + 'semantic-decorate-tags-after-full-reparse t) + ) + semantic-decoration-mode) + +(defun semantic-decoration-mode (&optional arg) + "Minor mode for decorating tags. +Decorations are specified in `semantic-decoration-styles'. +You can define new decoration styles with +`define-semantic-decoration-style'. +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." +;; +;;\\{semantic-decoration-map}" + (interactive + (list (or current-prefix-arg + (if semantic-decoration-mode 0 1)))) + (setq semantic-decoration-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-decoration-mode))) + (semantic-decoration-mode-setup) + (run-hooks 'semantic-decoration-mode-hook) + (if (interactive-p) + (message "decoration-mode minor mode %sabled" + (if semantic-decoration-mode "en" "dis"))) + (semantic-mode-line-update) + semantic-decoration-mode) + +(semantic-add-minor-mode 'semantic-decoration-mode + "" + nil) + +(defun semantic-decorate-tags-after-full-reparse (tag-list) + "Add decorations after a complete reparse of the current buffer. +TAG-LIST is the list of tags recently parsed. +Flush all existing decorations and call `semantic-decorate-add-decorations' to +add decorations. +Called from `semantic-after-toplevel-cache-change-hook'." + ;; Flush everything + (semantic-decorate-flush-decorations) + ;; Add it back on + (semantic-decorate-add-decorations tag-list)) + +(defun semantic-decorate-tags-after-partial-reparse (tag-list) + "Add decorations when new tags are created in the current buffer. +TAG-LIST is the list of newly created tags. +Call `semantic-decorate-add-decorations' to add decorations. +Called from `semantic-after-partial-cache-change-hook'." + (semantic-decorate-add-decorations tag-list)) + + +;;; Enable/Disable toggling +;; +(defun semantic-decoration-style-enabled-p (style) + "Return non-nil if STYLE is currently enabled. +Return nil if the style is disabled, or does not exist." + (let ((pair (assoc style semantic-decoration-styles))) + (and pair (cdr pair)))) + +(defun semantic-toggle-decoration-style (name &optional arg) + "Turn on/off the decoration style with NAME. +Decorations are specified in `semantic-decoration-styles'. +With prefix argument ARG, turn on if positive, otherwise off. +Return non-nil if the decoration style is enabled." + (interactive + (list (completing-read "Decoration style: " + semantic-decoration-styles nil t) + current-prefix-arg)) + (setq name (format "%s" name)) ;; Ensure NAME is a string. + (unless (equal name "") + (let* ((style (assoc name semantic-decoration-styles)) + (flag (if arg + (> (prefix-numeric-value arg) 0) + (not (cdr style))))) + (unless (eq (cdr style) flag) + ;; Store the new flag. + (setcdr style flag) + ;; Refresh decorations is `semantic-decoration-mode' is on. + (when semantic-decoration-mode + (semantic-decoration-mode -1) + (semantic-decoration-mode 1)) + (when (interactive-p) + (message "Decoration style %s turned %s" (car style) + (if flag "on" "off")))) + flag))) + +(defvar semantic-decoration-menu-cache nil + "Cache of the decoration menu.") + +(defun semantic-decoration-build-style-menu (style) + "Build a menu item for controlling a specific decoration STYLE." + (vector (car style) + `(lambda () (interactive) + (semantic-toggle-decoration-style + ,(car style))) + :style 'toggle + :selected `(semantic-decoration-style-enabled-p ,(car style)) + )) + +(defun semantic-build-decoration-mode-menu (&rest ignore) + "Create a menu listing all the known decorations for toggling. +IGNORE any input arguments." + (or semantic-decoration-menu-cache + (setq semantic-decoration-menu-cache + (mapcar 'semantic-decoration-build-style-menu + (reverse semantic-decoration-styles)) + ))) + + +;;; Defining decoration styles +;; +(defmacro define-semantic-decoration-style (name doc &rest flags) + "Define a new decoration style with NAME. +DOC is a documentation string describing the decoration style NAME. +It is appended to auto-generated doc strings. +An Optional list of FLAGS can also be specified. Flags are: + :enabled <value> - specify the default enabled value for NAME. + + +This defines two new overload functions respectively called `NAME-p' +and `NAME-highlight', for which you must provide a default +implementation in respectively the functions `NAME-p-default' and +`NAME-highlight-default'. Those functions are passed a tag. `NAME-p' +must return non-nil to indicate that the tag should be decorated by +`NAME-highlight'. + +To put primary decorations on a tag `NAME-highlight' must use +functions like `semantic-set-tag-face', `semantic-set-tag-intangible', +etc., found in the semantic-decorate library. + +To add other kind of decorations on a tag, `NAME-highlight' must use +`semantic-decorate-tag', and other functions of the semantic +decoration API found in this library." + (let ((predicate (semantic-decorate-style-predicate name)) + (highlighter (semantic-decorate-style-highlighter name)) + (defaultenable (if (plist-member flags :enabled) + (plist-get flags :enabled) + t)) + ) + `(progn + ;; Clear the menu cache so that new items are added when + ;; needed. + (setq semantic-decoration-menu-cache nil) + ;; Create an override method to specify if a given tag belongs + ;; to this type of decoration + (define-overloadable-function ,predicate (tag) + ,(format "Return non-nil to decorate TAG with `%s' style.\n%s" + name doc)) + ;; Create an override method that will perform the highlight + ;; operation if the -p method returns non-nil. + (define-overloadable-function ,highlighter (tag) + ,(format "Decorate TAG with `%s' style.\n%s" + name doc)) + ;; Add this to the list of primary decoration modes. + (add-to-list 'semantic-decoration-styles + (cons ',(symbol-name name) + ,defaultenable)) + ))) + +;;; Predefined decoration styles +;; + +;;; Tag boundaries highlighting +;; +(define-semantic-decoration-style semantic-tag-boundary + "Place an overline in front of each long tag. +Does not provide overlines for prototypes.") + +(defface semantic-tag-boundary-face + '((((class color) (background dark)) + (:overline "cyan")) + (((class color) (background light)) + (:overline "blue"))) + "*Face used to show long tags in. +Used by decoration style: `semantic-tag-boundary'." + :group 'semantic-faces) + +(defun semantic-tag-boundary-p-default (tag) + "Return non-nil if TAG is a type, or a non-prototype function." + (let ((c (semantic-tag-class tag))) + (and + (or + ;; All types get a line? + (eq c 'type) + ;; Functions which aren't prototypes get a line. + (and (eq c 'function) + (not (semantic-tag-get-attribute tag :prototype-flag))) + ) + ;; Note: The below restriction confused users. + ;; + ;; Nothing smaller than a few lines + ;;(> (- (semantic-tag-end tag) (semantic-tag-start tag)) 150) + ;; Random truth + t) + )) + +(defun semantic-tag-boundary-highlight-default (tag) + "Highlight the first line of TAG as a boundary." + (when (bufferp (semantic-tag-buffer tag)) + (with-current-buffer (semantic-tag-buffer tag) + (semantic-decorate-tag + tag + (semantic-tag-start tag) + (save-excursion + (goto-char (semantic-tag-start tag)) + (end-of-line) + (forward-char 1) + (point)) + 'semantic-tag-boundary-face)) + )) + +;;; Private member highlighting +;; +(define-semantic-decoration-style semantic-decoration-on-private-members + "Highlight class members that are designated as PRIVATE access." + :enabled nil) + +(defface semantic-decoration-on-private-members-face + '((((class color) (background dark)) + (:background "#200000")) + (((class color) (background light)) + (:background "#8fffff"))) + "*Face used to show privately scoped tags in. +Used by the decoration style: `semantic-decoration-on-private-members'." + :group 'semantic-faces) + +(defun semantic-decoration-on-private-members-highlight-default (tag) + "Highlight TAG as designated to have PRIVATE access. +Use a primary decoration." + (semantic-set-tag-face + tag 'semantic-decoration-on-private-members-face)) + +(defun semantic-decoration-on-private-members-p-default (tag) + "Return non-nil if TAG has PRIVATE access." + (and (member (semantic-tag-class tag) '(function variable)) + (eq (semantic-tag-protection tag) 'private))) + +;;; Protected member highlighting +;; +(defface semantic-decoration-on-protected-members-face + '((((class color) (background dark)) + (:background "#000020")) + (((class color) (background light)) + (:background "#fffff8"))) + "*Face used to show protected scoped tags in. +Used by the decoration style: `semantic-decoration-on-protected-members'." + :group 'semantic-faces) + +(define-semantic-decoration-style semantic-decoration-on-protected-members + "Highlight class members that are designated as PROTECTED access." + :enabled nil) + +(defun semantic-decoration-on-protected-members-p-default (tag) + "Return non-nil if TAG has PROTECTED access." + (and (member (semantic-tag-class tag) '(function variable)) + (eq (semantic-tag-protection tag) 'protected))) + +(defun semantic-decoration-on-protected-members-highlight-default (tag) + "Highlight TAG as designated to have PROTECTED access. +Use a primary decoration." + (semantic-set-tag-face + tag 'semantic-decoration-on-protected-members-face)) + +(provide 'semantic/decorate/mode) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/decorate/mode" +;; End: + +;;; semantic/decorate/mode.el ends here diff --git a/lisp/cedet/semantic/dep.el b/lisp/cedet/semantic/dep.el new file mode 100644 index 00000000000..d4b17744d06 --- /dev/null +++ b/lisp/cedet/semantic/dep.el @@ -0,0 +1,234 @@ +;;; semantic/dep.el --- Methods for tracking dependencies (include files) + +;;; Copyright (C) 2006, 2007, 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: +;; +;; Include tags (dependencies for a given source file) usually have +;; some short name. The target file that it is dependent on is +;; generally found on some sort of path controlled by the compiler or +;; project. +;; +;; EDE or even ECB can control our project dependencies, and help us +;; find file within the setting of a given project. For system +;; dependencies, we need to depend on user supplied lists, which can +;; manifest themselves in the form of system datatabases (from +;; semanticdb.) +;; +;; Provide ways to track these different files here. + +(require 'semantic/tag) + +;;; Code: + +(defvar semantic-dependency-include-path nil + "Defines the include path used when searching for files. +This should be a list of directories to search which is specific +to the file being included. + +If `semantic-dependency-tag-file' is overridden for a given +language, this path is most likely ignored. + +The above function, reguardless of being overriden, caches the +located dependency file location in the tag property +`dependency-file'. If you override this function, you do not +need to implement your own cache. Each time the buffer is fully +reparsed, the cache will be reset. + +TODO: use ffap.el to locate such items? + +NOTE: Obsolete this, or use as special user") +(make-variable-buffer-local `semantic-dependency-include-path) + +(defvar semantic-dependency-system-include-path nil + "Defines the system include path. +This should be set with either `defvar-mode-local', or with +`semantic-add-system-include'. + +For mode authors, use +`defcustom-mode-local-semantic-dependency-system-include-path' +to create a mode-specific variable to control this. + +When searching for a file associated with a name found in an tag of +class include, this path will be inspected for includes of type +`system'. Some include tags are agnostic to this setting and will +check both the project and system directories.") +(make-variable-buffer-local `semantic-dependency-system-include-path) + +(defmacro defcustom-mode-local-semantic-dependency-system-include-path + (mode name value &optional docstring) + "Create a mode-local value of the system-dependency include path. +MODE is the `major-mode' this name/value pairs is for. +NAME is the name of the customizable value users will use. +VALUE is the path (a list of strings) to add. +DOCSTRING is a documentation string applied to the variable NAME +users will customize. + +Creates a customizable variable users can customize that will +keep semantic data structures up to date." + `(progn + ;; Create a variable users can customize. + (defcustom ,name ,value + ,docstring + :group (quote ,(intern (car (split-string (symbol-name mode) "-")))) + :group 'semantic + :type '(repeat (directory :tag "Directory")) + :set (lambda (sym val) + (set-default sym val) + (setq-mode-local ,mode + semantic-dependency-system-include-path + val) + (when (fboundp + 'semantic-decoration-unparsed-include-do-reset) + (mode-local-map-mode-buffers + 'semantic-decoration-unparsed-include-do-reset + (quote ,mode)))) + ) + ;; Set the variable to the default value. + (defvar-mode-local ,mode semantic-dependency-system-include-path + ,name + "System path to search for include files.") + ;; Bind NAME onto our variable so tools can customize it + ;; without knowing about it. + (put 'semantic-dependency-system-include-path + (quote ,mode) (quote ,name)) + )) + +;;; PATH MANAGEMENT +;; +;; Some fcns to manage paths for a give mode. +;;;###autoload +(defun semantic-add-system-include (dir &optional mode) + "Add a system include DIR to path for MODE. +Modifies a mode-local version of `semantic-dependency-system-include-path'. + +Changes made by this function are not persistent." + (interactive "DNew Include Directory: ") + (if (not mode) (setq mode major-mode)) + (let ((dirtmp (file-name-as-directory dir)) + (value + (mode-local-value mode 'semantic-dependency-system-include-path)) + ) + (add-to-list 'value dirtmp t) + (eval `(setq-mode-local ,mode + semantic-dependency-system-include-path value)) + )) + +;;;###autoload +(defun semantic-remove-system-include (dir &optional mode) + "Add a system include DIR to path for MODE. +Modifies a mode-local version of`semantic-dependency-system-include-path'. + +Changes made by this function are not persistent." + (interactive (list + (completing-read + "Include Directory to Remove: " + semantic-dependency-system-include-path)) + ) + (if (not mode) (setq mode major-mode)) + (let ((dirtmp (file-name-as-directory dir)) + (value + (mode-local-value mode 'semantic-dependency-system-include-path)) + ) + (setq value (delete dirtmp value)) + (eval `(setq-mode-local ,mode semantic-dependency-system-include-path + value)) + )) + +;;;###autoload +(defun semantic-reset-system-include (&optional mode) + "Reset the system include list to empty for MODE. +Modifies a mode-local version of +`semantic-dependency-system-include-path'." + (interactive) + (if (not mode) (setq mode major-mode)) + (eval `(setq-mode-local ,mode semantic-dependency-system-include-path + nil)) + ) + +;;;###autoload +(defun semantic-customize-system-include-path (&optional mode) + "Customize the include path for this `major-mode'. +To create a customizable include path for a major MODE, use the +macro `defcustom-mode-local-semantic-dependency-system-include-path'." + (interactive) + (let ((ips (get 'semantic-dependency-system-include-path + (or mode major-mode)))) + ;; Do we have one? + (when (not ips) + (error "There is no customizable includepath variable for %s" + (or mode major-mode))) + ;; Customize it. + (customize-variable ips))) + +;;; PATH SEARCH +;; +;; methods for finding files on a provided path. +(defmacro semantic--dependency-find-file-on-path (file path) + (if (fboundp 'locate-file) + `(locate-file ,file ,path) + `(let ((p ,path) + (found nil)) + (while (and p (not found)) + (let ((f (expand-file-name ,file (car p)))) + (if (file-exists-p f) + (setq found f))) + (setq p (cdr p))) + found))) + +(defvar ede-minor-mode) +(defvar ede-object) +(declare-function ede-system-include-path "ede") + +(defun semantic-dependency-find-file-on-path (file systemp &optional mode) + "Return an expanded file name for FILE on available paths. +If SYSTEMP is true, then only search system paths. +If optional argument MODE is non-nil, then derive paths from the +provided mode, not from the current major mode." + (if (not mode) (setq mode major-mode)) + (let ((sysp (mode-local-value + mode 'semantic-dependency-system-include-path)) + (edesys (when (and (featurep 'ede) ede-minor-mode + ede-object) + (ede-system-include-path ede-object))) + (locp (mode-local-value + mode 'semantic-dependency-include-path)) + (found nil)) + (when (file-exists-p file) + (setq found file)) + (when (and (not found) (not systemp)) + (setq found (semantic--dependency-find-file-on-path file locp))) + (when (and (not found) edesys) + (setq found (semantic--dependency-find-file-on-path file edesys))) + (when (not found) + (setq found (semantic--dependency-find-file-on-path file sysp))) + (if found (expand-file-name found)))) + + +(provide 'semantic/dep) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/dep" +;; End: + +;;; semantic/dep.el ends here diff --git a/lisp/cedet/semantic/doc.el b/lisp/cedet/semantic/doc.el new file mode 100644 index 00000000000..9feeee294f6 --- /dev/null +++ b/lisp/cedet/semantic/doc.el @@ -0,0 +1,129 @@ +;;; semantic/doc.el --- Routines for documentation strings + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005, 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: +;; +;; It is good practice to write documenation for your functions and +;; variables. These core routines deal with these documentation +;; comments or strings. They can exist either as a tag property +;; (:documentation) or as a comment just before the symbol, or after +;; the symbol on the same line. + +(require 'semantic/tag) + +;;; Code: + +;;;###autoload +(define-overloadable-function semantic-documentation-for-tag (&optional tag nosnarf) + "Find documentation from TAG and return it as a clean string. +TAG might have DOCUMENTATION set in it already. If not, there may be +some documentation in a comment preceding TAG's definition which we +can look for. When appropriate, this can be overridden by a language specific +enhancement. +Optional argument NOSNARF means to only return the lexical analyzer token for it. +If nosnarf if 'lex, then only return the lex token." + (if (not tag) (setq tag (semantic-current-tag))) + (save-excursion + (when (semantic-tag-with-position-p tag) + (set-buffer (semantic-tag-buffer tag))) + (:override + ;; No override. Try something simple to find documentation nearby + (save-excursion + (semantic-go-to-tag tag) + (let ((doctmp (semantic-tag-docstring tag (current-buffer)))) + (or + ;; Is there doc in the tag??? + doctmp + ;; Check just before the definition. + (when (semantic-tag-with-position-p tag) + (semantic-documentation-comment-preceeding-tag tag nosnarf)) + ;; Lets look for comments either after the definition, but before code: + ;; Not sure yet. Fill in something clever later.... + nil)))))) + +(defun semantic-documentation-comment-preceeding-tag (&optional tag nosnarf) + "Find a comment preceeding TAG. +If TAG is nil. use the tag under point. +Searches the space between TAG and the preceeding tag for a comment, +and converts the comment into clean documentation. +Optional argument NOSNARF with a value of 'lex means to return +just the lexical token and not the string." + (if (not tag) (setq tag (semantic-current-tag))) + (save-excursion + ;; Find this tag. + (semantic-go-to-tag tag) + (let* ((starttag (semantic-find-tag-by-overlay-prev + (semantic-tag-start tag))) + (start (if starttag + (semantic-tag-end starttag) + (point-min)))) + (when (re-search-backward comment-start-skip start t) + ;; We found a comment that doesn't belong to the body + ;; of a function. + (semantic-doc-snarf-comment-for-tag nosnarf))) + )) + +(defun semantic-doc-snarf-comment-for-tag (nosnarf) + "Snarf up the comment at POINT for `semantic-documentation-for-tag'. +Attempt to strip out comment syntactic sugar. +Argument NOSNARF means don't modify the found text. +If NOSNARF is 'lex, then return the lex token." + (let* ((semantic-ignore-comments nil) + (semantic-lex-analyzer #'semantic-comment-lexer)) + (if (memq nosnarf '(lex flex)) ;; keep `flex' for compatibility + (car (semantic-lex (point) (1+ (point)))) + (let ((ct (semantic-lex-token-text + (car (semantic-lex (point) (1+ (point))))))) + (if nosnarf + nil + ;; ok, try to clean the text up. + ;; Comment start thingy + (while (string-match (concat "^\\s-*" comment-start-skip) ct) + (setq ct (concat (substring ct 0 (match-beginning 0)) + (substring ct (match-end 0))))) + ;; Arbitrary punctuation at the beginning of each line. + (while (string-match "^\\s-*\\s.+\\s-*" ct) + (setq ct (concat (substring ct 0 (match-beginning 0)) + (substring ct (match-end 0))))) + ;; End of a block comment. + (if (and (boundp 'block-comment-end) + block-comment-end + (string-match block-comment-end ct)) + (setq ct (concat (substring ct 0 (match-beginning 0)) + (substring ct (match-end 0))))) + ;; In case it's a real string, STRIPIT. + (while (string-match "\\s-*\\s\"+\\s-*" ct) + (setq ct (concat (substring ct 0 (match-beginning 0)) + (substring ct (match-end 0)))))) + ;; Now return the text. + ct)))) + +(provide 'semantic/doc) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/doc" +;; End: + +;;; semantic/doc.el ends here diff --git a/lisp/cedet/semantic/ede-grammar.el b/lisp/cedet/semantic/ede-grammar.el new file mode 100644 index 00000000000..c23b489c837 --- /dev/null +++ b/lisp/cedet/semantic/ede-grammar.el @@ -0,0 +1,202 @@ +;;; semantic/ede-grammar.el --- EDE support for Semantic Grammar Files + +;;; Copyright (C) 2003, 2004, 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> +;; Keywords: project, make + +;; 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: +;; +;; Handle .by or .wy files. + +(require 'semantic) +(require 'ede/proj) +(require 'ede/pmake) +(require 'ede/pconf) +(require 'ede/proj-elisp) +(require 'semantic/grammar) + +;;; Code: +(defclass semantic-ede-proj-target-grammar (ede-proj-target-makefile) + ((menu :initform nil) + (keybindings :initform nil) + (phony :initform t) + (sourcetype :initform + (semantic-ede-source-grammar-wisent + semantic-ede-source-grammar-bovine + )) + (availablecompilers :initform + (semantic-ede-grammar-compiler-wisent + semantic-ede-grammar-compiler-bovine + )) + ) + "This target consists of a group of grammar files. +A grammar target consists of grammar files that build Emacs Lisp programs for +parsing different languages.") + +(defvar semantic-ede-source-grammar-wisent + (ede-sourcecode "semantic-ede-grammar-source-wisent" + :name "Wisent Grammar" + :sourcepattern "\\.wy$" + ) + "Semantic Grammar source code definition for wisent.") + +(defclass semantic-ede-grammar-compiler-class (ede-compiler) + nil + "Specialized compiler for semantic grammars.") + +(defvar semantic-ede-grammar-compiler-wisent + (semantic-ede-grammar-compiler-class + "ede-emacs-wisent-compiler" + :name "emacs" + :variables '(("EMACS" . "emacs")) + :commands + '( + "@echo \"(add-to-list 'load-path nil)\" > grammar-make-script" + "@for loadpath in . ${LOADPATH}; do \\" + " echo \"(add-to-list 'load-path \\\"$$loadpath\\\")\" >> grammar-make-script; \\" + "done;" + "@echo \"(require 'semantic-load)\" >> grammar-make-script" + "@echo \"(require 'semantic-grammar)\" >> grammar-make-script" + ;; "@echo \"(setq debug-on-error t)\" >> grammar-make-script" + "\"$(EMACS)\" -batch --no-site-file -l grammar-make-script -f semantic-grammar-batch-build-packages $^" + ) + ;; :autoconf '("AM_PATH_LISPDIR") + :sourcetype '(semantic-ede-source-grammar-wisent) + :objectextention "-wy.elc" + ) + "Compile Emacs Lisp programs.") + + +(defvar semantic-ede-source-grammar-bovine + (ede-sourcecode "semantic-ede-grammar-source-bovine" + :name "Bovine Grammar" + :sourcepattern "\\.by$" + ) + "Semantic Grammar source code definition for the bovinator.") + +(defvar semantic-ede-grammar-compiler-bovine + (semantic-ede-grammar-compiler-class + "ede-emacs-wisent-compiler" + :name "emacs" + :variables '(("EMACS" . "emacs")) + :commands + '( + "@echo \"(add-to-list 'load-path nil)\" > grammar-make-script" + "@for loadpath in . ${LOADPATH}; do \\" + " echo \"(add-to-list 'load-path \\\"$$loadpath\\\")\" >> grammar-make-script; \\" + "done;" + "@echo \"(require 'semantic-load)\" >> grammar-make-script" + "@echo \"(require 'semantic-grammar)\" >> grammar-make-script" + ;; "@echo \"(setq debug-on-error t)\" >> grammar-make-script" + "\"$(EMACS)\" -batch --no-site-file -l grammar-make-script -f semantic-grammar-batch-build-packages $^" + ) + ;; :autoconf '("AM_PATH_LISPDIR") + :sourcetype '(semantic-ede-source-grammar-bovine) + :objectextention "-by.elc" + ) + "Compile Emacs Lisp programs.") + +;;; Target options. +(defmethod ede-buffer-mine ((this semantic-ede-proj-target-grammar) buffer) + "Return t if object THIS lays claim to the file in BUFFER. +Lays claim to all -by.el, and -wy.el files." + ;; We need to be a little more careful than this, but at the moment it + ;; is common to have only one target of this class per directory. + (if (string-match "-[bw]y\\.elc?$" (buffer-file-name buffer)) + t + (call-next-method) ; The usual thing. + )) + +(defmethod project-compile-target ((obj semantic-ede-proj-target-grammar)) + "Compile all sources in a Lisp target OBJ." + (let* ((cb (current-buffer)) + (proj (ede-target-parent obj)) + (default-directory (oref proj directory))) + (mapc (lambda (src) + (save-excursion + (set-buffer (find-file-noselect src)) + (save-excursion + (semantic-grammar-create-package)) + (save-buffer) + (let ((cf (concat (semantic-grammar-package) ".el"))) + (if (or (not (file-exists-p cf)) + (file-newer-than-file-p src cf)) + (byte-compile-file cf))))) + (oref obj source))) + (message "All Semantic Grammar sources are up to date in %s" (object-name obj))) + +;;; Makefile generation functions +;; +(defmethod ede-proj-makefile-sourcevar ((this semantic-ede-proj-target-grammar)) + "Return the variable name for THIS's sources." + (cond ((ede-proj-automake-p) + (error "No Automake support for Semantic Grammars")) + (t (concat (ede-pmake-varname this) "_SEMANTIC_GRAMMAR")))) + +(defmethod ede-proj-makefile-insert-variables :AFTER ((this semantic-ede-proj-target-grammar)) + "Insert variables needed by target THIS." + (ede-proj-makefile-insert-loadpath-items + (ede-proj-elisp-packages-to-loadpath + (list "eieio" "semantic" "inversion" "ede"))) + ;; eieio for object system needed in ede + ;; semantic because it is + ;; Inversion for versioning system. + ;; ede for project regeneration + (ede-pmake-insert-variable-shared + (concat (ede-pmake-varname this) "_SEMANTIC_GRAMMAR_EL") + (insert + (mapconcat (lambda (src) + (save-excursion + (set-buffer (find-file-noselect src)) + (concat (semantic-grammar-package) ".el"))) + (oref this source) + " "))) + ) + +(defmethod ede-proj-makefile-insert-rules ((this semantic-ede-proj-target-grammar)) + "Insert rules needed by THIS target." + ;; Add in some dependencies. +;; (mapc (lambda (src) +;; (let ((nm (file-name-sans-extension src))) +;; (insert nm "-wy.el: " src "\n" +;; nm "-wy.elc: " nm "-wy.el\n\n") +;; )) +;; (oref this source)) + ;; Call the normal insertion of rules. + (call-next-method) + ) + +(defmethod ede-proj-makefile-insert-dist-dependencies ((this semantic-ede-proj-target-grammar)) + "Insert dist dependencies, or intermediate targets. +This makes sure that all grammar lisp files are created before the dist +runs, so they are always up to date. +Argument THIS is the target that should insert stuff." + (call-next-method) + (insert " $(" (ede-pmake-varname this) "_SEMANTIC_GRAMMAR_EL)") + ) + +;; (autoload 'ede-proj-target-elisp "ede/proj-elisp" +;; "Target class for Emacs/Semantic grammar files." nil nil) + +(ede-proj-register-target "semantic grammar" + semantic-ede-proj-target-grammar) + +(provide 'semantic/ede-grammar) + +;;; semantic/ede-grammar.el ends here diff --git a/lisp/cedet/semantic/edit.el b/lisp/cedet/semantic/edit.el new file mode 100644 index 00000000000..cb573e35c1e --- /dev/null +++ b/lisp/cedet/semantic/edit.el @@ -0,0 +1,972 @@ +;;; semantic/edit.el --- Edit Management for Semantic + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; In Semantic 1.x, changes were handled in a simplistic manner, where +;; tags that changed were reparsed one at a time. Any other form of +;; edit were managed through a full reparse. +;; +;; This code attempts to minimize the number of times a full reparse +;; needs to occur. While overlays and tags will continue to be +;; recycled in the simple case, new cases where tags are inserted +;; or old tags removed from the original list are handled. +;; + +;;; NOTES FOR IMPROVEMENT +;; +;; Work done by the incremental parser could be improved by the +;; following: +;; +;; 1. Tags created could have as a property an overlay marking a region +;; of themselves that can be edited w/out affecting the definition of +;; that tag. +;; +;; 2. Tags w/ positioned children could have a property of an +;; overlay marking the region in themselves that contain the +;; children. This could be used to better improve splicing near +;; the beginning and end of the child lists. +;; + +;;; BUGS IN INCREMENTAL PARSER +;; +;; 1. Changes in the whitespace between tags could extend a +;; following tag. These will be marked as merely unmatched +;; syntax instead. +;; +;; 2. Incremental parsing while a new function is being typed in +;; somtimes gets a chance only when lists are incomplete, +;; preventing correct context identification. + +;; +(require 'semantic) + +;;; Code: +(defvar semantic-after-partial-cache-change-hook nil + "Normal hook run after the buffer cache has been updated. + +This hook will run when the cache has been partially reparsed. +Partial reparses are incurred when a user edits a buffer, and only the +modified sections are rescanned. + +Hook functions must take one argument, which is the list of tags +updated in the current buffer. + +For language specific hooks, make sure you define this as a local hook.") + +(defvar semantic-change-hooks + '(semantic-edits-change-function-handle-changes) + "Abnormal hook run when semantic detects a change in a buffer. +Each hook function must take three arguments, identical to the +common hook `after-change-functions'.") + +(defvar semantic-reparse-needed-change-hook nil + "Hooks run when a user edit is detected as needing a reparse. +For language specific hooks, make sure you define this as a local +hook. +Not used yet; part of the next generation reparse mechanism") + +(defvar semantic-no-reparse-needed-change-hook nil + "Hooks run when a user edit is detected as not needing a reparse. +If the hook returns non-nil, then declare that a reparse is needed. +For language specific hooks, make sure you define this as a local +hook. +Not used yet; part of the next generation reparse mechanism.") + +(defvar semantic-edits-new-change-hooks nil + "Abnormal hook run when a new change is found. +Functions must take one argument representing an overlay on that change.") + +(defvar semantic-edits-delete-change-hooks nil + "Abnormal hook run before a change overlay is deleted. +Deleted changes occur when multiple changes are merged. +Functions must take one argument representing an overlay being deleted.") + +(defvar semantic-edits-move-change-hook nil + "Abnormal hook run after a change overlay is moved. +Changes move when a new change overlaps an old change. The old change +will be moved. +Functions must take one argument representing an overlay being moved.") + +(defvar semantic-edits-reparse-change-hooks nil + "Abnormal hook run after a change results in a reparse. +Functions are called before the overlay is deleted, and after the +incremental reparse.") + +(defvar semantic-edits-incremental-reparse-failed-hook nil + "Hook run after the incremental parser fails. +When this happens, the buffer is marked as needing a full reprase.") + +(semantic-varalias-obsolete 'semantic-edits-incremental-reparse-failed-hooks + 'semantic-edits-incremental-reparse-failed-hook) + +(defcustom semantic-edits-verbose-flag nil + "Non-nil means the incremental perser is verbose. +If nil, errors are still displayed, but informative messages are not." + :group 'semantic + :type 'boolean) + +;;; Change State management +;; +;; Manage a series of overlays that define changes recently +;; made to the current buffer. +;;;###autoload +(defun semantic-change-function (start end length) + "Provide a mechanism for semantic tag management. +Argument START, END, and LENGTH specify the bounds of the change." + (setq semantic-unmatched-syntax-cache-check t) + (let ((inhibit-point-motion-hooks t) + ) + (run-hook-with-args 'semantic-change-hooks start end length) + )) + +(defun semantic-changes-in-region (start end &optional buffer) + "Find change overlays which exist in whole or in part between START and END. +Optional argument BUFFER is the buffer to search for changes in." + (save-excursion + (if buffer (set-buffer buffer)) + (let ((ol (semantic-overlays-in (max start (point-min)) + (min end (point-max)))) + (ret nil)) + (while ol + (when (semantic-overlay-get (car ol) 'semantic-change) + (setq ret (cons (car ol) ret))) + (setq ol (cdr ol))) + (sort ret #'(lambda (a b) (< (semantic-overlay-start a) + (semantic-overlay-start b))))))) + +(defun semantic-edits-change-function-handle-changes (start end length) + "Run whenever a buffer controlled by `semantic-mode' change. +Tracks when and how the buffer is re-parsed. +Argument START, END, and LENGTH specify the bounds of the change." + ;; We move start/end by one so that we can merge changes that occur + ;; just before, or just after. This lets simple typing capture everything + ;; into one overlay. + (let ((changes-in-change (semantic-changes-in-region (1- start) (1+ end))) + ) + (semantic-parse-tree-set-needs-update) + (if (not changes-in-change) + (let ((o (semantic-make-overlay start end))) + (semantic-overlay-put o 'semantic-change t) + ;; Run the hooks safely. When hooks blow it, our dirty + ;; function will be removed from the list of active change + ;; functions. + (condition-case nil + (run-hook-with-args 'semantic-edits-new-change-hooks o) + (error nil))) + (let ((tmp changes-in-change)) + ;; Find greatest bounds of all changes + (while tmp + (when (< (semantic-overlay-start (car tmp)) start) + (setq start (semantic-overlay-start (car tmp)))) + (when (> (semantic-overlay-end (car tmp)) end) + (setq end (semantic-overlay-end (car tmp)))) + (setq tmp (cdr tmp))) + ;; Move the first found overlay, recycling that overlay. + (semantic-overlay-move (car changes-in-change) start end) + (condition-case nil + (run-hook-with-args 'semantic-edits-move-change-hooks + (car changes-in-change)) + (error nil)) + (setq changes-in-change (cdr changes-in-change)) + ;; Delete other changes. They are now all bound here. + (while changes-in-change + (condition-case nil + (run-hook-with-args 'semantic-edits-delete-change-hooks + (car changes-in-change)) + (error nil)) + (semantic-overlay-delete (car changes-in-change)) + (setq changes-in-change (cdr changes-in-change)))) + ))) + +(defsubst semantic-edits-flush-change (change) + "Flush the CHANGE overlay." + (condition-case nil + (run-hook-with-args 'semantic-edits-delete-change-hooks + change) + (error nil)) + (semantic-overlay-delete change)) + +(defun semantic-edits-flush-changes () + "Flush the changes in the current buffer." + (let ((changes (semantic-changes-in-region (point-min) (point-max)))) + (while changes + (semantic-edits-flush-change (car changes)) + (setq changes (cdr changes)))) + ) + +(defun semantic-edits-change-in-one-tag-p (change hits) + "Return non-nil of the overlay CHANGE exists solely in one leaf tag. +HITS is the list of tags that CHANGE is in. It can have more than +one tag in it if the leaf tag is within a parent tag." + (and (< (semantic-tag-start (car hits)) + (semantic-overlay-start change)) + (> (semantic-tag-end (car hits)) + (semantic-overlay-end change)) + ;; Recurse on the rest. If this change is inside all + ;; of these tags, then they are all leaves or parents + ;; of the smallest tag. + (or (not (cdr hits)) + (semantic-edits-change-in-one-tag-p change (cdr hits)))) + ) + +;;; Change/Tag Query functions +;; +;; A change (region of space) can effect tags in different ways. +;; These functions perform queries on a buffer to determine different +;; ways that a change effects a buffer. +;; +;; NOTE: After debugging these, replace below to no longer look +;; at point and mark (via comments I assume.) +(defsubst semantic-edits-os (change) + "For testing: Start of CHANGE, or smaller of (point) and (mark)." + (if change (semantic-overlay-start change) + (if (< (point) (mark)) (point) (mark)))) + +(defsubst semantic-edits-oe (change) + "For testing: End of CHANGE, or larger of (point) and (mark)." + (if change (semantic-overlay-end change) + (if (> (point) (mark)) (point) (mark)))) + +(defun semantic-edits-change-leaf-tag (change) + "A leaf tag which completely encompasses CHANGE. +If change overlaps a tag, but is not encompassed in it, return nil. +Use `semantic-edits-change-overlap-leaf-tag'. +If CHANGE is completely encompassed in a tag, but overlaps sub-tags, +return nil." + (let* ((start (semantic-edits-os change)) + (end (semantic-edits-oe change)) + (tags (nreverse + (semantic-find-tag-by-overlay-in-region + start end)))) + ;; A leaf is always first in this list + (if (and tags + (<= (semantic-tag-start (car tags)) start) + (> (semantic-tag-end (car tags)) end)) + ;; Ok, we have a match. If this tag has children, + ;; we have to do more tests. + (let ((chil (semantic-tag-components (car tags)))) + (if (not chil) + ;; Simple leaf. + (car tags) + ;; For this type, we say that we encompass it if the + ;; change occurs outside the range of the children. + (if (or (not (semantic-tag-with-position-p (car chil))) + (> start (semantic-tag-end (nth (1- (length chil)) chil))) + (< end (semantic-tag-start (car chil)))) + ;; We have modifications to the definition of this parent + ;; so we have to reparse the whole thing. + (car tags) + ;; We actually modified an area between some children. + ;; This means we should return nil, as that case is + ;; calculated by someone else. + nil))) + nil))) + +(defun semantic-edits-change-between-tags (change) + "Return a cache list of tags surrounding CHANGE. +The returned list is the CONS cell in the master list pointing to +a tag just before CHANGE. The CDR will have the tag just after CHANGE. +CHANGE cannot encompass or overlap a leaf tag. +If CHANGE is fully encompassed in a tag that has children, and +this change occurs between those children, this returns non-nil. +See `semantic-edits-change-leaf-tag' for details on parents." + (let* ((start (semantic-edits-os change)) + (end (semantic-edits-oe change)) + (tags (nreverse + (semantic-find-tag-by-overlay-in-region + start end))) + (list-to-search nil) + (found nil)) + (if (not tags) + (setq list-to-search semantic--buffer-cache) + ;; A leaf is always first in this list + (if (and (< (semantic-tag-start (car tags)) start) + (> (semantic-tag-end (car tags)) end)) + ;; We are completely encompassed in a tag. + (if (setq list-to-search + (semantic-tag-components (car tags))) + ;; Ok, we are completely encompassed within the first tag + ;; entry, AND that tag has children. This means that change + ;; occured outside of all children, but inside some tag + ;; with children. + (if (or (not (semantic-tag-with-position-p (car list-to-search))) + (> start (semantic-tag-end + (nth (1- (length list-to-search)) + list-to-search))) + (< end (semantic-tag-start (car list-to-search)))) + ;; We have modifications to the definition of this parent + ;; and not between it's children. Clear the search list. + (setq list-to-search nil))) + ;; Search list is nil. + )) + ;; If we have a search list, lets go. Otherwise nothing. + (while (and list-to-search (not found)) + (if (cdr list-to-search) + ;; We end when the start of the CDR is after the end of our + ;; asked change. + (if (< (semantic-tag-start (cadr list-to-search)) end) + (setq list-to-search (cdr list-to-search)) + (setq found t)) + (setq list-to-search nil))) + ;; Return it. If it is nil, there is a logic bug, and we need + ;; to avoid this bit of logic anyway. + list-to-search + )) + +(defun semantic-edits-change-over-tags (change) + "Return a cache list of tags surrounding a CHANGE encompassing tags. +CHANGE must not only include all overlapped tags (excepting possible +parent tags) in their entirety. In this case, the change may be deleting +or moving whole tags. +The return value is a vector. +Cell 0 is a list of all tags completely encompassed in change. +Cell 1 is the cons cell into a master parser cache starting with +the cell which occurs BEFORE the first position of CHANGE. +Cell 2 is the parent of cell 1, or nil for the buffer cache. +This function returns nil if any tag covered by change is not +completely encompassed. +See `semantic-edits-change-leaf-tag' for details on parents." + (let* ((start (semantic-edits-os change)) + (end (semantic-edits-oe change)) + (tags (nreverse + (semantic-find-tag-by-overlay-in-region + start end))) + (parent nil) + (overlapped-tags nil) + inner-start inner-end + (list-to-search nil)) + ;; By the time this is already called, we know that it is + ;; not a leaf change, nor a between tag change. That leaves + ;; an overlap, and this condition. + + ;; A leaf is always first in this list. + ;; Is the leaf encompassed in this change? + (if (and tags + (>= (semantic-tag-start (car tags)) start) + (<= (semantic-tag-end (car tags)) end)) + (progn + ;; We encompass one whole change. + (setq overlapped-tags (list (car tags)) + inner-start (semantic-tag-start (car tags)) + inner-end (semantic-tag-end (car tags)) + tags (cdr tags)) + ;; Keep looping while tags are inside the change. + (while (and tags + (>= (semantic-tag-start (car tags)) start) + (<= (semantic-tag-end (car tags)) end)) + + ;; Check if this new all-encompassing tag is a parent + ;; of that which went before. Only check end because + ;; we know that start is less than inner-start since + ;; tags was sorted on that. + (if (> (semantic-tag-end (car tags)) inner-end) + ;; This is a parent. Drop the children found + ;; so far. + (setq overlapped-tags (list (car tags)) + inner-start (semantic-tag-start (car tags)) + inner-end (semantic-tag-end (car tags)) + ) + ;; It is not a parent encompassing tag + (setq overlapped-tags (cons (car tags) + overlapped-tags) + inner-start (semantic-tag-start (car tags)))) + (setq tags (cdr tags))) + (if (not tags) + ;; There are no tags left, and all tags originally + ;; found are encompassed by the change. Setup our list + ;; from the cache + (setq list-to-search semantic--buffer-cache);; We have a tag ouside the list. Check for + ;; We know we have a parent because it would + ;; completely cover the change. A tag can only + ;; do that if it is a parent after we get here. + (when (and tags + (< (semantic-tag-start (car tags)) start) + (> (semantic-tag-end (car tags)) end)) + ;; We have a parent. Stuff in the search list. + (setq parent (car tags) + list-to-search (semantic-tag-components parent)) + ;; If the first of TAGS is a parent (see above) + ;; then clear out the list. All other tags in + ;; here must therefore be parents of the car. + (setq tags nil) + ;; One last check, If start is before the first + ;; tag or after the last, we may have overlap into + ;; the characters that make up the definition of + ;; the tag we are parsing. + (when (or (semantic-tag-with-position-p (car list-to-search)) + (< start (semantic-tag-start + (car list-to-search))) + (> end (semantic-tag-end + (nth (1- (length list-to-search)) + list-to-search)))) + ;; We have a problem + (setq list-to-search nil + parent nil)))) + + (when list-to-search + + ;; Ok, return the vector only if all TAGS are + ;; confirmed as the lineage of `overlapped-tags' + ;; which must have a value by now. + + ;; Loop over the search list to find the preceeding CDR. + ;; Fortunatly, (car overlapped-tags) happens to be + ;; the first tag positionally. + (let ((tokstart (semantic-tag-start (car overlapped-tags)))) + (while (and list-to-search + ;; Assume always (car (cdr list-to-search)). + ;; A thrown error will be captured nicely, but + ;; that case shouldn't happen. + + ;; We end when the start of the CDR is after the + ;; end of our asked change. + (cdr list-to-search) + (< (semantic-tag-start (car (cdr list-to-search))) + tokstart) + (setq list-to-search (cdr list-to-search))))) + ;; Create the return vector + (vector overlapped-tags + list-to-search + parent) + )) + nil))) + +;;; Default Incremental Parser +;; +;; Logic about how to group changes for effective reparsing and splicing. + +(defun semantic-parse-changes-failed (&rest args) + "Signal that Semantic failed to parse changes. +That is, display a message by passing all ARGS to `format', then throw +a 'semantic-parse-changes-failed exception with value t." + (when semantic-edits-verbose-flag + (message "Semantic parse changes failed: %S" + (apply 'format args))) + (throw 'semantic-parse-changes-failed t)) + +(defsubst semantic-edits-incremental-fail () + "When the incremental parser fails, we mark that we need a full reparse." + ;;(debug) + (semantic-parse-tree-set-needs-rebuild) + (when semantic-edits-verbose-flag + (message "Force full reparse (%s)" + (buffer-name (current-buffer)))) + (run-hooks 'semantic-edits-incremental-reparse-failed-hook)) + +(defun semantic-edits-incremental-parser () + "Incrementally reparse the current buffer. +Incremental parser allows semantic to only reparse those sections of +the buffer that have changed. This function depends on +`semantic-edits-change-function-handle-changes' setting up change +overlays in the current buffer. Those overlays are analyzed against +the semantic cache to see what needs to be changed." + (let ((changed-tags + ;; Don't use `semantic-safe' here to explicitly catch errors + ;; and reset the parse tree. + (catch 'semantic-parse-changes-failed + (if debug-on-error + (semantic-edits-incremental-parser-1) + (condition-case err + (semantic-edits-incremental-parser-1) + (error + (message "incremental parser error: %S" + (error-message-string err)) + t)))))) + (when (eq changed-tags t) + ;; Force a full reparse. + (semantic-edits-incremental-fail) + (setq changed-tags nil)) + changed-tags)) + +(defmacro semantic-edits-assert-valid-region () + "Asert that parse-start and parse-end are sorted correctly." +;;; (if (> parse-start parse-end) +;;; (error "Bug is %s !> %d! Buff min/max = [ %d %d ]" +;;; parse-start parse-end +;;; (point-min) (point-max))) + ) + +(defun semantic-edits-incremental-parser-1 () + "Incrementally reparse the current buffer. +Return the list of tags that changed. +If the incremental parse fails, throw a 'semantic-parse-changes-failed +exception with value t, that can be caught to schedule a full reparse. +This function is for internal use by `semantic-edits-incremental-parser'." + (let* ((changed-tags nil) + (debug-on-quit t) ; try to find this annoying bug! + (changes (semantic-changes-in-region + (point-min) (point-max))) + (tags nil) ;tags found at changes + (newf-tags nil) ;newfound tags in change + (parse-start nil) ;location to start parsing + (parse-end nil) ;location to end parsing + (parent-tag nil) ;parent of the cache list. + (cache-list nil) ;list of children within which + ;we incrementally reparse. + (reparse-symbol nil) ;The ruled we start at for reparse. + (change-group nil) ;changes grouped in this reparse + (last-cond nil) ;track the last case used. + ;query this when debugging to find + ;source of bugs. + ) + (or changes + ;; If we were called, and there are no changes, then we + ;; don't know what to do. Force a full reparse. + (semantic-parse-changes-failed "Don't know what to do")) + ;; Else, we have some changes. Loop over them attempting to + ;; patch things up. + (while changes + ;; Calculate the reparse boundary. + ;; We want to take some set of changes, and group them + ;; together into a small change group. One change forces + ;; a reparse of a larger region (the size of some set of + ;; tags it encompases.) It may contain several tags. + ;; That region may have other changes in it (several small + ;; changes in one function, for example.) + ;; Optimize for the simple cases here, but try to handle + ;; complex ones too. + + (while (and changes ; we still have changes + (or (not parse-start) + ;; Below, if the change we are looking at + ;; is not the first change for this + ;; iteration, and it starts before the end + ;; of current parse region, then it is + ;; encompased within the bounds of tags + ;; modified by the previous iteration's + ;; change. + (< (semantic-overlay-start (car changes)) + parse-end))) + + ;; REMOVE LATER + (if (eq (car changes) (car change-group)) + (semantic-parse-changes-failed + "Possible infinite loop detected")) + + ;; Store this change in this change group. + (setq change-group (cons (car changes) change-group)) + + (cond + ;; Is this is a new parse group? + ((not parse-start) + (setq last-cond "new group") + (let (tmp) + (cond + +;;;; Are we encompassed all in one tag? + ((setq tmp (semantic-edits-change-leaf-tag (car changes))) + (setq last-cond "Encompassed in tag") + (setq tags (list tmp) + parse-start (semantic-tag-start tmp) + parse-end (semantic-tag-end tmp) + ) + (semantic-edits-assert-valid-region)) + +;;;; Did the change occur between some tags? + ((setq cache-list (semantic-edits-change-between-tags + (car changes))) + (setq last-cond "Between and not overlapping tags") + ;; The CAR of cache-list is the tag just before + ;; our change, but wasn't modified. Hmmm. + ;; Bound our reparse between these two tags + (setq tags nil + parent-tag + (car (semantic-find-tag-by-overlay + parse-start))) + (cond + ;; A change at the beginning of the buffer. + ;; Feb 06 - + ;; IDed when the first cache-list tag is after + ;; our change, meaning there is nothing before + ;; the chnge. + ((> (semantic-tag-start (car cache-list)) + (semantic-overlay-end (car changes))) + (setq last-cond "Beginning of buffer") + (setq parse-start + ;; Don't worry about parents since + ;; there there would be an exact + ;; match in the tag list otherwise + ;; and the routine would fail. + (point-min) + parse-end + (semantic-tag-start (car cache-list))) + (semantic-edits-assert-valid-region) + ) + ;; A change stuck on the first surrounding tag. + ((= (semantic-tag-end (car cache-list)) + (semantic-overlay-start (car changes))) + (setq last-cond "Beginning of Tag") + ;; Reparse that first tag. + (setq parse-start + (semantic-tag-start (car cache-list)) + parse-end + (semantic-overlay-end (car changes)) + tags + (list (car cache-list))) + (semantic-edits-assert-valid-region) + ) + ;; A change at the end of the buffer. + ((not (car (cdr cache-list))) + (setq last-cond "End of buffer") + (setq parse-start (semantic-tag-end + (car cache-list)) + parse-end (point-max)) + (semantic-edits-assert-valid-region) + ) + (t + (setq last-cond "Default") + (setq parse-start + (semantic-tag-end (car cache-list)) + parse-end + (semantic-tag-start (car (cdr cache-list))) + ) + (semantic-edits-assert-valid-region)))) + +;;;; Did the change completely overlap some number of tags? + ((setq tmp (semantic-edits-change-over-tags + (car changes))) + (setq last-cond "Overlap multiple tags") + ;; Extract the information + (setq tags (aref tmp 0) + cache-list (aref tmp 1) + parent-tag (aref tmp 2)) + ;; We can calculate parse begin/end by checking + ;; out what is in TAGS. The one near start is + ;; always first. Make sure the reprase includes + ;; the `whitespace' around the snarfed tags. + ;; Since cache-list is positioned properly, use it + ;; to find that boundary. + (if (eq (car tags) (car cache-list)) + ;; Beginning of the buffer! + (let ((end-marker (nth (length tags) + cache-list))) + (setq parse-start (point-min)) + (if end-marker + (setq parse-end + (semantic-tag-start end-marker)) + (setq parse-end (semantic-overlay-end + (car changes)))) + (semantic-edits-assert-valid-region) + ) + ;; Middle of the buffer. + (setq parse-start + (semantic-tag-end (car cache-list))) + ;; For the end, we need to scoot down some + ;; number of tags. We 1+ the length of tags + ;; because we want to skip the first tag + ;; (remove 1-) then want the tag after the end + ;; of the list (1+) + (let ((end-marker (nth (1+ (length tags)) cache-list))) + (if end-marker + (setq parse-end (semantic-tag-start end-marker)) + ;; No marker. It is the last tag in our + ;; list of tags. Only possible if END + ;; already matches the end of that tag. + (setq parse-end + (semantic-overlay-end (car changes))))) + (semantic-edits-assert-valid-region) + )) + +;;;; Unhandled case. + ;; Throw error, and force full reparse. + ((semantic-parse-changes-failed "Unhandled change group"))) + )) + ;; Is this change inside the previous parse group? + ;; We already checked start. + ((< (semantic-overlay-end (car changes)) parse-end) + (setq last-cond "in bounds") + nil) + ;; This change extends the current parse group. + ;; Find any new tags, and see how to append them. + ((semantic-parse-changes-failed + (setq last-cond "overlap boundary") + "Unhandled secondary change overlapping boundary")) + ) + ;; Prepare for the next iteration. + (setq changes (cdr changes))) + + ;; By the time we get here, all TAGS are children of + ;; some parent. They should all have the same start symbol + ;; since that is how the multi-tag parser works. Grab + ;; the reparse symbol from the first of the returned tags. + ;; + ;; Feb '06 - If repase-symbol is nil, then they are top level + ;; tags. (I'm guessing.) Is this right? + (setq reparse-symbol + (semantic--tag-get-property (car (or tags cache-list)) + 'reparse-symbol)) + ;; Find a parent if not provided. + (and (not parent-tag) tags + (setq parent-tag + (semantic-find-tag-parent-by-overlay + (car tags)))) + ;; We can do the same trick for our parent and resulting + ;; cache list. + (unless cache-list + (if parent-tag + (setq cache-list + ;; We need to get all children in case we happen + ;; to have a mix of positioned and non-positioned + ;; children. + (semantic-tag-components parent-tag)) + ;; Else, all the tags since there is no parent. + ;; It sucks to have to use the full buffer cache in + ;; this case because it can be big. Failure to provide + ;; however results in a crash. + (setq cache-list semantic--buffer-cache) + )) + ;; Use the boundary to calculate the new tags found. + (setq newf-tags (semantic-parse-region + parse-start parse-end reparse-symbol)) + ;; Make sure all these tags are given overlays. + ;; They have already been cooked by the parser and just + ;; need the overlays. + (let ((tmp newf-tags)) + (while tmp + (semantic--tag-link-to-buffer (car tmp)) + (setq tmp (cdr tmp)))) + + ;; See how this change lays out. + (cond + +;;;; Whitespace change + ((and (not tags) (not newf-tags)) + ;; A change that occured outside of any existing tags + ;; and there are no new tags to replace it. + (when semantic-edits-verbose-flag + (message "White space changes")) + nil + ) + +;;;; New tags in old whitespace area. + ((and (not tags) newf-tags) + ;; A change occured outside existing tags which added + ;; a new tag. We need to splice these tags back + ;; into the cache at the right place. + (semantic-edits-splice-insert newf-tags parent-tag cache-list) + + (setq changed-tags + (append newf-tags changed-tags)) + + (when semantic-edits-verbose-flag + (message "Inserted tags: (%s)" + (semantic-format-tag-name (car newf-tags)))) + ) + +;;;; Old tags removed + ((and tags (not newf-tags)) + ;; A change occured where pre-existing tags were + ;; deleted! Remove the tag from the cache. + (semantic-edits-splice-remove tags parent-tag cache-list) + + (setq changed-tags + (append tags changed-tags)) + + (when semantic-edits-verbose-flag + (message "Deleted tags: (%s)" + (semantic-format-tag-name (car tags)))) + ) + +;;;; One tag was updated. + ((and (= (length tags) 1) (= (length newf-tags) 1)) + ;; One old tag was modified, and it is replaced by + ;; One newfound tag. Splice the new tag into the + ;; position of the old tag. + ;; Do the splice. + (semantic-edits-splice-replace (car tags) (car newf-tags)) + ;; Add this tag to our list of changed toksns + (setq changed-tags (cons (car tags) changed-tags)) + ;; Debug + (when semantic-edits-verbose-flag + (message "Update Tag Table: %s" + (semantic-format-tag-name (car tags) nil t))) + ;; Flush change regardless of above if statement. + ) + +;;;; Some unhandled case. + ((semantic-parse-changes-failed "Don't know what to do"))) + + ;; We got this far, and we didn't flag a full reparse. + ;; Clear out this change group. + (while change-group + (semantic-edits-flush-change (car change-group)) + (setq change-group (cdr change-group))) + + ;; Don't increment change here because an earlier loop + ;; created change-groups. + (setq parse-start nil) + ) + ;; Mark that we are done with this glop + (semantic-parse-tree-set-up-to-date) + ;; Return the list of tags that changed. The caller will + ;; use this information to call hooks which can fix themselves. + changed-tags)) + +;; Make it the default changes parser +;;;###autoload +(defalias 'semantic-parse-changes-default + 'semantic-edits-incremental-parser) + +;;; Cache Splicing +;; +;; The incremental parser depends on the ability to parse up sections +;; of the file, and splice the results back into the cache. There are +;; three types of splices. A REPLACE, an ADD, and a REMOVE. REPLACE +;; is one of the simpler cases, as the starting cons cell representing +;; the old tag can be used to auto-splice in. ADD and REMOVE +;; require scanning the cache to find the correct location so that the +;; list can be fiddled. +(defun semantic-edits-splice-remove (oldtags parent cachelist) + "Remove OLDTAGS from PARENT's CACHELIST. +OLDTAGS are tags in the currenet buffer, preferably linked +together also in CACHELIST. +PARENT is the parent tag containing OLDTAGS. +CACHELIST should be the children from PARENT, but may be +pre-positioned to a convenient location." + (let* ((first (car oldtags)) + (last (nth (1- (length oldtags)) oldtags)) + (chil (if parent + (semantic-tag-components parent) + semantic--buffer-cache)) + (cachestart cachelist) + (cacheend nil) + ) + ;; First in child list? + (if (eq first (car chil)) + ;; First tags in the cache are being deleted. + (progn + (when semantic-edits-verbose-flag + (message "To Remove First Tag: (%s)" + (semantic-format-tag-name first))) + ;; Find the last tag + (setq cacheend chil) + (while (and cacheend (not (eq last (car cacheend)))) + (setq cacheend (cdr cacheend))) + ;; The splicable part is after cacheend.. so move cacheend + ;; one more tag. + (setq cacheend (cdr cacheend)) + ;; Splice the found end tag into the cons cell + ;; owned by the current top child. + (setcar chil (car cacheend)) + (setcdr chil (cdr cacheend)) + (when (not cacheend) + ;; No cacheend.. then the whole system is empty. + ;; The best way to deal with that is to do a full + ;; reparse + (semantic-parse-changes-failed "Splice-remove failed. Empty buffer?") + )) + (message "To Remove Middle Tag: (%s)" + (semantic-format-tag-name first))) + ;; Find in the cache the preceeding tag + (while (and cachestart (not (eq first (car (cdr cachestart))))) + (setq cachestart (cdr cachestart))) + ;; Find the last tag + (setq cacheend cachestart) + (while (and cacheend (not (eq last (car cacheend)))) + (setq cacheend (cdr cacheend))) + ;; Splice the end position into the start position. + ;; If there is no start, then this whole section is probably + ;; gone. + (if cachestart + (setcdr cachestart (cdr cacheend)) + (semantic-parse-changes-failed "Splice-remove failed.")) + + ;; Remove old overlays of these deleted tags + (while oldtags + (semantic--tag-unlink-from-buffer (car oldtags)) + (setq oldtags (cdr oldtags))) + )) + +(defun semantic-edits-splice-insert (newtags parent cachelist) + "Insert NEWTAGS into PARENT using CACHELIST. +PARENT could be nil, in which case CACHLIST is the buffer cache +which must be updated. +CACHELIST must be searched to find where NEWTAGS are to be inserted. +The positions of NEWTAGS must be synchronized with those in +CACHELIST for this to work. Some routines pre-position CACHLIST at a +convenient location, so use that." + (let* ((start (semantic-tag-start (car newtags))) + (newtagendcell (nthcdr (1- (length newtags)) newtags)) + (end (semantic-tag-end (car newtagendcell))) + ) + (if (> (semantic-tag-start (car cachelist)) start) + ;; We are at the beginning. + (let* ((pc (if parent + (semantic-tag-components parent) + semantic--buffer-cache)) + (nc (cons (car pc) (cdr pc))) ; new cons cell. + ) + ;; Splice the new cache cons cell onto the end of our list. + (setcdr newtagendcell nc) + ;; Set our list into parent. + (setcar pc (car newtags)) + (setcdr pc (cdr newtags))) + ;; We are at the end, or in the middle. Find our match first. + (while (and (cdr cachelist) + (> end (semantic-tag-start (car (cdr cachelist))))) + (setq cachelist (cdr cachelist))) + ;; Now splice into the list! + (setcdr newtagendcell (cdr cachelist)) + (setcdr cachelist newtags)))) + +(defun semantic-edits-splice-replace (oldtag newtag) + "Replace OLDTAG with NEWTAG in the current cache. +Do this by recycling OLDTAG's first CONS cell. This effectivly +causes the new tag to completely replace the old one. +Make sure that all information in the overlay is transferred. +It is presumed that OLDTAG and NEWTAG are both cooked. +When this routine returns, OLDTAG is raw, and the data will be +lost if not transferred into NEWTAG." + (let* ((oo (semantic-tag-overlay oldtag)) + (o (semantic-tag-overlay newtag)) + (oo-props (semantic-overlay-properties oo))) + (while oo-props + (semantic-overlay-put o (car oo-props) (car (cdr oo-props))) + (setq oo-props (cdr (cdr oo-props))) + ) + ;; Free the old overlay(s) + (semantic--tag-unlink-from-buffer oldtag) + ;; Recover properties + (semantic--tag-copy-properties oldtag newtag) + ;; Splice into the main list. + (setcdr oldtag (cdr newtag)) + (setcar oldtag (car newtag)) + ;; This important bit is because the CONS cell representing + ;; OLDTAG is now pointing to NEWTAG, but the NEWTAG + ;; cell is about to be abandoned. Here we update our overlay + ;; to point at the updated state of the world. + (semantic-overlay-put o 'semantic oldtag) + )) + +(add-hook 'semantic-before-toplevel-cache-flush-hook + #'semantic-edits-flush-changes) + +(provide 'semantic/edit) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/edit" +;; End: + +;;; semantic/edit.el ends here diff --git a/lisp/cedet/semantic/find.el b/lisp/cedet/semantic/find.el new file mode 100644 index 00000000000..9886685cb5d --- /dev/null +++ b/lisp/cedet/semantic/find.el @@ -0,0 +1,705 @@ +;;; semantic/find.el --- Search routines for Semantic + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 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: +;; +;; Routines for searching through lists of tags. +;; There are several groups of tag search routines: +;; +;; 1) semantic-brute-find-tag-by-* +;; These routines use brute force hierarchical search to scan +;; through lists of tags. They include some parameters +;; used for compatibility with the semantic 1.x search routines. +;; +;; 1.5) semantic-brute-find-first-tag-by-* +;; Like 1, except seraching stops on the first match for the given +;; information. +;; +;; 2) semantic-find-tag-by-* +;; These prefered search routines attempt to scan through lists +;; in an intelligent way based on questions asked. +;; +;; 3) semantic-find-*-overlay +;; These routines use overlays to return tags based on a buffer position. +;; +;; 4) ... + +;;; Code: + +(require 'semantic) +(require 'semantic/tag) + +(declare-function semantic-tag-protected-p "semantic/tag-ls") + +;;; Overlay Search Routines +;; +;; These routines provide fast access to tokens based on a buffer that +;; has parsed tokens in it. Uses overlays to perform the hard work. +;; +;;;###autoload +(defun semantic-find-tag-by-overlay (&optional positionormarker buffer) + "Find all tags covering POSITIONORMARKER by using overlays. +If POSITIONORMARKER is nil, use the current point. +Optional BUFFER is used if POSITIONORMARKER is a number, otherwise the current +buffer is used. This finds all tags covering the specified position +by checking for all overlays covering the current spot. They are then sorted +from largest to smallest via the start location." + (save-excursion + (when positionormarker + (if (markerp positionormarker) + (set-buffer (marker-buffer positionormarker)) + (if (bufferp buffer) + (set-buffer buffer)))) + (let ((ol (semantic-overlays-at (or positionormarker (point)))) + (ret nil)) + (while ol + (let ((tmp (semantic-overlay-get (car ol) 'semantic))) + (when (and tmp + ;; We don't need with-position because no tag w/out + ;; a position could exist in an overlay. + (semantic-tag-p tmp)) + (setq ret (cons tmp ret)))) + (setq ol (cdr ol))) + (sort ret (lambda (a b) (< (semantic-tag-start a) + (semantic-tag-start b))))))) + +;;;###autoload +(defun semantic-find-tag-by-overlay-in-region (start end &optional buffer) + "Find all tags which exist in whole or in part between START and END. +Uses overlays to determine positin. +Optional BUFFER argument specifies the buffer to use." + (save-excursion + (if buffer (set-buffer buffer)) + (let ((ol (semantic-overlays-in start end)) + (ret nil)) + (while ol + (let ((tmp (semantic-overlay-get (car ol) 'semantic))) + (when (and tmp + ;; See above about position + (semantic-tag-p tmp)) + (setq ret (cons tmp ret)))) + (setq ol (cdr ol))) + (sort ret (lambda (a b) (< (semantic-tag-start a) + (semantic-tag-start b))))))) + +;;;###autoload +(defun semantic-find-tag-by-overlay-next (&optional start buffer) + "Find the next tag after START in BUFFER. +If START is in an overlay, find the tag which starts next, +not the current tag." + (save-excursion + (if buffer (set-buffer buffer)) + (if (not start) (setq start (point))) + (let ((os start) (ol nil)) + (while (and os (< os (point-max)) (not ol)) + (setq os (semantic-overlay-next-change os)) + (when os + ;; Get overlays at position + (setq ol (semantic-overlays-at os)) + ;; find the overlay that belongs to semantic + ;; and starts at the found position. + (while (and ol (listp ol)) + (if (and (semantic-overlay-get (car ol) 'semantic) + (semantic-tag-p + (semantic-overlay-get (car ol) 'semantic)) + (= (semantic-overlay-start (car ol)) os)) + (setq ol (car ol))) + (when (listp ol) (setq ol (cdr ol)))))) + ;; convert ol to a tag + (when (and ol (semantic-tag-p (semantic-overlay-get ol 'semantic))) + (semantic-overlay-get ol 'semantic))))) + +;;;###autoload +(defun semantic-find-tag-by-overlay-prev (&optional start buffer) + "Find the next tag before START in BUFFER. +If START is in an overlay, find the tag which starts next, +not the current tag." + (save-excursion + (if buffer (set-buffer buffer)) + (if (not start) (setq start (point))) + (let ((os start) (ol nil)) + (while (and os (> os (point-min)) (not ol)) + (setq os (semantic-overlay-previous-change os)) + (when os + ;; Get overlays at position + (setq ol (semantic-overlays-at (1- os))) + ;; find the overlay that belongs to semantic + ;; and ENDS at the found position. + ;; + ;; Use end because we are going backward. + (while (and ol (listp ol)) + (if (and (semantic-overlay-get (car ol) 'semantic) + (semantic-tag-p + (semantic-overlay-get (car ol) 'semantic)) + (= (semantic-overlay-end (car ol)) os)) + (setq ol (car ol))) + (when (listp ol) (setq ol (cdr ol)))))) + ;; convert ol to a tag + (when (and ol + (semantic-tag-p (semantic-overlay-get ol 'semantic))) + (semantic-overlay-get ol 'semantic))))) + +;;;###autoload +(defun semantic-find-tag-parent-by-overlay (tag) + "Find the parent of TAG by overlays. +Overlays are a fast way of finding this information for active buffers." + (let ((tag (nreverse (semantic-find-tag-by-overlay + (semantic-tag-start tag))))) + ;; This is a lot like `semantic-current-tag-parent', but + ;; it uses a position to do it's work. Assumes two tags don't share + ;; the same start unless they are siblings. + (car (cdr tag)))) + +;;;###autoload +(defun semantic-current-tag () + "Return the current tag in the current buffer. +If there are more than one in the same location, return the +smallest tag. Return nil if there is no tag here." + (car (nreverse (semantic-find-tag-by-overlay)))) + +;;;###autoload +(defun semantic-current-tag-parent () + "Return the current tags parent in the current buffer. +A tag's parent would be a containing structure, such as a type +containing a field. Return nil if there is no parent." + (car (cdr (nreverse (semantic-find-tag-by-overlay))))) + +(defun semantic-current-tag-of-class (class) + "Return the current (smallest) tags of CLASS in the current buffer. +If the smallest tag is not of type CLASS, keep going upwards until one +is found. +Uses `semantic-tag-class' for classification." + (let ((tags (nreverse (semantic-find-tag-by-overlay)))) + (while (and tags + (not (eq (semantic-tag-class (car tags)) class))) + (setq tags (cdr tags))) + (car tags))) + +;;; Search Routines +;; +;; These are routines that search a single tags table. +;; +;; The original API (see COMPATIBILITY section below) in semantic 1.4 +;; had these usage statistics: +;; +;; semantic-find-nonterminal-by-name 17 +;; semantic-find-nonterminal-by-name-regexp 8 - Most doing completion +;; semantic-find-nonterminal-by-position 13 +;; semantic-find-nonterminal-by-token 21 +;; semantic-find-nonterminal-by-type 2 +;; semantic-find-nonterminal-standard 1 +;; +;; semantic-find-nonterminal-by-function (not in other searches) 1 +;; +;; New API: As above w/out `search-parts' or `search-includes' arguments. +;; Extra fcn: Specific to completion which is what -name-regexp is +;; mostly used for +;; +;; As for the sarguments "search-parts" and "search-includes" here +;; are stats: +;; +;; search-parts: 4 - charting x2, find-doc, senator (sans db) +;; +;; Implement command to flatten a tag table. Call new API Fcn w/ +;; flattened table for same results. +;; +;; search-include: 2 - analyze x2 (sans db) +;; +;; Not used effectively. Not to be re-implemented here. + +(defsubst semantic--find-tags-by-function (predicate &optional table) + "Find tags for which PREDICATE is non-nil in TABLE. +PREDICATE is a lambda expression which accepts on TAG. +TABLE is a semantic tags table. See `semantic-something-to-tag-table'." + (let ((tags (semantic-something-to-tag-table table)) + (result nil)) +; (mapc (lambda (tag) (and (funcall predicate tag) +; (setq result (cons tag result)))) +; tags) + ;; A while loop is actually faster. Who knew + (while tags + (and (funcall predicate (car tags)) + (setq result (cons (car tags) result))) + (setq tags (cdr tags))) + (nreverse result))) + +;; I can shave off some time by removing the funcall (see above) +;; and having the question be inlined in the while loop. +;; Strangely turning the upper level fcns into macros had a larger +;; impact. +(defmacro semantic--find-tags-by-macro (form &optional table) + "Find tags for which FORM is non-nil in TABLE. +TABLE is a semantic tags table. See `semantic-something-to-tag-table'." + `(let ((tags (semantic-something-to-tag-table ,table)) + (result nil)) + (while tags + (and ,form + (setq result (cons (car tags) result))) + (setq tags (cdr tags))) + (nreverse result))) + +;;; Top level Searches +;; +;;;###autoload +(defun semantic-find-first-tag-by-name (name &optional table) + "Find the first tag with NAME in TABLE. +NAME is a string. +TABLE is a semantic tags table. See `semantic-something-to-tag-table'. +This routine uses `assoc' to quickly find the first matching entry." + (funcall (if semantic-case-fold 'assoc-ignore-case 'assoc) + name (semantic-something-to-tag-table table))) + +(defmacro semantic-find-tags-by-name (name &optional table) + "Find all tags with NAME in TABLE. +NAME is a string. +TABLE is a tag table. See `semantic-something-to-tag-table'." + `(let ((case-fold-search semantic-case-fold)) + (semantic--find-tags-by-macro + (string= ,name (semantic-tag-name (car tags))) + ,table))) + +(defmacro semantic-find-tags-for-completion (prefix &optional table) + "Find all tags whos name begins with PREFIX in TABLE. +PREFIX is a string. +TABLE is a tag table. See `semantic-something-to-tag-table'. +While it would be nice to use `try-completion' or `all-completions', +those functions do not return the tags, only a string. +Uses `compare-strings' for fast comparison." + `(let ((l (length ,prefix))) + (semantic--find-tags-by-macro + (eq (compare-strings ,prefix 0 nil + (semantic-tag-name (car tags)) 0 l + semantic-case-fold) + t) + ,table))) + +(defmacro semantic-find-tags-by-name-regexp (regexp &optional table) + "Find all tags with name matching REGEXP in TABLE. +REGEXP is a string containing a regular expression, +TABLE is a tag table. See `semantic-something-to-tag-table'. +Consider using `semantic-find-tags-for-completion' if you are +attempting to do completions." + `(let ((case-fold-search semantic-case-fold)) + (semantic--find-tags-by-macro + (string-match ,regexp (semantic-tag-name (car tags))) + ,table))) + +(defmacro semantic-find-tags-by-class (class &optional table) + "Find all tags of class CLASS in TABLE. +CLASS is a symbol representing the class of the token, such as +'variable, of 'function.. +TABLE is a tag table. See `semantic-something-to-tag-table'." + `(semantic--find-tags-by-macro + (eq ,class (semantic-tag-class (car tags))) + ,table)) + +(defmacro semantic-find-tags-by-type (type &optional table) + "Find all tags of with a type TYPE in TABLE. +TYPE is a string or tag representing a data type as defined in the +language the tags were parsed from, such as \"int\", or perhaps +a tag whose name is that of a struct or class. +TABLE is a tag table. See `semantic-something-to-tag-table'." + `(semantic--find-tags-by-macro + (semantic-tag-of-type-p (car tags) ,type) + ,table)) + +(defmacro semantic-find-tags-of-compound-type (&optional table) + "Find all tags which are a compound type in TABLE. +Compound types are structures, or other data type which +is not of a primitive nature, such as int or double. +Used in completion." + `(semantic--find-tags-by-macro + (semantic-tag-type-compound-p (car tags)) + ,table)) + +;;;###autoload +(define-overloadable-function semantic-find-tags-by-scope-protection (scopeprotection parent &optional table) + "Find all tags accessable by SCOPEPROTECTION. +SCOPEPROTECTION is a symbol which can be returned by the method +`semantic-tag-protection'. A hard-coded order is used to determine a match. +PARENT is a tag representing the PARENT slot needed for +`semantic-tag-protection'. +TABLE is a list of tags (a subset of PARENT members) to scan. If TABLE is nil, +the type members of PARENT are used. +See `semantic-tag-protected-p' for details on which tags are returned." + (if (not (eq (semantic-tag-class parent) 'type)) + (signal 'wrong-type-argument '(semantic-find-tags-by-scope-protection + parent + semantic-tag-class type)) + (:override))) + +(defun semantic-find-tags-by-scope-protection-default + (scopeprotection parent &optional table) + "Find all tags accessable by SCOPEPROTECTION. +SCOPEPROTECTION is a symbol which can be returned by the method +`semantic-tag-protection'. A hard-coded order is used to determine a match. +PARENT is a tag representing the PARENT slot needed for +`semantic-tag-protection'. +TABLE is a list of tags (a subset of PARENT members) to scan. If TABLE is nil, +the type members of PARENT are used. +See `semantic-tag-protected-p' for details on which tags are returned." + (if (not table) (setq table (semantic-tag-type-members parent))) + (if (null scopeprotection) + table + (require 'semantic/tag-ls) + (semantic--find-tags-by-macro + (not (semantic-tag-protected-p (car tags) scopeprotection parent)) + table))) + +(defsubst semantic-find-tags-included (&optional table) + "Find all tags in TABLE that are of the 'include class. +TABLE is a tag table. See `semantic-something-to-tag-table'." + (semantic-find-tags-by-class 'include table)) + +;;; Deep Searches + +(defmacro semantic-deep-find-tags-by-name (name &optional table) + "Find all tags with NAME in TABLE. +Search in top level tags, and their components, in TABLE. +NAME is a string. +TABLE is a tag table. See `semantic-flatten-tags-table'. +See also `semantic-find-tags-by-name'." + `(semantic-find-tags-by-name + ,name (semantic-flatten-tags-table ,table))) + +(defmacro semantic-deep-find-tags-for-completion (prefix &optional table) + "Find all tags whos name begins with PREFIX in TABLE. +Search in top level tags, and their components, in TABLE. +TABLE is a tag table. See `semantic-flatten-tags-table'. +See also `semantic-find-tags-for-completion'." + `(semantic-find-tags-for-completion + ,prefix (semantic-flatten-tags-table ,table))) + +(defmacro semantic-deep-find-tags-by-name-regexp (regexp &optional table) + "Find all tags with name matching REGEXP in TABLE. +Search in top level tags, and their components, in TABLE. +REGEXP is a string containing a regular expression, +TABLE is a tag table. See `semantic-flatten-tags-table'. +See also `semantic-find-tags-by-name-regexp'. +Consider using `semantic-deep-find-tags-for-completion' if you are +attempting to do completions." + `(semantic-find-tags-by-name-regexp + ,regexp (semantic-flatten-tags-table ,table))) + +;;; Specialty Searches + +(defun semantic-find-tags-external-children-of-type (type &optional table) + "Find all tags in whose parent is TYPE in TABLE. +These tags are defined outside the scope of the original TYPE declaration. +TABLE is a tag table. See `semantic-something-to-tag-table'." + (semantic--find-tags-by-macro + (equal (semantic-tag-external-member-parent (car tags)) + type) + table)) + +(defun semantic-find-tags-subclasses-of-type (type &optional table) + "Find all tags of class type in whose parent is TYPE in TABLE. +These tags are defined outside the scope of the original TYPE declaration. +TABLE is a tag table. See `semantic-something-to-tag-table'." + (semantic--find-tags-by-macro + (and (eq (semantic-tag-class (car tags)) 'type) + (or (member type (semantic-tag-type-superclasses (car tags))) + (member type (semantic-tag-type-interfaces (car tags))))) + table)) + +;; +;; ************************** Compatibility *************************** +;; + +;;; Old Style Brute Force Search Routines +;; +;; These functions will search through tags lists explicity for +;; desired information. + +;; The -by-name nonterminal search can use the built in fcn +;; `assoc', which is faster than looping ourselves, so we will +;; not use `semantic-brute-find-tag-by-function' to do this, +;; instead erroring on the side of speed. + +(defun semantic-brute-find-first-tag-by-name + (name streamorbuffer &optional search-parts search-include) + "Find a tag NAME within STREAMORBUFFER. NAME is a string. +If SEARCH-PARTS is non-nil, search children of tags. +If SEARCH-INCLUDE was never implemented. + +Use `semantic-find-first-tag-by-name' instead." + (let* ((stream (semantic-something-to-tag-table streamorbuffer)) + (assoc-fun (if semantic-case-fold + #'assoc-ignore-case + #'assoc)) + (m (funcall assoc-fun name stream))) + (if m + m + (let ((toklst stream) + (children nil)) + (while (and (not m) toklst) + (if search-parts + (progn + (setq children (semantic-tag-components-with-overlays + (car toklst))) + (if children + (setq m (semantic-brute-find-first-tag-by-name + name children search-parts search-include))))) + (setq toklst (cdr toklst))) + (if (not m) + ;; Go to dependencies, and search there. + nil) + m)))) + +(defmacro semantic-brute-find-tag-by-class + (class streamorbuffer &optional search-parts search-includes) + "Find all tags with a class CLASS within STREAMORBUFFER. +CLASS is a symbol representing the class of the tags to find. +See `semantic-tag-class'. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'. + +Use `semantic-find-tag-by-class' instead." + `(semantic-brute-find-tag-by-function + (lambda (tag) (eq ,class (semantic-tag-class tag))) + ,streamorbuffer ,search-parts ,search-includes)) + +(defmacro semantic-brute-find-tag-standard + (streamorbuffer &optional search-parts search-includes) + "Find all tags in STREAMORBUFFER which define simple class types. +See `semantic-tag-class'. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'." + `(semantic-brute-find-tag-by-function + (lambda (tag) (member (semantic-tag-class tag) + '(function variable type))) + ,streamorbuffer ,search-parts ,search-includes)) + +(defun semantic-brute-find-tag-by-type + (type streamorbuffer &optional search-parts search-includes) + "Find all tags with type TYPE within STREAMORBUFFER. +TYPE is a string which is the name of the type of the tags returned. +See `semantic-tag-type'. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'." + (semantic-brute-find-tag-by-function + (lambda (tag) + (let ((ts (semantic-tag-type tag))) + (if (and (listp ts) + (or (= (length ts) 1) + (eq (semantic-tag-class ts) 'type))) + (setq ts (semantic-tag-name ts))) + (equal type ts))) + streamorbuffer search-parts search-includes)) + +(defun semantic-brute-find-tag-by-type-regexp + (regexp streamorbuffer &optional search-parts search-includes) + "Find all tags with type matching REGEXP within STREAMORBUFFER. +REGEXP is a regular expression which matches the name of the type of the +tags returned. See `semantic-tag-type'. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'." + (semantic-brute-find-tag-by-function + (lambda (tag) + (let ((ts (semantic-tag-type tag))) + (if (listp ts) + (setq ts + (if (eq (semantic-tag-class ts) 'type) + (semantic-tag-name ts) + (car ts)))) + (and ts (string-match regexp ts)))) + streamorbuffer search-parts search-includes)) + +(defun semantic-brute-find-tag-by-name-regexp + (regex streamorbuffer &optional search-parts search-includes) + "Find all tags whose name match REGEX in STREAMORBUFFER. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'." + (semantic-brute-find-tag-by-function + (lambda (tag) (string-match regex (semantic-tag-name tag))) + streamorbuffer search-parts search-includes) + ) + +(defun semantic-brute-find-tag-by-property + (property value streamorbuffer &optional search-parts search-includes) + "Find all tags with PROPERTY equal to VALUE in STREAMORBUFFER. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'." + (semantic-brute-find-tag-by-function + (lambda (tag) (equal (semantic--tag-get-property tag property) value)) + streamorbuffer search-parts search-includes) + ) + +(defun semantic-brute-find-tag-by-attribute + (attr streamorbuffer &optional search-parts search-includes) + "Find all tags with a given ATTR in STREAMORBUFFER. +ATTR is a symbol key into the attributes list. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'." + (semantic-brute-find-tag-by-function + (lambda (tag) (semantic-tag-get-attribute tag attr)) + streamorbuffer search-parts search-includes) + ) + +(defun semantic-brute-find-tag-by-attribute-value + (attr value streamorbuffer &optional search-parts search-includes) + "Find all tags with a given ATTR equal to VALUE in STREAMORBUFFER. +ATTR is a symbol key into the attributes list. +VALUE is the value that ATTR should match. +Optional argument SEARCH-PARTS and SEARCH-INCLUDES are passed to +`semantic-brute-find-tag-by-function'." + (semantic-brute-find-tag-by-function + (lambda (tag) (equal (semantic-tag-get-attribute tag attr) value)) + streamorbuffer search-parts search-includes) + ) + +(defun semantic-brute-find-tag-by-function + (function streamorbuffer &optional search-parts search-includes) + "Find all tags for which FUNCTION's value is non-nil within STREAMORBUFFER. +FUNCTION must return non-nil if an element of STREAM will be included +in the new list. + +If optional argument SEARCH-PARTS is non-nil, all sub-parts of tags +are searched. The overloadable function `semantic-tag-componenets' is +used for the searching child lists. If SEARCH-PARTS is the symbol +'positiononly, then only children that have positional information are +searched. + +If SEARCH-INCLUDES has not been implemented. +This parameter hasn't be active for a while and is obsolete." + (let ((stream (semantic-something-to-tag-table streamorbuffer)) + (sl nil) ;list of tag children + (nl nil) ;new list + (case-fold-search semantic-case-fold)) + (dolist (tag stream) + (if (not (semantic-tag-p tag)) + ;; `semantic-tag-components-with-overlays' can return invalid + ;; tags if search-parts is not equal to 'positiononly + nil ;; Ignore them! + (if (funcall function tag) + (setq nl (cons tag nl))) + (and search-parts + (setq sl (if (eq search-parts 'positiononly) + (semantic-tag-components-with-overlays tag) + (semantic-tag-components tag)) + ) + (setq nl (nconc nl + (semantic-brute-find-tag-by-function + function sl + search-parts)))))) + (setq nl (nreverse nl)) + nl)) + +(defun semantic-brute-find-first-tag-by-function + (function streamorbuffer &optional search-parts search-includes) + "Find the first tag which FUNCTION match within STREAMORBUFFER. +FUNCTION must return non-nil if an element of STREAM will be included +in the new list. + +The following parameters were never implemented. + +If optional argument SEARCH-PARTS, all sub-parts of tags are searched. +The overloadable function `semantic-tag-components' is used for +searching. +If SEARCH-INCLUDES is non-nil, then all include files are also +searched for matches." + (let ((stream (semantic-something-to-tag-table streamorbuffer)) + (found nil) + (case-fold-search semantic-case-fold)) + (while (and (not found) stream) + (if (funcall function (car stream)) + (setq found (car stream))) + (setq stream (cdr stream))) + found)) + + +;;; Old Positional Searches +;; +;; Are these useful anymore? +;; +(defun semantic-brute-find-tag-by-position (position streamorbuffer + &optional nomedian) + "Find a tag covering POSITION within STREAMORBUFFER. +POSITION is a number, or marker. If NOMEDIAN is non-nil, don't do +the median calculation, and return nil." + (save-excursion + (if (markerp position) (set-buffer (marker-buffer position))) + (let* ((stream (if (bufferp streamorbuffer) + (save-excursion + (set-buffer streamorbuffer) + (semantic-fetch-tags)) + streamorbuffer)) + (prev nil) + (found nil)) + (while (and stream (not found)) + ;; perfect fit + (if (and (>= position (semantic-tag-start (car stream))) + (<= position (semantic-tag-end (car stream)))) + (setq found (car stream)) + ;; Median between to objects. + (if (and prev (not nomedian) + (>= position (semantic-tag-end prev)) + (<= position (semantic-tag-start (car stream)))) + (let ((median (/ (+ (semantic-tag-end prev) + (semantic-tag-start (car stream))) + 2))) + (setq found + (if (> position median) + (car stream) + prev))))) + ;; Next!!! + (setq prev (car stream) + stream (cdr stream))) + found))) + +(defun semantic-brute-find-innermost-tag-by-position + (position streamorbuffer &optional nomedian) + "Find a list of tags covering POSITION within STREAMORBUFFER. +POSITION is a number, or marker. If NOMEDIAN is non-nil, don't do +the median calculation, and return nil. +This function will find the topmost item, and recurse until no more +details are available of findable." + (let* ((returnme nil) + (current (semantic-brute-find-tag-by-position + position streamorbuffer nomedian)) + (nextstream (and current + (if (eq (semantic-tag-class current) 'type) + (semantic-tag-type-members current) + nil)))) + (while nextstream + (setq returnme (cons current returnme)) + (setq current (semantic-brute-find-tag-by-position + position nextstream nomedian)) + (setq nextstream (and current + ;; NOTE TO SELF: + ;; Looking at this after several years away, + ;; what does this do??? + (if (eq (semantic-tag-class current) 'token) + (semantic-tag-type-members current) + nil)))) + (nreverse (cons current returnme)))) + +(provide 'semantic/find) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/find" +;; End: + +;;; semantic/find.el ends here diff --git a/lisp/cedet/semantic/format.el b/lisp/cedet/semantic/format.el new file mode 100644 index 00000000000..13945931b3f --- /dev/null +++ b/lisp/cedet/semantic/format.el @@ -0,0 +1,724 @@ +;;; semantic/format.el --- Routines for formatting tags + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, +;;; 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: +;; +;; Once a language file has been parsed into a TAG, it is often useful +;; then display that tag information in browsers, completion engines, or +;; help routines. The functions and setup in this file provide ways +;; to reformat a tag into different standard output types. +;; +;; In addition, macros for setting up customizable variables that let +;; the user choose their default format type are also provided. +;; + +;;; Code: +(eval-when-compile (require 'font-lock)) +(require 'semantic) +(require 'semantic/tag-ls) +(require 'ezimage) + +(eval-when-compile (require 'semantic/find)) + +;;; Tag to text overload functions +;; +;; abbreviations, prototypes, and coloring support. +(defvar semantic-format-tag-functions + '(semantic-format-tag-name + semantic-format-tag-canonical-name + semantic-format-tag-abbreviate + semantic-format-tag-summarize + semantic-format-tag-summarize-with-file + semantic-format-tag-short-doc + semantic-format-tag-prototype + semantic-format-tag-concise-prototype + semantic-format-tag-uml-abbreviate + semantic-format-tag-uml-prototype + semantic-format-tag-uml-concise-prototype + semantic-format-tag-prin1 + ) + "List of functions which convert a tag to text. +Each function must take the parameters TAG &optional PARENT COLOR. +TAG is the tag to convert. +PARENT is a parent tag or name which refers to the structure +or class which contains TAG. PARENT is NOT a class which a TAG +would claim as a parent. +COLOR indicates that the generated text should be colored using +`font-lock'.") + +(defvar semantic-format-tag-custom-list + (append '(radio) + (mapcar (lambda (f) (list 'const f)) + semantic-format-tag-functions) + '(function)) + "A List used by customizeable variables to choose a tag to text function. +Use this variable in the :type field of a customizable variable.") + +(defcustom semantic-format-use-images-flag ezimage-use-images + "Non-nil means semantic format functions use images. +Images can be used as icons instead of some types of text strings." + :group 'semantic + :type 'boolean) + +(defvar semantic-function-argument-separator "," + "Text used to separate arguments when creating text from tags.") +(make-variable-buffer-local 'semantic-function-argument-separator) + +(defvar semantic-format-parent-separator "::" + "Text used to separate names when between namespaces/classes and functions.") +(make-variable-buffer-local 'semantic-format-parent-separator) + +(defvar semantic-format-face-alist + `( (function . font-lock-function-name-face) + (variable . font-lock-variable-name-face) + (type . font-lock-type-face) + ;; These are different between Emacsen. + (include . ,(if (featurep 'xemacs) + 'font-lock-preprocessor-face + 'font-lock-constant-face)) + (package . ,(if (featurep 'xemacs) + 'font-lock-preprocessor-face + 'font-lock-constant-face)) + ;; Not a tag, but instead a feature of output + (label . font-lock-string-face) + (comment . font-lock-comment-face) + (keyword . font-lock-keyword-face) + (abstract . italic) + (static . underline) + (documentation . font-lock-doc-face) + ) + "Face used to colorize tags of different types. +Override the value locally if a language supports other tag types. +When adding new elements, try to use symbols also returned by the parser. +The form of an entry in this list is of the form: + ( SYMBOL . FACE ) +where SYMBOL is a tag type symbol used with semantic. FACE +is a symbol representing a face. +Faces used are generated in `font-lock' for consistency, and will not +be used unless font lock is a feature.") + + +;;; Coloring Functions +;; +(defun semantic--format-colorize-text (text face-class) + "Apply onto TEXT a color associated with FACE-CLASS. +FACE-CLASS is a tag type found in `semantic-format-face-alist'. +See that variable for details on adding new types." + (if (featurep 'font-lock) + (let ((face (cdr-safe (assoc face-class semantic-format-face-alist))) + (newtext (concat text))) + (put-text-property 0 (length text) 'face face newtext) + newtext) + text)) + +(defun semantic--format-colorize-merge-text (precoloredtext face-class) + "Apply onto PRECOLOREDTEXT a color associated with FACE-CLASS. +FACE-CLASS is a tag type found in `semantic-formatface-alist'. +See that variable for details on adding new types." + (let ((face (cdr-safe (assoc face-class semantic-format-face-alist))) + (newtext (concat precoloredtext)) + ) + (if (featurep 'xemacs) + (add-text-properties 0 (length newtext) (list 'face face) newtext) + (alter-text-property 0 (length newtext) 'face + (lambda (current-face) + (let ((cf + (cond ((facep current-face) + (list current-face)) + ((listp current-face) + current-face) + (t nil))) + (nf + (cond ((facep face) + (list face)) + ((listp face) + face) + (t nil)))) + (append cf nf))) + newtext)) + newtext)) + +;;; Function Arguments +;; +(defun semantic--format-tag-arguments (args formatter color) + "Format the argument list ARGS with FORMATTER. +FORMATTER is a function used to format a tag. +COLOR specifies if color should be used." + (let ((out nil)) + (while args + (push (if (and formatter + (semantic-tag-p (car args)) + (not (string= (semantic-tag-name (car args)) "")) + ) + (funcall formatter (car args) nil color) + (semantic-format-tag-name-from-anything + (car args) nil color 'variable)) + out) + (setq args (cdr args))) + (mapconcat 'identity (nreverse out) semantic-function-argument-separator) + )) + +;;; Data Type +(define-overloadable-function semantic-format-tag-type (tag color) + "Convert the data type of TAG to a string usable in tag formatting. +It is presumed that TYPE is a string or semantic tag.") + +(defun semantic-format-tag-type-default (tag color) + "Convert the data type of TAG to a string usable in tag formatting. +Argument COLOR specifies to colorize the text." + (let* ((type (semantic-tag-type tag)) + (out (cond ((semantic-tag-p type) + (let* ((typetype (semantic-tag-type type)) + (name (semantic-tag-name type)) + (str (if typetype + (concat typetype " " name) + name))) + (if color + (semantic--format-colorize-text + str + 'type) + str))) + ((and (listp type) + (stringp (car type))) + (car type)) + ((stringp type) + type) + (t nil)))) + (if (and color out) + (setq out (semantic--format-colorize-text out 'type)) + out) + )) + + +;;; Abstract formatting functions +;; + +(defun semantic-format-tag-prin1 (tag &optional parent color) + "Convert TAG to a string that is the print name for TAG. +PARENT and COLOR are ignored." + (format "%S" tag)) + +(defun semantic-format-tag-name-from-anything (anything &optional + parent color + colorhint) + "Convert just about anything into a name like string. +Argument ANYTHING is the thing to be converted. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors. +Optional COLORHINT is the type of color to use if ANYTHING is not a tag +with a tag class. See `semantic--format-colorize-text' for a definition +of FACE-CLASS for which this is used." + (cond ((stringp anything) + (semantic--format-colorize-text anything colorhint)) + ((semantic-tag-p anything) + (let ((ans (semantic-format-tag-name anything parent color))) + ;; If ANS is empty string or nil, then the name wasn't + ;; supplied. The implication is as in C where there is a data + ;; type but no name for a prototype from an include file, or + ;; an argument just wasn't used in the body of the fcn. + (if (or (null ans) (string= ans "")) + (setq ans (semantic-format-tag-type anything color))) + ans)) + ((and (listp anything) + (stringp (car anything))) + (semantic--format-colorize-text (car anything) colorhint)))) + +;;;###autoload +(define-overloadable-function semantic-format-tag-name (tag &optional parent color) + "Return the name string describing TAG. +The name is the shortest possible representation. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-name-default (tag &optional parent color) + "Return an abbreviated string describing TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let ((name (semantic-tag-name tag)) + (destructor + (if (eq (semantic-tag-class tag) 'function) + (semantic-tag-function-destructor-p tag)))) + (when destructor + (setq name (concat "~" name))) + (if color + (setq name (semantic--format-colorize-text name (semantic-tag-class tag)))) + name)) + +(declare-function semantic-go-to-tag "semantic/tag-file") + +(defun semantic--format-tag-parent-tree (tag parent) + "Under Consideration. + +Return a list of parents for TAG. +PARENT is the first parent, or nil. If nil, then an attempt to +determine PARENT is made. +Once PARENT is identified, additional parents are looked for. +The return list first element is the nearest parent, and the last +item is the first parent which may be a string. The root parent may +not be the actual first parent as there may just be a failure to find +local definitions." + ;; First, validate the PARENT argument. + (unless parent + ;; All mechanisms here must be fast as often parent + ;; is nil because there isn't one. + (setq parent (or (semantic-tag-function-parent tag) + (save-excursion + (require 'semantic/tag-file) + (semantic-go-to-tag tag) + (semantic-current-tag-parent))))) + (when (stringp parent) + (setq parent (semantic-find-first-tag-by-name + parent (current-buffer)))) + ;; Try and find a trail of parents from PARENT + (let ((rlist (list parent)) + ) + ;; IMPLELEMENT ME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + (reverse rlist))) + +(define-overloadable-function semantic-format-tag-canonical-name (tag &optional parent color) + "Return a canonical name for TAG. +A canonical name includes the names of any parents or namespaces preceeding +the tag. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-canonical-name-default (tag &optional parent color) + "Return a canonical name for TAG. +A canonical name includes the names of any parents or namespaces preceeding +the tag with colons separating them. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let ((parent-input-str + (if (and parent + (semantic-tag-p parent) + (semantic-tag-of-class-p parent 'type)) + (concat + ;; Choose a class of 'type as the default parent for something. + ;; Just a guess though. + (semantic-format-tag-name-from-anything parent nil color 'type) + ;; Default separator between class/namespace and others. + semantic-format-parent-separator) + "")) + (tag-parent-str + (or (when (and (semantic-tag-of-class-p tag 'function) + (semantic-tag-function-parent tag)) + (concat (semantic-tag-function-parent tag) + semantic-format-parent-separator)) + "")) + ) + (concat parent-input-str + tag-parent-str + (semantic-format-tag-name tag parent color)) + )) + +(define-overloadable-function semantic-format-tag-abbreviate (tag &optional parent color) + "Return an abbreviated string describing TAG. +The abbreviation is to be short, with possible symbols indicating +the type of tag, or other information. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-abbreviate-default (tag &optional parent color) + "Return an abbreviated string describing TAG. +Optional argument PARENT is a parent tag in the tag hierarchy. +In this case PARENT refers to containment, not inheritance. +Optional argument COLOR means highlight the prototype with font-lock colors. +This is a simple C like default." + ;; Do lots of complex stuff here. + (let ((class (semantic-tag-class tag)) + (name (semantic-format-tag-canonical-name tag parent color)) + (suffix "") + (prefix "") + str) + (cond ((eq class 'function) + (setq suffix "()")) + ((eq class 'include) + (setq suffix "<>")) + ((eq class 'variable) + (setq suffix (if (semantic-tag-variable-default tag) + "=" ""))) + ((eq class 'label) + (setq suffix ":")) + ((eq class 'code) + (setq prefix "{" + suffix "}")) + ((eq class 'type) + (setq suffix "{}")) + ) + (setq str (concat prefix name suffix)) + str)) + +;;;###autoload +(define-overloadable-function semantic-format-tag-summarize (tag &optional parent color) + "Summarize TAG in a reasonable way. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-summarize-default (tag &optional parent color) + "Summarize TAG in a reasonable way. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let* ((proto (semantic-format-tag-prototype tag nil color)) + (names (if parent + semantic-symbol->name-assoc-list-for-type-parts + semantic-symbol->name-assoc-list)) + (tsymb (semantic-tag-class tag)) + (label (capitalize (or (cdr-safe (assoc tsymb names)) + (symbol-name tsymb))))) + (if color + (setq label (semantic--format-colorize-text label 'label))) + (concat label ": " proto))) + +(define-overloadable-function semantic-format-tag-summarize-with-file (tag &optional parent color) + "Like `semantic-format-tag-summarize', but with the file name. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-summarize-with-file-default (tag &optional parent color) + "Summarize TAG in a reasonable way. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let* ((proto (semantic-format-tag-prototype tag nil color)) + (file (semantic-tag-file-name tag)) + ) + ;; Nothing for tag? Try parent. + (when (and (not file) (and parent)) + (setq file (semantic-tag-file-name parent))) + ;; Don't include the file name if we can't find one, or it is the + ;; same as the current buffer. + (if (or (not file) + (string= file (buffer-file-name (current-buffer)))) + proto + (setq file (file-name-nondirectory file)) + (when color + (setq file (semantic--format-colorize-text file 'label))) + (concat file ": " proto)))) + +(define-overloadable-function semantic-format-tag-short-doc (tag &optional parent color) + "Display a short form of TAG's documentation. (Comments, or docstring.) +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(declare-function semantic-documentation-for-tag "semantic/doc") + +(defun semantic-format-tag-short-doc-default (tag &optional parent color) + "Display a short form of TAG's documentation. (Comments, or docstring.) +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let* ((fname (or (semantic-tag-file-name tag) + (when parent (semantic-tag-file-name parent)))) + (buf (or (semantic-tag-buffer tag) + (when parent (semantic-tag-buffer parent)))) + (doc (semantic-tag-docstring tag buf))) + (when (and (not doc) (not buf) fname) + ;; If there is no doc, and no buffer, but we have a filename, + ;; lets try again. + (save-match-data + (setq buf (find-file-noselect fname))) + (setq doc (semantic-tag-docstring tag buf))) + (when (not doc) + (require 'semantic/doc) + (setq doc (semantic-documentation-for-tag tag)) + ) + (setq doc + (if (not doc) + ;; No doc, use summarize. + (semantic-format-tag-summarize tag parent color) + ;; We have doc. Can we devise a single line? + (if (string-match "$" doc) + (substring doc 0 (match-beginning 0)) + doc) + )) + (when color + (setq doc (semantic--format-colorize-text doc 'documentation))) + doc + )) + +;;; Prototype generation +;; +;;;###autoload +(define-overloadable-function semantic-format-tag-prototype (tag &optional parent color) + "Return a prototype for TAG. +This function should be overloaded, though it need not be used. +This is because it can be used to create code by language independent +tools. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-prototype-default (tag &optional parent color) + "Default method for returning a prototype for TAG. +This will work for C like languages. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let* ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color)) + (type (if (member class '(function variable type)) + (semantic-format-tag-type tag color))) + (args (if (member class '(function type)) + (semantic--format-tag-arguments + (if (eq class 'function) + (semantic-tag-function-arguments tag) + (list "") + ;;(semantic-tag-type-members tag) + ) + #'semantic-format-tag-prototype + color))) + (const (semantic-tag-get-attribute tag :constant-flag)) + (tm (semantic-tag-get-attribute tag :typemodifiers)) + (mods (append + (if const '("const") nil) + (cond ((stringp tm) (list tm)) + ((consp tm) tm) + (t nil)) + )) + (array (if (eq class 'variable) + (let ((deref + (semantic-tag-get-attribute + tag :dereference)) + (r "")) + (while (and deref (/= deref 0)) + (setq r (concat r "[]") + deref (1- deref))) + r))) + ) + (if args + (setq args + (concat " " + (if (eq class 'type) "{" "(") + args + (if (eq class 'type) "}" ")")))) + (when mods + (setq mods (concat (mapconcat 'identity mods " ") " "))) + (concat (or mods "") + (if type (concat type " ")) + name + (or args "") + (or array "")))) + +;;;###autoload +(define-overloadable-function semantic-format-tag-concise-prototype (tag &optional parent color) + "Return a concise prototype for TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-concise-prototype-default (tag &optional parent color) + "Return a concise prototype for TAG. +This default function will make a cheap concise prototype using C like syntax. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let ((class (semantic-tag-class tag))) + (cond + ((eq class 'type) + (concat (semantic-format-tag-name tag parent color) "{}")) + ((eq class 'function) + (concat (semantic-format-tag-name tag parent color) + " (" + (semantic--format-tag-arguments + (semantic-tag-function-arguments tag) + 'semantic-format-tag-concise-prototype + color) + ")")) + ((eq class 'variable) + (let* ((deref (semantic-tag-get-attribute + tag :dereference)) + (array "") + ) + (while (and deref (/= deref 0)) + (setq array (concat array "[]") + deref (1- deref))) + (concat (semantic-format-tag-name tag parent color) + array))) + (t + (semantic-format-tag-abbreviate tag parent color))))) + +;;; UML display styles +;; +(defcustom semantic-uml-colon-string " : " + "*String used as a color separator between parts of a UML string. +In UML, a variable may appear as `varname : type'. +Change this variable to change the output separator." + :group 'semantic + :type 'string) + +(defcustom semantic-uml-no-protection-string "" + "*String used to describe when no protection is specified. +Used by `semantic-format-tag-uml-protection-to-string'." + :group 'semantic + :type 'string) + +(defun semantic--format-uml-post-colorize (text tag parent) + "Add color to TEXT created from TAG and PARENT. +Adds augmentation for `abstract' and `static' entries." + (if (semantic-tag-abstract-p tag parent) + (setq text (semantic--format-colorize-merge-text text 'abstract))) + (if (semantic-tag-static-p tag parent) + (setq text (semantic--format-colorize-merge-text text 'static))) + text + ) + +(defun semantic-uml-attribute-string (tag &optional parent) + "Return a string for TAG, a child of PARENT representing a UML attribute. +UML attribute strings are things like {abstract} or {leaf}." + (cond ((semantic-tag-abstract-p tag parent) + "{abstract}") + ((semantic-tag-leaf-p tag parent) + "{leaf}") + )) + +(defvar semantic-format-tag-protection-image-alist + '(("+" . ezimage-unlock) + ("#" . ezimage-key) + ("-" . ezimage-lock) + ) + "Association of protection strings, and images to use.") + +(defvar semantic-format-tag-protection-symbol-to-string-assoc-list + '((public . "+") + (protected . "#") + (private . "-") + ) + "Association list of the form (SYMBOL . \"STRING\") for protection symbols. +This associates a symbol, such as 'public with the st ring \"+\".") + +(define-overloadable-function semantic-format-tag-uml-protection-to-string (protection-symbol color) + "Convert PROTECTION-SYMBOL to a string for UML. +By default, uses `semantic-format-tag-protection-symbol-to-string-assoc-list' +to convert. +By defaul character returns are: + public -- + + private -- - + protected -- #. +If PROTECTION-SYMBOL is unknown, then the return value is +`semantic-uml-no-protection-string'. +COLOR indicates if we should use an image on the text.") + +(defun semantic-format-tag-uml-protection-to-string-default (protection-symbol color) + "Convert PROTECTION-SYMBOL to a string for UML. +Uses `semantic-format-tag-protection-symbol-to-string-assoc-list' to convert. +If PROTECTION-SYMBOL is unknown, then the return value is +`semantic-uml-no-protection-string'. +COLOR indicates if we should use an image on the text." + (let* ((ezimage-use-images (and semantic-format-use-images-flag color)) + (key (assoc protection-symbol + semantic-format-tag-protection-symbol-to-string-assoc-list)) + (str (or (cdr-safe key) semantic-uml-no-protection-string))) + (ezimage-image-over-string + (copy-sequence str) ; make a copy to keep the original pristine. + semantic-format-tag-protection-image-alist))) + +(defsubst semantic-format-tag-uml-protection (tag parent color) + "Retrieve the protection string for TAG with PARENT. +Argument COLOR specifies that color should be added to the string as +needed." + (semantic-format-tag-uml-protection-to-string + (semantic-tag-protection tag parent) + color)) + +(defun semantic--format-tag-uml-type (tag color) + "Format the data type of TAG to a string usable for formatting. +COLOR indicates if it should be colorized." + (let ((str (semantic-format-tag-type tag color))) + (if str + (concat semantic-uml-colon-string str)))) + +(define-overloadable-function semantic-format-tag-uml-abbreviate (tag &optional parent color) + "Return a UML style abbreviation for TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-uml-abbreviate-default (tag &optional parent color) + "Return a UML style abbreviation for TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let* ((name (semantic-format-tag-name tag parent color)) + (type (semantic--format-tag-uml-type tag color)) + (protstr (semantic-format-tag-uml-protection tag parent color)) + (text nil)) + (setq text + (concat + protstr + (if type (concat name type) + name))) + (if color + (setq text (semantic--format-uml-post-colorize text tag parent))) + text)) + +(define-overloadable-function semantic-format-tag-uml-prototype (tag &optional parent color) + "Return a UML style prototype for TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-uml-prototype-default (tag &optional parent color) + "Return a UML style prototype for TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let* ((class (semantic-tag-class tag)) + (cp (semantic-format-tag-name tag parent color)) + (type (semantic--format-tag-uml-type tag color)) + (prot (semantic-format-tag-uml-protection tag parent color)) + (argtext + (cond ((eq class 'function) + (concat + " (" + (semantic--format-tag-arguments + (semantic-tag-function-arguments tag) + #'semantic-format-tag-uml-prototype + color) + ")")) + ((eq class 'type) + "{}"))) + (text nil)) + (setq text (concat prot cp argtext type)) + (if color + (setq text (semantic--format-uml-post-colorize text tag parent))) + text + )) + +(define-overloadable-function semantic-format-tag-uml-concise-prototype (tag &optional parent color) + "Return a UML style concise prototype for TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors.") + +(defun semantic-format-tag-uml-concise-prototype-default (tag &optional parent color) + "Return a UML style concise prototype for TAG. +Optional argument PARENT is the parent type if TAG is a detail. +Optional argument COLOR means highlight the prototype with font-lock colors." + (let* ((cp (semantic-format-tag-concise-prototype tag parent color)) + (type (semantic--format-tag-uml-type tag color)) + (prot (semantic-format-tag-uml-protection tag parent color)) + (text nil) + ) + (setq text (concat prot cp type)) + (if color + (setq text (semantic--format-uml-post-colorize text tag parent))) + text)) + +(provide 'semantic/format) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/format" +;; End: + +;;; semantic/format.el ends here diff --git a/lisp/cedet/semantic/fw.el b/lisp/cedet/semantic/fw.el new file mode 100644 index 00000000000..9f9bcaaea23 --- /dev/null +++ b/lisp/cedet/semantic/fw.el @@ -0,0 +1,387 @@ +;;; semantic/fw.el --- Framework for Semantic + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Semantic has several core features shared across it's lex/parse/util +;; stages. This used to clutter semantic.el some. These routines are all +;; simple things that are not parser specific, but aid in making +;; semantic flexible and compatible amongst different Emacs platforms. + +;;; Code: +;; +(require 'mode-local) +(require 'eieio) +(require 'semantic/loaddefs) + +;;; Compatibility + +(defalias 'semantic-buffer-local-value 'buffer-local-value) +(defalias 'semantic-overlay-live-p 'overlay-buffer) +(defalias 'semantic-make-overlay 'make-overlay) +(defalias 'semantic-overlay-put 'overlay-put) +(defalias 'semantic-overlay-get 'overlay-get) +(defalias 'semantic-overlay-properties 'overlay-properties) +(defalias 'semantic-overlay-move 'move-overlay) +(defalias 'semantic-overlay-delete 'delete-overlay) +(defalias 'semantic-overlays-at 'overlays-at) +(defalias 'semantic-overlays-in 'overlays-in) +(defalias 'semantic-overlay-buffer 'overlay-buffer) +(defalias 'semantic-overlay-start 'overlay-start) +(defalias 'semantic-overlay-end 'overlay-end) +(defalias 'semantic-overlay-size 'overlay-size) +(defalias 'semantic-overlay-next-change 'next-overlay-change) +(defalias 'semantic-overlay-previous-change 'previous-overlay-change) +(defalias 'semantic-overlay-lists 'overlay-lists) +(defalias 'semantic-overlay-p 'overlayp) +(defalias 'semantic-read-event 'read-event) +(defalias 'semantic-popup-menu 'popup-menu) +(defalias 'semantic-make-local-hook 'identity) +(defalias 'semantic-mode-line-update 'force-mode-line-update) +(defalias 'semantic-run-mode-hooks 'run-mode-hooks) +(defalias 'semantic-compile-warn 'byte-compile-warn) +(defalias 'semantic-menu-item 'identity) + +(defun semantic-event-window (event) + "Extract the window from EVENT." + (car (car (cdr event)))) + +(defun semantic-delete-overlay-maybe (overlay) + "Delete OVERLAY if it is a semantic token overlay." + (if (semantic-overlay-get overlay 'semantic) + (semantic-overlay-delete overlay))) + +;;; Positional Data Cache +;; +(defvar semantic-cache-data-overlays nil + "List of all overlays waiting to be flushed.") + +(defun semantic-cache-data-to-buffer (buffer start end value name &optional lifespan) + "In BUFFER over the region START END, remember VALUE. +NAME specifies a special name that can be searched for later to +recover the cached data with `semantic-get-cache-data'. +LIFESPAN indicates how long the data cache will be remembered. +The default LIFESPAN is 'end-of-command. +Possible Lifespans are: + 'end-of-command - Remove the cache at the end of the currently + executing command. + 'exit-cache-zone - Remove when point leaves the overlay at the + end of the currently executing command." + ;; Check if LIFESPAN is valid before to create any overlay + (or lifespan (setq lifespan 'end-of-command)) + (or (memq lifespan '(end-of-command exit-cache-zone)) + (error "semantic-cache-data-to-buffer: Unknown LIFESPAN: %s" + lifespan)) + (let ((o (semantic-make-overlay start end buffer))) + (semantic-overlay-put o 'cache-name name) + (semantic-overlay-put o 'cached-value value) + (semantic-overlay-put o 'lifespan lifespan) + (setq semantic-cache-data-overlays + (cons o semantic-cache-data-overlays)) + ;;(message "Adding to cache: %s" o) + (add-hook 'post-command-hook 'semantic-cache-data-post-command-hook) + )) + +(defun semantic-cache-data-post-command-hook () + "Flush `semantic-cache-data-overlays' based 'lifespan property. +Remove self from `post-command-hook' if it is empty." + (let ((newcache nil) + (oldcache semantic-cache-data-overlays)) + (while oldcache + (let* ((o (car oldcache)) + (life (semantic-overlay-get o 'lifespan)) + ) + (if (or (eq life 'end-of-command) + (and (eq life 'exit-cache-zone) + (not (member o (semantic-overlays-at (point)))))) + (progn + ;;(message "Removing from cache: %s" o) + (semantic-overlay-delete o) + ) + (setq newcache (cons o newcache)))) + (setq oldcache (cdr oldcache))) + (setq semantic-cache-data-overlays (nreverse newcache))) + + ;; Remove ourselves if we have removed all overlays. + (unless semantic-cache-data-overlays + (remove-hook 'post-command-hook + 'semantic-cache-data-post-command-hook))) + +(defun semantic-get-cache-data (name &optional point) + "Get cached data with NAME from optional POINT." + (save-excursion + (if point (goto-char point)) + (let ((o (semantic-overlays-at (point))) + (ans nil)) + (while (and (not ans) o) + (if (equal (semantic-overlay-get (car o) 'cache-name) name) + (setq ans (car o)) + (setq o (cdr o)))) + (when ans + (semantic-overlay-get ans 'cached-value))))) + +;;; Obsoleting various functions & variables +;; +(defun semantic-overload-symbol-from-function (name) + "Return the symbol for overload used by NAME, the defined symbol." + (let ((sym-name (symbol-name name))) + (if (string-match "^semantic-" sym-name) + (intern (substring sym-name (match-end 0))) + name))) + +(defun semantic-alias-obsolete (oldfnalias newfn) + "Make OLDFNALIAS an alias for NEWFN. +Mark OLDFNALIAS as obsolete, such that the byte compiler +will throw a warning when it encounters this symbol." + (defalias oldfnalias newfn) + (make-obsolete oldfnalias newfn) + (when (and (function-overload-p newfn) + (not (overload-obsoleted-by newfn)) + ;; Only throw this warning when byte compiling things. + (boundp 'byte-compile-current-file) + byte-compile-current-file + (not (string-match "cedet" byte-compile-current-file)) + ) + (make-obsolete-overload oldfnalias newfn) + (semantic-compile-warn + "%s: `%s' obsoletes overload `%s'" + byte-compile-current-file + newfn + (semantic-overload-symbol-from-function oldfnalias)) + )) + +(defun semantic-varalias-obsolete (oldvaralias newvar) + "Make OLDVARALIAS an alias for variable NEWVAR. +Mark OLDVARALIAS as obsolete, such that the byte compiler +will throw a warning when it encounters this symbol." + (make-obsolete-variable oldvaralias newvar) + (condition-case nil + (defvaralias oldvaralias newvar) + (error + ;; Only throw this warning when byte compiling things. + (when (and (boundp 'byte-compile-current-file) + byte-compile-current-file) + (semantic-compile-warn + "variable `%s' obsoletes, but isn't alias of `%s'" + newvar oldvaralias) + )))) + +;;; Help debugging +;; +(defmacro semantic-safe (format &rest body) + "Turn into a FORMAT message any error caught during eval of BODY. +Return the value of last BODY form or nil if an error occurred. +FORMAT can have a %s escape which will be replaced with the actual +error message. +If `debug-on-error' is set, errors are not caught, so that you can +debug them. +Avoid using a large BODY since it is duplicated." + ;;(declare (debug t) (indent 1)) + `(if debug-on-error + ;;(let ((inhibit-quit nil)) ,@body) + ;; Note to self: Doing the above screws up the wisent parser. + (progn ,@body) + (condition-case err + (progn ,@body) + (error + (message ,format (format "%S - %s" (current-buffer) + (error-message-string err))) + nil)))) +(put 'semantic-safe 'lisp-indent-function 1) + +;;; Misc utilities +;; +(defsubst semantic-map-buffers (function) + "Run FUNCTION for each Semantic enabled buffer found. +FUNCTION does not have arguments. When FUNCTION is entered +`current-buffer' is a selected Semantic enabled buffer." + (mode-local-map-file-buffers function #'semantic-active-p)) + +(defalias 'semantic-map-mode-buffers 'mode-local-map-mode-buffers) + +(semantic-alias-obsolete 'define-mode-overload-implementation + 'define-mode-local-override) + +(defun semantic-install-function-overrides (overrides &optional transient mode) + "Install the function OVERRIDES in the specified environment. +OVERRIDES must be an alist ((OVERLOAD . FUNCTION) ...) where OVERLOAD +is a symbol identifying an overloadable entry, and FUNCTION is the +function to override it with. +If optional argument TRANSIENT is non-nil, installed overrides can in +turn be overridden by next installation. +If optional argument MODE is non-nil, it must be a major mode symbol. +OVERRIDES will be installed globally for this major mode. If MODE is +nil, OVERRIDES will be installed locally in the current buffer. This +later installation should be done in MODE hook." + (mode-local-bind + ;; Add the semantic- prefix to OVERLOAD short names. + (mapcar + #'(lambda (e) + (let ((name (symbol-name (car e)))) + (if (string-match "^semantic-" name) + e + (cons (intern (format "semantic-%s" name)) (cdr e))))) + overrides) + (list 'constant-flag (not transient) + 'override-flag t) + mode)) + +;;; User Interrupt handling +;; +(defvar semantic-current-input-throw-symbol nil + "The current throw symbol for `semantic-exit-on-input'.") + +(defmacro semantic-exit-on-input (symbol &rest forms) + "Using SYMBOL as an argument to `throw', execute FORMS. +If FORMS includes a call to `semantic-thow-on-input', then +if a user presses any key during execution, this form macro +will exit with the value passed to `semantic-throw-on-input'. +If FORMS completes, then the return value is the same as `progn'." + `(let ((semantic-current-input-throw-symbol ,symbol)) + (catch ,symbol + ,@forms))) +(put 'semantic-exit-on-input 'lisp-indent-function 1) + +(defmacro semantic-throw-on-input (from) + "Exit with `throw' when in `semantic-exit-on-input' on user input. +FROM is an indication of where this function is called from as a value +to pass to `throw'. It is recommended to use the name of the function +calling this one." + `(when (and semantic-current-input-throw-symbol + (or (input-pending-p) (accept-process-output))) + (throw semantic-current-input-throw-symbol ,from))) + + +;;; Special versions of Find File +;; +(defun semantic-find-file-noselect (file &optional nowarn rawfile wildcards) + "Call `find-file-noselect' with various features turned off. +Use this when referencing a file that will be soon deleted. +FILE, NOWARN, RAWFILE, and WILDCARDS are passed into `find-file-noselect'" + (let* ((recentf-exclude '( (lambda (f) t) )) + ;; This is a brave statement. Don't waste time loading in + ;; lots of modes. Especially decoration mode can waste a lot + ;; of time for a buffer we intend to kill. + (semantic-init-hook nil) + ;; This disables the part of EDE that asks questions + (ede-auto-add-method 'never) + ;; Ask font-lock to not colorize these buffers, nor to + ;; whine about it either. + (font-lock-maximum-size 0) + (font-lock-verbose nil) + ;; Disable revision control + (vc-handled-backends nil) + ;; Don't prompt to insert a template if we visit an empty file + (auto-insert nil) + ;; We don't want emacs to query about unsafe local variables + (enable-local-variables + (if (featurep 'xemacs) + ;; XEmacs only has nil as an option? + nil + ;; Emacs 23 has the spiffy :safe option, nil otherwise. + (if (>= emacs-major-version 22) + nil + :safe))) + ;; ... or eval variables + (enable-local-eval nil) + ) + (save-match-data + (if (featurep 'xemacs) + (find-file-noselect file nowarn rawfile) + (find-file-noselect file nowarn rawfile wildcards))) + )) + + +;; ;;; Editor goodies ;-) +;; ;; +;; (defconst semantic-fw-font-lock-keywords +;; (eval-when-compile +;; (let* ( +;; ;; Variable declarations +;; (vl nil) +;; (kv (if vl (regexp-opt vl t) "")) +;; ;; Function declarations +;; (vf '( +;; "define-lex" +;; "define-lex-analyzer" +;; "define-lex-block-analyzer" +;; "define-lex-regex-analyzer" +;; "define-lex-spp-macro-declaration-analyzer" +;; "define-lex-spp-macro-undeclaration-analyzer" +;; "define-lex-spp-include-analyzer" +;; "define-lex-simple-regex-analyzer" +;; "define-lex-keyword-type-analyzer" +;; "define-lex-sexp-type-analyzer" +;; "define-lex-regex-type-analyzer" +;; "define-lex-string-type-analyzer" +;; "define-lex-block-type-analyzer" +;; ;;"define-mode-overload-implementation" +;; ;;"define-semantic-child-mode" +;; "define-semantic-idle-service" +;; "define-semantic-decoration-style" +;; "define-wisent-lexer" +;; "semantic-alias-obsolete" +;; "semantic-varalias-obsolete" +;; "semantic-make-obsolete-overload" +;; "defcustom-mode-local-semantic-dependency-system-include-path" +;; )) +;; (kf (if vf (regexp-opt vf t) "")) +;; ;; Regexp depths +;; (kv-depth (if kv (regexp-opt-depth kv) nil)) +;; (kf-depth (if kf (regexp-opt-depth kf) nil)) +;; ) +;; `((,(concat +;; ;; Declarative things +;; "(\\(" kv "\\|" kf "\\)" +;; ;; Whitespaces & names +;; "\\>[ \t]*\\(\\sw+\\)?[ \t]*\\(\\sw+\\)?" +;; ) +;; (1 font-lock-keyword-face) +;; (,(+ 1 kv-depth kf-depth 1) +;; (cond ((match-beginning 2) +;; font-lock-type-face) +;; ((match-beginning ,(+ 1 kv-depth 1)) +;; font-lock-function-name-face) +;; ) +;; nil t) +;; (,(+ 1 kv-depth kf-depth 1 1) +;; (cond ((match-beginning 2) +;; font-lock-variable-name-face) +;; ) +;; nil t))) +;; )) +;; "Highlighted Semantic keywords.") + +;; (when (fboundp 'font-lock-add-keywords) +;; (font-lock-add-keywords 'emacs-lisp-mode +;; semantic-fw-font-lock-keywords)) + +;;; Interfacing with edebug +;; +(defun semantic-fw-add-edebug-spec () + (def-edebug-spec semantic-exit-on-input 'def-body)) + +(add-hook 'edebug-setup-hook 'semantic-fw-add-edebug-spec) + +(provide 'semantic/fw) + +;;; semantic/fw.el ends here diff --git a/lisp/cedet/semantic/grammar-wy.el b/lisp/cedet/semantic/grammar-wy.el new file mode 100644 index 00000000000..ae1aec7b466 --- /dev/null +++ b/lisp/cedet/semantic/grammar-wy.el @@ -0,0 +1,478 @@ +;;; semantic/grammar-wy.el --- Generated parser support file + +;; Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> +;; 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: +;; +;; This file is generated from the grammar file semantic-grammar.wy in +;; the upstream CEDET repository. + +;;; Code: + +(require 'semantic/lex) +(defvar semantic-grammar-lex-c-char-re) + +;; Current parsed nonterminal name. +(defvar semantic-grammar-wy--nterm nil) +;; Index of rule in a nonterminal clause. +(defvar semantic-grammar-wy--rindx nil) + +;;; Declarations +;; +(defconst semantic-grammar-wy--keyword-table + (semantic-lex-make-keyword-table + '(("%default-prec" . DEFAULT-PREC) + ("%no-default-prec" . NO-DEFAULT-PREC) + ("%keyword" . KEYWORD) + ("%languagemode" . LANGUAGEMODE) + ("%left" . LEFT) + ("%nonassoc" . NONASSOC) + ("%package" . PACKAGE) + ("%prec" . PREC) + ("%put" . PUT) + ("%quotemode" . QUOTEMODE) + ("%right" . RIGHT) + ("%scopestart" . SCOPESTART) + ("%start" . START) + ("%token" . TOKEN) + ("%type" . TYPE) + ("%use-macros" . USE-MACROS)) + 'nil) + "Table of language keywords.") + +(defconst semantic-grammar-wy--token-table + (semantic-lex-make-type-table + '(("punctuation" + (GT . ">") + (LT . "<") + (OR . "|") + (SEMI . ";") + (COLON . ":")) + ("close-paren" + (RBRACE . "}") + (RPAREN . ")")) + ("open-paren" + (LBRACE . "{") + (LPAREN . "(")) + ("block" + (BRACE_BLOCK . "(LBRACE RBRACE)") + (PAREN_BLOCK . "(LPAREN RPAREN)")) + ("code" + (EPILOGUE . "%%...EOF") + (PROLOGUE . "%{...%}")) + ("sexp" + (SEXP)) + ("qlist" + (PREFIXED_LIST)) + ("char" + (CHARACTER)) + ("symbol" + (PERCENT_PERCENT . "\\`%%\\'") + (SYMBOL)) + ("string" + (STRING))) + '(("punctuation" :declared t) + ("block" :declared t) + ("sexp" matchdatatype sexp) + ("sexp" syntax "\\=") + ("sexp" :declared t) + ("qlist" matchdatatype sexp) + ("qlist" syntax "\\s'\\s-*(") + ("qlist" :declared t) + ("char" syntax semantic-grammar-lex-c-char-re) + ("char" :declared t) + ("symbol" syntax ":?\\(\\sw\\|\\s_\\)+") + ("symbol" :declared t) + ("string" :declared t) + ("keyword" :declared t))) + "Table of lexical tokens.") + +(defconst semantic-grammar-wy--parse-table + (progn + (eval-when-compile + (require 'semantic/wisent/comp)) + (wisent-compile-grammar + '((DEFAULT-PREC NO-DEFAULT-PREC KEYWORD LANGUAGEMODE LEFT NONASSOC PACKAGE PREC PUT QUOTEMODE RIGHT SCOPESTART START TOKEN TYPE USE-MACROS STRING SYMBOL PERCENT_PERCENT CHARACTER PREFIXED_LIST SEXP PROLOGUE EPILOGUE PAREN_BLOCK BRACE_BLOCK LPAREN RPAREN LBRACE RBRACE COLON SEMI OR LT GT) + nil + (grammar + ((prologue)) + ((epilogue)) + ((declaration)) + ((nonterminal)) + ((PERCENT_PERCENT))) + (prologue + ((PROLOGUE) + (wisent-raw-tag + (semantic-tag-new-code "prologue" nil)))) + (epilogue + ((EPILOGUE) + (wisent-raw-tag + (semantic-tag-new-code "epilogue" nil)))) + (declaration + ((decl) + (eval $1))) + (decl + ((default_prec_decl)) + ((no_default_prec_decl)) + ((languagemode_decl)) + ((package_decl)) + ((precedence_decl)) + ((put_decl)) + ((quotemode_decl)) + ((scopestart_decl)) + ((start_decl)) + ((keyword_decl)) + ((token_decl)) + ((type_decl)) + ((use_macros_decl))) + (default_prec_decl + ((DEFAULT-PREC) + `(wisent-raw-tag + (semantic-tag "default-prec" 'assoc :value + '("t"))))) + (no_default_prec_decl + ((NO-DEFAULT-PREC) + `(wisent-raw-tag + (semantic-tag "default-prec" 'assoc :value + '("nil"))))) + (languagemode_decl + ((LANGUAGEMODE symbols) + `(wisent-raw-tag + (semantic-tag ',(car $2) + 'languagemode :rest ',(cdr $2))))) + (package_decl + ((PACKAGE SYMBOL) + `(wisent-raw-tag + (semantic-tag-new-package ',$2 nil)))) + (precedence_decl + ((associativity token_type_opt items) + `(wisent-raw-tag + (semantic-tag ',$1 'assoc :type ',$2 :value ',$3)))) + (associativity + ((LEFT) + (progn "left")) + ((RIGHT) + (progn "right")) + ((NONASSOC) + (progn "nonassoc"))) + (put_decl + ((PUT put_name put_value) + `(wisent-raw-tag + (semantic-tag ',$2 'put :value ',(list $3)))) + ((PUT put_name put_value_list) + `(wisent-raw-tag + (semantic-tag ',$2 'put :value ',$3))) + ((PUT put_name_list put_value) + `(wisent-raw-tag + (semantic-tag ',(car $2) + 'put :rest ',(cdr $2) + :value ',(list $3)))) + ((PUT put_name_list put_value_list) + `(wisent-raw-tag + (semantic-tag ',(car $2) + 'put :rest ',(cdr $2) + :value ',$3)))) + (put_name_list + ((BRACE_BLOCK) + (mapcar 'semantic-tag-name + (semantic-parse-region + (car $region1) + (cdr $region1) + 'put_names 1)))) + (put_names + ((LBRACE) + nil) + ((RBRACE) + nil) + ((put_name) + (wisent-raw-tag + (semantic-tag $1 'put-name)))) + (put_name + ((SYMBOL)) + ((token_type))) + (put_value_list + ((BRACE_BLOCK) + (mapcar 'semantic-tag-code-detail + (semantic-parse-region + (car $region1) + (cdr $region1) + 'put_values 1)))) + (put_values + ((LBRACE) + nil) + ((RBRACE) + nil) + ((put_value) + (wisent-raw-tag + (semantic-tag-new-code "put-value" $1)))) + (put_value + ((SYMBOL any_value) + (cons $1 $2))) + (scopestart_decl + ((SCOPESTART SYMBOL) + `(wisent-raw-tag + (semantic-tag ',$2 'scopestart)))) + (quotemode_decl + ((QUOTEMODE SYMBOL) + `(wisent-raw-tag + (semantic-tag ',$2 'quotemode)))) + (start_decl + ((START symbols) + `(wisent-raw-tag + (semantic-tag ',(car $2) + 'start :rest ',(cdr $2))))) + (keyword_decl + ((KEYWORD SYMBOL string_value) + `(wisent-raw-tag + (semantic-tag ',$2 'keyword :value ',$3)))) + (token_decl + ((TOKEN token_type_opt SYMBOL string_value) + `(wisent-raw-tag + (semantic-tag ',$3 ',(if $2 'token 'keyword) + :type ',$2 :value ',$4))) + ((TOKEN token_type_opt symbols) + `(wisent-raw-tag + (semantic-tag ',(car $3) + 'token :type ',$2 :rest ',(cdr $3))))) + (token_type_opt + (nil) + ((token_type))) + (token_type + ((LT SYMBOL GT) + (progn $2))) + (type_decl + ((TYPE token_type plist_opt) + `(wisent-raw-tag + (semantic-tag ',$2 'type :value ',$3)))) + (plist_opt + (nil) + ((plist))) + (plist + ((plist put_value) + (append + (list $2) + $1)) + ((put_value) + (list $1))) + (use_name_list + ((BRACE_BLOCK) + (mapcar 'semantic-tag-name + (semantic-parse-region + (car $region1) + (cdr $region1) + 'use_names 1)))) + (use_names + ((LBRACE) + nil) + ((RBRACE) + nil) + ((SYMBOL) + (wisent-raw-tag + (semantic-tag $1 'use-name)))) + (use_macros_decl + ((USE-MACROS SYMBOL use_name_list) + `(wisent-raw-tag + (semantic-tag "macro" 'macro :type ',$2 :value ',$3)))) + (string_value + ((STRING) + (read $1))) + (any_value + ((SYMBOL)) + ((STRING)) + ((PAREN_BLOCK)) + ((PREFIXED_LIST)) + ((SEXP))) + (symbols + ((lifo_symbols) + (nreverse $1))) + (lifo_symbols + ((lifo_symbols SYMBOL) + (cons $2 $1)) + ((SYMBOL) + (list $1))) + (nonterminal + ((SYMBOL + (setq semantic-grammar-wy--nterm $1 semantic-grammar-wy--rindx 0) + COLON rules SEMI) + (wisent-raw-tag + (semantic-tag $1 'nonterminal :children $4)))) + (rules + ((lifo_rules) + (apply 'nconc + (nreverse $1)))) + (lifo_rules + ((lifo_rules OR rule) + (cons $3 $1)) + ((rule) + (list $1))) + (rule + ((rhs) + (let* + ((nterm semantic-grammar-wy--nterm) + (rindx semantic-grammar-wy--rindx) + (rhs $1) + comps prec action elt) + (setq semantic-grammar-wy--rindx + (1+ semantic-grammar-wy--rindx)) + (while rhs + (setq elt + (car rhs) + rhs + (cdr rhs)) + (cond + ((vectorp elt) + (if prec + (error "duplicate %%prec in `%s:%d' rule" nterm rindx)) + (setq prec + (aref elt 0))) + ((consp elt) + (if + (or action comps) + (setq comps + (cons elt comps) + semantic-grammar-wy--rindx + (1+ semantic-grammar-wy--rindx)) + (setq action + (car elt)))) + (t + (setq comps + (cons elt comps))))) + (wisent-cook-tag + (wisent-raw-tag + (semantic-tag + (format "%s:%d" nterm rindx) + 'rule :type + (if comps "group" "empty") + :value comps :prec prec :expr action)))))) + (rhs + (nil) + ((rhs item) + (cons $2 $1)) + ((rhs action) + (cons + (list $2) + $1)) + ((rhs PREC item) + (cons + (vector $3) + $1))) + (action + ((PAREN_BLOCK)) + ((PREFIXED_LIST)) + ((BRACE_BLOCK) + (format "(progn\n%s)" + (let + ((s $1)) + (if + (string-match "^{[
\n ]*" s) + (setq s + (substring s + (match-end 0)))) + (if + (string-match "[
\n ]*}$" s) + (setq s + (substring s 0 + (match-beginning 0)))) + s)))) + (items + ((lifo_items) + (nreverse $1))) + (lifo_items + ((lifo_items item) + (cons $2 $1)) + ((item) + (list $1))) + (item + ((SYMBOL)) + ((CHARACTER)))) + '(grammar prologue epilogue declaration nonterminal rule put_names put_values use_names))) + "Parser table.") + +(defun semantic-grammar-wy--install-parser () + "Setup the Semantic Parser." + (semantic-install-function-overrides + '((parse-stream . wisent-parse-stream))) + (setq semantic-parser-name "LALR" + semantic--parse-table semantic-grammar-wy--parse-table + semantic-debug-parser-source "semantic-grammar.wy" + semantic-flex-keywords-obarray semantic-grammar-wy--keyword-table + semantic-lex-types-obarray semantic-grammar-wy--token-table) + ;; Collect unmatched syntax lexical tokens + (semantic-make-local-hook 'wisent-discarding-token-functions) + (add-hook 'wisent-discarding-token-functions + 'wisent-collect-unmatched-syntax nil t)) + + +;;; Analyzers + +(define-lex-sexp-type-analyzer semantic-grammar-wy--<sexp>-sexp-analyzer + "sexp analyzer for <sexp> tokens." + "\\=" + 'SEXP) + +(define-lex-sexp-type-analyzer semantic-grammar-wy--<qlist>-sexp-analyzer + "sexp analyzer for <qlist> tokens." + "\\s'\\s-*(" + 'PREFIXED_LIST) + +(define-lex-keyword-type-analyzer semantic-grammar-wy--<keyword>-keyword-analyzer + "keyword analyzer for <keyword> tokens." + "\\(\\sw\\|\\s_\\)+") + +(define-lex-block-type-analyzer semantic-grammar-wy--<block>-block-analyzer + "block analyzer for <block> tokens." + "\\s(\\|\\s)" + '((("(" LPAREN PAREN_BLOCK) + ("{" LBRACE BRACE_BLOCK)) + (")" RPAREN) + ("}" RBRACE)) + ) + +(define-lex-regex-type-analyzer semantic-grammar-wy--<char>-regexp-analyzer + "regexp analyzer for <char> tokens." + semantic-grammar-lex-c-char-re + nil + 'CHARACTER) + +(define-lex-sexp-type-analyzer semantic-grammar-wy--<string>-sexp-analyzer + "sexp analyzer for <string> tokens." + "\\s\"" + 'STRING) + +(define-lex-regex-type-analyzer semantic-grammar-wy--<symbol>-regexp-analyzer + "regexp analyzer for <symbol> tokens." + ":?\\(\\sw\\|\\s_\\)+" + '((PERCENT_PERCENT . "\\`%%\\'")) + 'SYMBOL) + +(define-lex-string-type-analyzer semantic-grammar-wy--<punctuation>-string-analyzer + "string analyzer for <punctuation> tokens." + "\\(\\s.\\|\\s$\\|\\s'\\)+" + '((GT . ">") + (LT . "<") + (OR . "|") + (SEMI . ";") + (COLON . ":")) + 'punctuation) + +(provide 'semantic/grammar-wy) + +;;; semantic/grammar-wy.el ends here diff --git a/lisp/cedet/semantic/grammar.el b/lisp/cedet/semantic/grammar.el new file mode 100644 index 00000000000..f47275bdcf6 --- /dev/null +++ b/lisp/cedet/semantic/grammar.el @@ -0,0 +1,1897 @@ +;;; semantic/grammar.el --- Major mode framework for Semantic grammars + +;;; Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> +;; Maintainer: David Ponce <david@dponce.com> + +;; 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: +;; +;; Major mode framework for editing Semantic's input grammar files. + +;;; History: +;; + +;;; Code: + +(require 'semantic) +(require 'semantic/ctxt) +(require 'semantic/format) +(require 'semantic/grammar-wy) +(require 'semantic/idle) +(declare-function semantic-momentary-highlight-tag "semantic/decorate") +(declare-function semantic-analyze-context "semantic/analyze") +(declare-function semantic-analyze-tags-of-class-list + "semantic/analyze/complete") + +(eval-when-compile + (require 'eldoc) + (require 'semantic/edit) + (require 'semantic/find)) + + +;;;; +;;;; Set up lexer +;;;; + +(defconst semantic-grammar-lex-c-char-re "'\\s\\?.'" + "Regexp matching C-like character literals.") + +;; Most of the analyzers are auto-generated from the grammar, but the +;; following which need special handling code. +;; +(define-lex-regex-analyzer semantic-grammar-lex-prologue + "Detect and create a prologue token." + "\\<%{" + ;; Zing to the end of this brace block. + (semantic-lex-push-token + (semantic-lex-token + 'PROLOGUE (point) + (save-excursion + (semantic-lex-unterminated-syntax-protection 'PROLOGUE + (forward-char) + (forward-sexp 1) + (point)))))) + +(defsubst semantic-grammar-epilogue-start () + "Return the start position of the grammar epilogue." + (save-excursion + (goto-char (point-min)) + (if (re-search-forward "^\\s-*\\<%%\\>\\s-*$" nil t 2) + (match-beginning 0) + (1+ (point-max))))) + +(define-lex-regex-analyzer semantic-grammar-lex-epilogue + "Detect and create an epilogue or percent-percent token." + "\\<%%\\>" + (let ((start (match-beginning 0)) + (end (match-end 0)) + (class 'PERCENT_PERCENT)) + (when (>= start (semantic-grammar-epilogue-start)) + (setq class 'EPILOGUE + end (point-max))) + (semantic-lex-push-token + (semantic-lex-token class start end)))) + +(define-lex semantic-grammar-lexer + "Lexical analyzer that handles Semantic grammar buffers. +It ignores whitespaces, newlines and comments." + semantic-lex-ignore-newline + semantic-lex-ignore-whitespace + ;; Must detect prologue/epilogue before other symbols/keywords! + semantic-grammar-lex-prologue + semantic-grammar-lex-epilogue + semantic-grammar-wy--<keyword>-keyword-analyzer + semantic-grammar-wy--<symbol>-regexp-analyzer + semantic-grammar-wy--<char>-regexp-analyzer + semantic-grammar-wy--<string>-sexp-analyzer + ;; Must detect comments after strings because `comment-start-skip' + ;; regexp match semicolons inside strings! + semantic-lex-ignore-comments + ;; Must detect prefixed list before punctuation because prefix chars + ;; are also punctuations! + semantic-grammar-wy--<qlist>-sexp-analyzer + ;; Must detect punctuations after comments because the semicolon can + ;; be a punctuation or a comment start! + semantic-grammar-wy--<punctuation>-string-analyzer + semantic-grammar-wy--<block>-block-analyzer + semantic-grammar-wy--<sexp>-sexp-analyzer) + +;;; Test the lexer +;; +(defun semantic-grammar-lex-buffer () + "Run `semantic-grammar-lex' on current buffer." + (interactive) + (semantic-lex-init) + (setq semantic-lex-analyzer 'semantic-grammar-lexer) + (let ((token-stream + (semantic-lex (point-min) (point-max)))) + (with-current-buffer (get-buffer-create "*semantic-grammar-lex*") + (erase-buffer) + (pp token-stream (current-buffer)) + (goto-char (point-min)) + (pop-to-buffer (current-buffer))))) + +;;;; +;;;; Semantic action expansion +;;;; + +(defun semantic-grammar-ASSOC (&rest args) + "Return expansion of built-in ASSOC expression. +ARGS are ASSOC's key value list." + (let ((key t)) + `(semantic-tag-make-assoc-list + ,@(mapcar #'(lambda (i) + (prog1 + (if key + (list 'quote i) + i) + (setq key (not key)))) + args)))) + +(defsubst semantic-grammar-quote-p (sym) + "Return non-nil if SYM is bound to the `quote' function." + (condition-case nil + (eq (indirect-function sym) + (indirect-function 'quote)) + (error nil))) + +(defsubst semantic-grammar-backquote-p (sym) + "Return non-nil if SYM is bound to the `backquote' function." + (condition-case nil + (eq (indirect-function sym) + (indirect-function 'backquote)) + (error nil))) + +;;;; +;;;; API to access grammar tags +;;;; + +(define-mode-local-override semantic-tag-components + semantic-grammar-mode (tag) + "Return the children of tag TAG." + (semantic-tag-get-attribute tag :children)) + +(defun semantic-grammar-first-tag-name (class) + "Return the name of the first tag of class CLASS found. +Warn if other tags of class CLASS exist." + (let* ((tags (semantic-find-tags-by-class + class (current-buffer)))) + (if tags + (prog1 + (semantic-tag-name (car tags)) + (if (cdr tags) + (message "*** Ignore all but first declared %s" + class)))))) + +(defun semantic-grammar-tag-symbols (class) + "Return the list of symbols defined in tags of class CLASS. +That is tag names plus names defined in tag attribute `:rest'." + (let* ((tags (semantic-find-tags-by-class + class (current-buffer)))) + (apply 'append + (mapcar + #'(lambda (tag) + (mapcar + 'intern + (cons (semantic-tag-name tag) + (semantic-tag-get-attribute tag :rest)))) + tags)))) + +(defsubst semantic-grammar-item-text (item) + "Return the readable string form of ITEM." + (if (string-match semantic-grammar-lex-c-char-re item) + (concat "?" (substring item 1 -1)) + item)) + +(defsubst semantic-grammar-item-value (item) + "Return symbol or character value of ITEM string." + (if (string-match semantic-grammar-lex-c-char-re item) + (let ((c (read (concat "?" (substring item 1 -1))))) + (if (featurep 'xemacs) + ;; Handle characters as integers in XEmacs like in GNU Emacs. + (char-int c) + c)) + (intern item))) + +(defun semantic-grammar-prologue () + "Return grammar prologue code as a string value." + (let ((tag (semantic-find-first-tag-by-name + "prologue" + (semantic-find-tags-by-class 'code (current-buffer))))) + (if tag + (save-excursion + (concat + (buffer-substring + (progn + (goto-char (semantic-tag-start tag)) + (skip-chars-forward "%{\r\n\t ") + (point)) + (progn + (goto-char (semantic-tag-end tag)) + (skip-chars-backward "\r\n\t %}") + (point))) + "\n")) + ""))) + +(defun semantic-grammar-epilogue () + "Return grammar epilogue code as a string value." + (let ((tag (semantic-find-first-tag-by-name + "epilogue" + (semantic-find-tags-by-class 'code (current-buffer))))) + (if tag + (save-excursion + (concat + (buffer-substring + (progn + (goto-char (semantic-tag-start tag)) + (skip-chars-forward "%\r\n\t ") + (point)) + (progn + (goto-char (semantic-tag-end tag)) + (skip-chars-backward "\r\n\t") + ;; If a grammar footer is found, skip it. + (re-search-backward "^;;;\\s-+\\S-+\\s-+ends here" + (save-excursion + (beginning-of-line) + (point)) + t) + (skip-chars-backward "\r\n\t") + (point))) + "\n")) + ""))) + +(defsubst semantic-grammar-buffer-file (&optional buffer) + "Return name of file sans directory BUFFER is visiting. +No argument or nil as argument means use the current buffer." + (file-name-nondirectory (buffer-file-name buffer))) + +(defun semantic-grammar-package () + "Return the %package value as a string. +If there is no %package statement in the grammar, return a default +package name derived from the grammar file name. For example, the +default package name for the grammar file foo.wy is foo-wy, and for +foo.by it is foo-by." + (or (semantic-grammar-first-tag-name 'package) + (let* ((file (semantic-grammar-buffer-file)) + (ext (file-name-extension file)) + (i (string-match (format "\\([.]\\)%s\\'" ext) file))) + (concat (substring file 0 i) "-" ext)))) + +(defsubst semantic-grammar-languagemode () + "Return the %languagemode value as a list of symbols or nil." + (semantic-grammar-tag-symbols 'languagemode)) + +(defsubst semantic-grammar-start () + "Return the %start value as a list of symbols or nil." + (semantic-grammar-tag-symbols 'start)) + +(defsubst semantic-grammar-scopestart () + "Return the %scopestart value as a symbol or nil." + (intern (or (semantic-grammar-first-tag-name 'scopestart) "nil"))) + +(defsubst semantic-grammar-quotemode () + "Return the %quotemode value as a symbol or nil." + (intern (or (semantic-grammar-first-tag-name 'quotemode) "nil"))) + +(defsubst semantic-grammar-keywords () + "Return the language keywords. +That is an alist of (VALUE . TOKEN) where VALUE is the string value of +the keyword and TOKEN is the terminal symbol identifying the keyword." + (mapcar + #'(lambda (key) + (cons (semantic-tag-get-attribute key :value) + (intern (semantic-tag-name key)))) + (semantic-find-tags-by-class 'keyword (current-buffer)))) + +(defun semantic-grammar-keyword-properties (keywords) + "Return the list of KEYWORDS properties." + (let ((puts (semantic-find-tags-by-class + 'put (current-buffer))) + put keys key plist assoc pkey pval props) + (while puts + (setq put (car puts) + puts (cdr puts) + keys (mapcar + 'intern + (cons (semantic-tag-name put) + (semantic-tag-get-attribute put :rest)))) + (while keys + (setq key (car keys) + keys (cdr keys) + assoc (rassq key keywords)) + (if (null assoc) + nil ;;(message "*** %%put to undefined keyword %s ignored" key) + (setq key (car assoc) + plist (semantic-tag-get-attribute put :value)) + (while plist + (setq pkey (intern (caar plist)) + pval (read (cdar plist)) + props (cons (list key pkey pval) props) + plist (cdr plist)))))) + props)) + +(defun semantic-grammar-tokens () + "Return defined lexical tokens. +That is an alist (TYPE . DEFS) where type is a %token <type> symbol +and DEFS is an alist of (TOKEN . VALUE). TOKEN is the terminal symbol +identifying the token and VALUE is the string value of the token or +nil." + (let (tags alist assoc tag type term names value) + + ;; Check for <type> in %left, %right & %nonassoc declarations + (setq tags (semantic-find-tags-by-class + 'assoc (current-buffer))) + (while tags + (setq tag (car tags) + tags (cdr tags)) + (when (setq type (semantic-tag-type tag)) + (setq names (semantic-tag-get-attribute tag :value) + assoc (assoc type alist)) + (or assoc (setq assoc (list type) + alist (cons assoc alist))) + (while names + (setq term (car names) + names (cdr names)) + (or (string-match semantic-grammar-lex-c-char-re term) + (setcdr assoc (cons (list (intern term)) + (cdr assoc))))))) + + ;; Then process %token declarations so they can override any + ;; previous specifications + (setq tags (semantic-find-tags-by-class + 'token (current-buffer))) + (while tags + (setq tag (car tags) + tags (cdr tags)) + (setq names (cons (semantic-tag-name tag) + (semantic-tag-get-attribute tag :rest)) + type (or (semantic-tag-type tag) "<no-type>") + value (semantic-tag-get-attribute tag :value) + assoc (assoc type alist)) + (or assoc (setq assoc (list type) + alist (cons assoc alist))) + (while names + (setq term (intern (car names)) + names (cdr names)) + (setcdr assoc (cons (cons term value) (cdr assoc))))) + alist)) + +(defun semantic-grammar-token-%type-properties (&optional props) + "Return properties set by %type statements. +This declare a new type if necessary. +If optional argument PROPS is non-nil, it is an existing list of +properties where to add new properties." + (let (type) + (dolist (tag (semantic-find-tags-by-class 'type (current-buffer))) + (setq type (semantic-tag-name tag)) + ;; Indicate to auto-generate the analyzer for this type + (push (list type :declared t) props) + (dolist (e (semantic-tag-get-attribute tag :value)) + (push (list type (intern (car e)) (read (or (cdr e) "nil"))) + props))) + props)) + +(defun semantic-grammar-token-%put-properties (tokens) + "For types found in TOKENS, return properties set by %put statements." + (let (found props) + (dolist (put (semantic-find-tags-by-class 'put (current-buffer))) + (dolist (type (cons (semantic-tag-name put) + (semantic-tag-get-attribute put :rest))) + (setq found (assoc type tokens)) + (if (null found) + nil ;; %put <type> ignored, no token defined + (setq type (car found)) + (dolist (e (semantic-tag-get-attribute put :value)) + (push (list type (intern (car e)) (read (or (cdr e) "nil"))) + props))))) + props)) + +(defsubst semantic-grammar-token-properties (tokens) + "Return properties of declared types. +Types are explicitly declared by %type statements. Types found in +TOKENS are those declared implicitly by %token statements. +Properties can be set by %put and %type statements. +Properties set by %type statements take precedence over those set by +%put statements." + (let ((props (semantic-grammar-token-%put-properties tokens))) + (semantic-grammar-token-%type-properties props))) + +(defun semantic-grammar-use-macros () + "Return macro definitions from %use-macros statements. +Also load the specified macro libraries." + (let (lib defs) + (dolist (tag (semantic-find-tags-by-class 'macro (current-buffer))) + (setq lib (intern (semantic-tag-type tag))) + (condition-case nil + ;;(load lib) ;; Be sure to use the latest macro library. + (require lib) + (error nil)) + (dolist (mac (semantic-tag-get-attribute tag :value)) + (push (cons (intern mac) + (intern (format "%s-%s" lib mac))) + defs))) + (nreverse defs))) + +(defvar semantic-grammar-macros nil + "List of associations (MACRO-NAME . EXPANDER).") +(make-variable-buffer-local 'semantic-grammar-macros) + +(defun semantic-grammar-macros () + "Build and return the alist of defined macros." + (append + ;; Definitions found in tags. + (semantic-grammar-use-macros) + ;; Other pre-installed definitions. + semantic-grammar-macros)) + +;;;; +;;;; Overloaded functions that build parser data. +;;;; + +;;; Keyword table builder +;; +(defun semantic-grammar-keywordtable-builder-default () + "Return the default value of the keyword table." + (let ((keywords (semantic-grammar-keywords))) + `(semantic-lex-make-keyword-table + ',keywords + ',(semantic-grammar-keyword-properties keywords)))) + +(define-overloadable-function semantic-grammar-keywordtable-builder () + "Return the keyword table table value.") + +;;; Token table builder +;; +(defun semantic-grammar-tokentable-builder-default () + "Return the default value of the table of lexical tokens." + (let ((tokens (semantic-grammar-tokens))) + `(semantic-lex-make-type-table + ',tokens + ',(semantic-grammar-token-properties tokens)))) + +(define-overloadable-function semantic-grammar-tokentable-builder () + "Return the value of the table of lexical tokens.") + +;;; Parser table builder +;; +(defun semantic-grammar-parsetable-builder-default () + "Return the default value of the parse table." + (error "`semantic-grammar-parsetable-builder' not defined")) + +(define-overloadable-function semantic-grammar-parsetable-builder () + "Return the parser table value.") + +;;; Parser setup code builder +;; +(defun semantic-grammar-setupcode-builder-default () + "Return the default value of the setup code form." + (error "`semantic-grammar-setupcode-builder' not defined")) + +(define-overloadable-function semantic-grammar-setupcode-builder () + "Return the parser setup code form.") + +;;;; +;;;; Lisp code generation +;;;; +(defvar semantic--grammar-input-buffer nil) +(defvar semantic--grammar-output-buffer nil) + +(defsubst semantic-grammar-keywordtable () + "Return the variable name of the keyword table." + (concat (file-name-sans-extension + (semantic-grammar-buffer-file + semantic--grammar-output-buffer)) + "--keyword-table")) + +(defsubst semantic-grammar-tokentable () + "Return the variable name of the token table." + (concat (file-name-sans-extension + (semantic-grammar-buffer-file + semantic--grammar-output-buffer)) + "--token-table")) + +(defsubst semantic-grammar-parsetable () + "Return the variable name of the parse table." + (concat (file-name-sans-extension + (semantic-grammar-buffer-file + semantic--grammar-output-buffer)) + "--parse-table")) + +(defsubst semantic-grammar-setupfunction () + "Return the name of the parser setup function." + (concat (file-name-sans-extension + (semantic-grammar-buffer-file + semantic--grammar-output-buffer)) + "--install-parser")) + +(defmacro semantic-grammar-as-string (object) + "Return OBJECT as a string value." + `(if (stringp ,object) + ,object + ;;(require 'pp) + (pp-to-string ,object))) + +(defun semantic-grammar-insert-defconst (name value docstring) + "Insert declaration of constant NAME with VALUE and DOCSTRING." + (let ((start (point))) + (insert (format "(defconst %s\n%s%S)\n\n" name value docstring)) + (save-excursion + (goto-char start) + (indent-sexp)))) + +(defun semantic-grammar-insert-defun (name body docstring) + "Insert declaration of function NAME with BODY and DOCSTRING." + (let ((start (point))) + (insert (format "(defun %s ()\n%S\n%s)\n\n" name docstring body)) + (save-excursion + (goto-char start) + (indent-sexp)))) + +(defun semantic-grammar-insert-define (define) + "Insert the declaration specified by DEFINE expression. +Typically a DEFINE expression should look like this: + +\(define-thing name docstring expression1 ...)" + ;;(require 'pp) + (let ((start (point))) + (insert (format "(%S %S" (car define) (nth 1 define))) + (dolist (item (nthcdr 2 define)) + (insert "\n") + (delete-blank-lines) + (pp item (current-buffer))) + (insert ")\n\n") + (save-excursion + (goto-char start) + (indent-sexp)))) + +(defconst semantic-grammar-header-template + '("\ +;;; " file " --- Generated parser support file + +" copy " + +;; Author: " user-full-name " <" user-mail-address "> +;; Created: " date " +;; Keywords: syntax +;; X-RCS: " vcid " + +;; This file is not part of GNU Emacs. +;; +;; 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 software is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; PLEASE DO NOT MANUALLY EDIT THIS FILE! It is automatically +;; generated from the grammar file " gram ". + +;;; History: +;; + +;;; Code: +") + "Generated header template. +The symbols in the template are local variables in +`semantic-grammar-header'") + +(defconst semantic-grammar-footer-template + '("\ + +\(provide '" libr ") + +;;; " file " ends here +") + "Generated footer template. +The symbols in the list are local variables in +`semantic-grammar-footer'.") + +(defun semantic-grammar-copyright-line () + "Return the grammar copyright line, or nil if not found." + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "^;;+[ \t]+Copyright (C) .*$" + ;; Search only in the four top lines + (save-excursion (forward-line 4) (point)) + t) + (match-string 0)))) + +(defun semantic-grammar-header () + "Return text of a generated standard header." + (let ((file (semantic-grammar-buffer-file + semantic--grammar-output-buffer)) + (gram (semantic-grammar-buffer-file)) + (date (format-time-string "%Y-%m-%d %T%z")) + (vcid (concat "$" "Id" "$")) ;; Avoid expansion + ;; Try to get the copyright from the input grammar, or + ;; generate a new one if not found. + (copy (or (semantic-grammar-copyright-line) + (concat (format-time-string ";; Copyright (C) %Y ") + user-full-name))) + (out "")) + (dolist (S semantic-grammar-header-template) + (cond ((stringp S) + (setq out (concat out S))) + ((symbolp S) + (setq out (concat out (symbol-value S)))))) + out)) + +(defun semantic-grammar-footer () + "Return text of a generated standard footer." + (let* ((file (semantic-grammar-buffer-file + semantic--grammar-output-buffer)) + (libr (file-name-sans-extension file)) + (out "")) + (dolist (S semantic-grammar-footer-template) + (cond ((stringp S) + (setq out (concat out S))) + ((symbolp S) + (setq out (concat out (symbol-value S)))))) + out)) + +(defun semantic-grammar-token-data () + "Return the string value of the table of lexical tokens." + (semantic-grammar-as-string + (semantic-grammar-tokentable-builder))) + +(defun semantic-grammar-keyword-data () + "Return the string value of the table of keywords." + (semantic-grammar-as-string + (semantic-grammar-keywordtable-builder))) + +(defun semantic-grammar-parser-data () + "Return the parser table as a string value." + (semantic-grammar-as-string + (semantic-grammar-parsetable-builder))) + +(defun semantic-grammar-setup-data () + "Return the parser setup code form as a string value." + (semantic-grammar-as-string + (semantic-grammar-setupcode-builder))) + +;;; Generation of lexical analyzers. +;; +(defvar semantic-grammar--lex-block-specs) + +(defsubst semantic-grammar--lex-delim-spec (block-spec) + "Return delimiters specification from BLOCK-SPEC." + (condition-case nil + (let* ((standard-input (cdr block-spec)) + (delim-spec (read))) + (if (and (consp delim-spec) + (car delim-spec) (symbolp (car delim-spec)) + (cadr delim-spec) (symbolp (cadr delim-spec))) + delim-spec + (error))) + (error + (error "Invalid delimiters specification %s in block token %s" + (cdr block-spec) (car block-spec))))) + +(defun semantic-grammar--lex-block-specs () + "Compute lexical block specifications for the current buffer. +Block definitions are read from the current table of lexical types." + (cond + ;; Block specifications have been parsed and are invalid. + ((eq semantic-grammar--lex-block-specs 'error) + nil + ) + ;; Parse block specifications. + ((null semantic-grammar--lex-block-specs) + (condition-case err + (let* ((blocks (cdr (semantic-lex-type-value "block" t))) + (open-delims (cdr (semantic-lex-type-value "open-paren" t))) + (close-delims (cdr (semantic-lex-type-value "close-paren" t))) + olist clist block-spec delim-spec open-spec close-spec) + (dolist (block-spec blocks) + (setq delim-spec (semantic-grammar--lex-delim-spec block-spec) + open-spec (assq (car delim-spec) open-delims) + close-spec (assq (cadr delim-spec) close-delims)) + (or open-spec + (error "Missing open-paren token %s required by block %s" + (car delim-spec) (car block-spec))) + (or close-spec + (error "Missing close-paren token %s required by block %s" + (cdr delim-spec) (car block-spec))) + ;; build alist ((OPEN-DELIM OPEN-SYM BLOCK-SYM) ...) + (push (list (cdr open-spec) (car open-spec) (car block-spec)) + olist) + ;; build alist ((CLOSE-DELIM CLOSE-SYM) ...) + (push (list (cdr close-spec) (car close-spec)) + clist)) + (setq semantic-grammar--lex-block-specs (cons olist clist))) + (error + (setq semantic-grammar--lex-block-specs 'error) + (message "%s" (error-message-string err)) + nil)) + ) + ;; Block specifications already parsed. + (t + semantic-grammar--lex-block-specs))) + +(defsubst semantic-grammar-quoted-form (exp) + "Return a quoted form of EXP if it isn't a self evaluating form." + (if (and (not (null exp)) + (or (listp exp) (symbolp exp))) + (list 'quote exp) + exp)) + +(defun semantic-grammar-insert-defanalyzer (type) + "Insert declaration of the lexical analyzer defined with TYPE." + (let* ((type-name (symbol-name type)) + (type-value (symbol-value type)) + (syntax (get type 'syntax)) + (declared (get type :declared)) + spec mtype prefix name doc) + ;; Generate an analyzer if the corresponding type has been + ;; explicitly declared in a %type statement, and if at least the + ;; syntax property has been provided. + (when (and declared syntax) + (setq prefix (file-name-sans-extension + (semantic-grammar-buffer-file + semantic--grammar-output-buffer)) + mtype (or (get type 'matchdatatype) 'regexp) + name (intern (format "%s--<%s>-%s-analyzer" prefix type mtype)) + doc (format "%s analyzer for <%s> tokens." mtype type)) + (cond + ;; Regexp match analyzer + ((eq mtype 'regexp) + (semantic-grammar-insert-define + `(define-lex-regex-type-analyzer ,name + ,doc ,syntax + ,(semantic-grammar-quoted-form (cdr type-value)) + ',(or (car type-value) (intern type-name)))) + ) + ;; String compare analyzer + ((eq mtype 'string) + (semantic-grammar-insert-define + `(define-lex-string-type-analyzer ,name + ,doc ,syntax + ,(semantic-grammar-quoted-form (cdr type-value)) + ',(or (car type-value) (intern type-name)))) + ) + ;; Block analyzer + ((and (eq mtype 'block) + (setq spec (semantic-grammar--lex-block-specs))) + (semantic-grammar-insert-define + `(define-lex-block-type-analyzer ,name + ,doc ,syntax + ,(semantic-grammar-quoted-form spec))) + ) + ;; Sexp analyzer + ((eq mtype 'sexp) + (semantic-grammar-insert-define + `(define-lex-sexp-type-analyzer ,name + ,doc ,syntax + ',(or (car type-value) (intern type-name)))) + ) + ;; keyword analyzer + ((eq mtype 'keyword) + (semantic-grammar-insert-define + `(define-lex-keyword-type-analyzer ,name + ,doc ,syntax)) + ) + )) + )) + +(defun semantic-grammar-insert-defanalyzers () + "Insert declarations of lexical analyzers." + (let (tokens props) + (with-current-buffer semantic--grammar-input-buffer + (setq tokens (semantic-grammar-tokens) + props (semantic-grammar-token-properties tokens))) + (insert "(require 'semantic-lex)\n\n") + (let ((semantic-lex-types-obarray + (semantic-lex-make-type-table tokens props)) + semantic-grammar--lex-block-specs) + (mapatoms 'semantic-grammar-insert-defanalyzer + semantic-lex-types-obarray)))) + +;;; Generation of the grammar support file. +;; +(defcustom semantic-grammar-file-regexp "\\.[wb]y$" + "Regexp which matches grammar source files." + :group 'semantic + :type 'regexp) + +(defsubst semantic-grammar-noninteractive () + "Return non-nil if running without interactive terminal." + (if (featurep 'xemacs) + (noninteractive) + noninteractive)) + +(defun semantic-grammar-create-package (&optional force) + "Create package Lisp code from grammar in current buffer. +Does nothing if the Lisp code seems up to date. +If optional argument FORCE is non-nil, unconditionally re-generate the +Lisp code." + (interactive "P") + (setq force (or force current-prefix-arg)) + (semantic-fetch-tags) + (let* ( + ;; Values of the following local variables are obtained from + ;; the grammar parsed tree in current buffer, that is before + ;; switching to the output file. + (package (semantic-grammar-package)) + (output (concat package ".el")) + (semantic--grammar-input-buffer (current-buffer)) + (semantic--grammar-output-buffer (find-file-noselect output)) + (header (semantic-grammar-header)) + (prologue (semantic-grammar-prologue)) + (epilogue (semantic-grammar-epilogue)) + (footer (semantic-grammar-footer)) + ) + (if (and (not force) + (not (buffer-modified-p)) + (file-newer-than-file-p + (buffer-file-name semantic--grammar-output-buffer) + (buffer-file-name semantic--grammar-input-buffer))) + (message "Package `%s' is up to date." package) + ;; Create the package + (set-buffer semantic--grammar-output-buffer) + ;; Use Unix EOLs, so that the file is portable to all platforms. + (setq buffer-file-coding-system 'raw-text-unix) + (erase-buffer) + (unless (eq major-mode 'emacs-lisp-mode) + (emacs-lisp-mode)) + +;;;; Header + Prologue + + (insert header + "\n;;; Prologue\n;;\n" + prologue + ) + ;; Evaluate the prologue now, because it might provide definition + ;; of grammar macro expanders. + (eval-region (point-min) (point)) + + (save-excursion + +;;;; Declarations + + (insert "\n;;; Declarations\n;;\n") + + ;; `eval-defun' is not necessary to reset `defconst' values. + (semantic-grammar-insert-defconst + (semantic-grammar-keywordtable) + (with-current-buffer semantic--grammar-input-buffer + (semantic-grammar-keyword-data)) + "Table of language keywords.") + + (semantic-grammar-insert-defconst + (semantic-grammar-tokentable) + (with-current-buffer semantic--grammar-input-buffer + (semantic-grammar-token-data)) + "Table of lexical tokens.") + + (semantic-grammar-insert-defconst + (semantic-grammar-parsetable) + (with-current-buffer semantic--grammar-input-buffer + (semantic-grammar-parser-data)) + "Parser table.") + + (semantic-grammar-insert-defun + (semantic-grammar-setupfunction) + (with-current-buffer semantic--grammar-input-buffer + (semantic-grammar-setup-data)) + "Setup the Semantic Parser.") + +;;;; Analyzers + (insert "\n;;; Analyzers\n;;\n") + + (semantic-grammar-insert-defanalyzers) + +;;;; Epilogue & Footer + + (insert "\n;;; Epilogue\n;;\n" + epilogue + footer + ) + + ) + + (save-buffer 16) + + ;; If running in batch mode, there is nothing more to do. + ;; Save the generated file and quit. + (if (semantic-grammar-noninteractive) + (let ((version-control t) + (delete-old-versions t) + (make-backup-files t) + (vc-make-backup-files t)) + (kill-buffer (current-buffer))) + ;; If running interactively, eval declarations and epilogue + ;; code, then pop to the buffer visiting the generated file. + (eval-region (point) (point-max)) + (goto-char (point-min)) + (pop-to-buffer (current-buffer)) + ;; The generated code has been evaluated and updated into + ;; memory. Now find all buffers that match the major modes we + ;; have created this language for, and force them to call our + ;; setup function again, refreshing all semantic data, and + ;; enabling them to work with the new code just created. +;;;; FIXME? + ;; At this point, I don't know any user's defined setup code :-( + ;; At least, what I can do for now, is to run the generated + ;; parser-install function. + (semantic-map-mode-buffers + (semantic-grammar-setupfunction) + (semantic-grammar-languagemode))) + ) + ;; Return the name of the generated package file. + output)) + +(defun semantic-grammar-recreate-package () + "Unconditionnaly create Lisp code from grammar in current buffer. +Like \\[universal-argument] \\[semantic-grammar-create-package]." + (interactive) + (semantic-grammar-create-package t)) + +(defun semantic-grammar-batch-build-one-package (file) + "Build a Lisp package from the grammar in FILE. +That is, generate Lisp code from FILE, and `byte-compile' it. +Return non-nil if there were no errors, nil if errors." + ;; We need this require so that we can find `byte-compile-dest-file'. + (require 'bytecomp) + (unless (auto-save-file-name-p file) + ;; Create the package + (let ((packagename + (condition-case err + (with-current-buffer (find-file-noselect file) + (semantic-grammar-create-package)) + (error + (message "%s" (error-message-string err)) + nil)))) + (when packagename + ;; Only byte compile if out of date + (if (file-newer-than-file-p + packagename (byte-compile-dest-file packagename)) + (let (;; Some complex grammar table expressions need a few + ;; more resources than the default. + (max-specpdl-size (max 3000 max-specpdl-size)) + (max-lisp-eval-depth (max 1000 max-lisp-eval-depth)) + ) + ;; byte compile the resultant file + (byte-compile-file packagename)) + t))))) + +(defun semantic-grammar-batch-build-packages () + "Build Lisp packages from grammar files on the command line. +That is, run `semantic-grammar-batch-build-one-package' for each file. +Each file is processed even if an error occurred previously. +Must be used from the command line, with `-batch'. +For example, to process grammar files in current directory, invoke: + + \"emacs -batch -f semantic-grammar-batch-build-packages .\". + +See also the variable `semantic-grammar-file-regexp'." + (or (semantic-grammar-noninteractive) + (error "\ +`semantic-grammar-batch-build-packages' must be used with -batch" + )) + (let ((status 0) + ;; Remove vc from find-file-hook. It causes bad stuff to + ;; happen in Emacs 20. + (find-file-hook (delete 'vc-find-file-hook find-file-hook))) + (message "Compiling Grammars from: %s" (locate-library "semantic-grammar")) + (dolist (arg command-line-args-left) + (unless (and arg (file-exists-p arg)) + (error "Argument %s is not a valid file name" arg)) + (setq arg (expand-file-name arg)) + (if (file-directory-p arg) + ;; Directory as argument + (dolist (src (condition-case nil + (directory-files + arg nil semantic-grammar-file-regexp) + (error + (error "Unable to read directory files")))) + (or (semantic-grammar-batch-build-one-package + (expand-file-name src arg)) + (setq status 1))) + ;; Specific file argument + (or (semantic-grammar-batch-build-one-package arg) + (setq status 1)))) + (kill-emacs status) + )) + +;;;; +;;;; Macros highlighting +;;;; + +(defvar semantic--grammar-macros-regexp-1 nil) +(make-variable-buffer-local 'semantic--grammar-macros-regexp-1) + +(defun semantic--grammar-macros-regexp-1 () + "Return font-lock keyword regexp for pre-installed macro names." + (and semantic-grammar-macros + (not semantic--grammar-macros-regexp-1) + (condition-case nil + (setq semantic--grammar-macros-regexp-1 + (concat "(\\s-*" + (regexp-opt + (mapcar #'(lambda (e) (symbol-name (car e))) + semantic-grammar-macros) + t) + "\\>")) + (error nil))) + semantic--grammar-macros-regexp-1) + +(defconst semantic--grammar-macdecl-re + "\\<%use-macros\\>[ \t\r\n]+\\(\\sw\\|\\s_\\)+[ \t\r\n]+{" + "Regexp that matches a macro declaration statement.") + +(defvar semantic--grammar-macros-regexp-2 nil) +(make-variable-buffer-local 'semantic--grammar-macros-regexp-2) + +(defun semantic--grammar-clear-macros-regexp-2 (&rest ignore) + "Clear the cached regexp that match macros local in this grammar. +IGNORE arguments. +Added to `before-change-functions' hooks to be run before each text +change." + (setq semantic--grammar-macros-regexp-2 nil)) + +(defun semantic--grammar-macros-regexp-2 () + "Return the regexp that match macros local in this grammar." + (unless semantic--grammar-macros-regexp-2 + (let (macs) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward semantic--grammar-macdecl-re nil t) + (condition-case nil + (setq macs (nconc macs + (split-string + (buffer-substring-no-properties + (point) + (progn + (backward-char) + (forward-list 1) + (down-list -1) + (point)))))) + (error nil))) + (when macs + (setq semantic--grammar-macros-regexp-2 + (concat "(\\s-*" (regexp-opt macs t) "\\>")))))) + semantic--grammar-macros-regexp-2) + +(defun semantic--grammar-macros-matcher (end) + "Search for a grammar macro name to highlight. +END is the limit of the search." + (let ((regexp (semantic--grammar-macros-regexp-1))) + (or (and regexp (re-search-forward regexp end t)) + (and (setq regexp (semantic--grammar-macros-regexp-2)) + (re-search-forward regexp end t))))) + +;;;; +;;;; Define major mode +;;;; + +(defvar semantic-grammar-syntax-table + (let ((table (make-syntax-table (standard-syntax-table)))) + (modify-syntax-entry ?\: "." table) ;; COLON + (modify-syntax-entry ?\> "." table) ;; GT + (modify-syntax-entry ?\< "." table) ;; LT + (modify-syntax-entry ?\| "." table) ;; OR + (modify-syntax-entry ?\; ". 12" table) ;; SEMI, Comment start ;; + (modify-syntax-entry ?\n ">" table) ;; Comment end + (modify-syntax-entry ?\" "\"" table) ;; String + (modify-syntax-entry ?\% "w" table) ;; Word + (modify-syntax-entry ?\- "_" table) ;; Symbol + (modify-syntax-entry ?\. "_" table) ;; Symbol + (modify-syntax-entry ?\\ "\\" table) ;; Quote + (modify-syntax-entry ?\` "'" table) ;; Prefix ` (backquote) + (modify-syntax-entry ?\' "'" table) ;; Prefix ' (quote) + (modify-syntax-entry ?\, "'" table) ;; Prefix , (comma) + (modify-syntax-entry ?\# "'" table) ;; Prefix # (sharp) + table) + "Syntax table used in a Semantic grammar buffers.") + +(defvar semantic-grammar-mode-hook nil + "Hook run when starting Semantic grammar mode.") + +(defvar semantic-grammar-mode-keywords-1 + `(("\\(\\<%%\\>\\|\\<%[{}]\\)" + 0 font-lock-reference-face) + ("\\(%\\)\\(\\(\\sw\\|\\s_\\)+\\)" + (1 font-lock-reference-face) + (2 font-lock-keyword-face)) + ("\\<error\\>" + 0 (unless (semantic-grammar-in-lisp-p) 'bold)) + ("^\\(\\(\\sw\\|\\s_\\)+\\)[ \n\r\t]*:" + 1 font-lock-function-name-face) + (semantic--grammar-macros-matcher + 1 ,(if (boundp 'font-lock-builtin-face) + 'font-lock-builtin-face + 'font-lock-preprocessor-face)) + ("\\$\\(\\sw\\|\\s_\\)*" + 0 font-lock-variable-name-face) + ("<\\(\\(\\sw\\|\\s_\\)+\\)>" + 1 font-lock-type-face) + (,semantic-grammar-lex-c-char-re + 0 ,(if (boundp 'font-lock-constant-face) + 'font-lock-constant-face + 'font-lock-string-face) t) + ;; Must highlight :keyword here, because ':' is a punctuation in + ;; grammar mode! + ("[\r\n\t ]+:\\sw+\\>" + 0 font-lock-builtin-face) + ;; ;; Append the Semantic keywords + ;; ,@semantic-fw-font-lock-keywords + ) + "Font Lock keywords used to highlight Semantic grammar buffers.") + +(defvar semantic-grammar-mode-keywords-2 + (append semantic-grammar-mode-keywords-1 + lisp-font-lock-keywords-1) + "Font Lock keywords used to highlight Semantic grammar buffers.") + +(defvar semantic-grammar-mode-keywords-3 + (append semantic-grammar-mode-keywords-1 + lisp-font-lock-keywords-2) + "Font Lock keywords used to highlight Semantic grammar buffers.") + +(defvar semantic-grammar-mode-keywords + semantic-grammar-mode-keywords-1 + "Font Lock keywords used to highlight Semantic grammar buffers.") + +(defvar semantic-grammar-map + (let ((km (make-sparse-keymap))) + + (define-key km "|" 'semantic-grammar-electric-punctuation) + (define-key km ";" 'semantic-grammar-electric-punctuation) + (define-key km "%" 'semantic-grammar-electric-punctuation) + (define-key km "(" 'semantic-grammar-electric-punctuation) + (define-key km ")" 'semantic-grammar-electric-punctuation) + (define-key km ":" 'semantic-grammar-electric-punctuation) + + (define-key km "\t" 'semantic-grammar-indent) + (define-key km "\M-\t" 'semantic-grammar-complete) + (define-key km "\C-c\C-c" 'semantic-grammar-create-package) + (define-key km "\C-cm" 'semantic-grammar-find-macro-expander) + (define-key km "\C-cik" 'semantic-grammar-insert-keyword) +;; (define-key km "\C-cc" 'semantic-grammar-generate-and-load) +;; (define-key km "\C-cr" 'semantic-grammar-generate-one-rule) + + km) + "Keymap used in `semantic-grammar-mode'.") + +(defvar semantic-grammar-menu + '("Grammar" + ["Indent Line" semantic-grammar-indent] + ["Complete Symbol" semantic-grammar-complete] + ["Find Macro" semantic-grammar-find-macro-expander] + "--" + ["Insert %keyword" semantic-grammar-insert-keyword] + "--" + ["Update Lisp Package" semantic-grammar-create-package] + ["Recreate Lisp Package" semantic-grammar-recreate-package] + ) + "Common semantic grammar menu.") + +(defun semantic-grammar-setup-menu-emacs (symbol mode-menu) + "Setup a GNU Emacs grammar menu in variable SYMBOL. +MODE-MENU is an optional specific menu whose items are appended to the +common grammar menu." + (let ((items (make-symbol "items"))) + `(unless (boundp ',symbol) + (easy-menu-define ,symbol (current-local-map) + "Grammar Menu" semantic-grammar-menu) + (let ((,items (cdr ,mode-menu))) + (when ,items + (easy-menu-add-item ,symbol nil "--") + (while ,items + (easy-menu-add-item ,symbol nil (car ,items)) + (setq ,items (cdr ,items)))))) + )) + +(defun semantic-grammar-setup-menu-xemacs (symbol mode-menu) + "Setup an XEmacs grammar menu in variable SYMBOL. +MODE-MENU is an optional specific menu whose items are appended to the +common grammar menu." + (let ((items (make-symbol "items")) + (path (make-symbol "path"))) + `(progn + (unless (boundp ',symbol) + (easy-menu-define ,symbol nil + "Grammar Menu" (copy-sequence semantic-grammar-menu))) + (easy-menu-add ,symbol) + (let ((,items (cdr ,mode-menu)) + (,path (list (car ,symbol)))) + (when ,items + (easy-menu-add-item nil ,path "--") + (while ,items + (easy-menu-add-item nil ,path (car ,items)) + (setq ,items (cdr ,items)))))) + )) + +(defmacro semantic-grammar-setup-menu (&optional mode-menu) + "Setup a mode local grammar menu. +MODE-MENU is an optional specific menu whose items are appended to the +common grammar menu." + (let ((menu (intern (format "%s-menu" major-mode)))) + (if (featurep 'xemacs) + (semantic-grammar-setup-menu-xemacs menu mode-menu) + (semantic-grammar-setup-menu-emacs menu mode-menu)))) + +(defsubst semantic-grammar-in-lisp-p () + "Return non-nil if point is in Lisp code." + (or (>= (point) (semantic-grammar-epilogue-start)) + (condition-case nil + (save-excursion + (up-list -1) + t) + (error nil)))) + +(defun semantic-grammar-edits-new-change-hook-fcn (overlay) + "Function set into `semantic-edits-new-change-hook'. +Argument OVERLAY is the overlay created to mark the change. +When OVERLAY marks a change in the scope of a nonterminal tag extend +the change bounds to encompass the whole nonterminal tag." + (let ((outer (car (semantic-find-tag-by-overlay-in-region + (semantic-edits-os overlay) + (semantic-edits-oe overlay))))) + (if (semantic-tag-of-class-p outer 'nonterminal) + (semantic-overlay-move overlay + (semantic-tag-start outer) + (semantic-tag-end outer))))) + +(defun semantic-grammar-mode () + "Initialize a buffer for editing Semantic grammars. + +\\{semantic-grammar-map}" + (interactive) + (kill-all-local-variables) + (setq major-mode 'semantic-grammar-mode + mode-name "Semantic Grammar Framework") + (set (make-local-variable 'parse-sexp-ignore-comments) t) + (set (make-local-variable 'comment-start) ";;") + ;; Look within the line for a ; following an even number of backslashes + ;; after either a non-backslash or the line beginning. + (set (make-local-variable 'comment-start-skip) + "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\);+ *") + (set-syntax-table semantic-grammar-syntax-table) + (use-local-map semantic-grammar-map) + (set (make-local-variable 'indent-line-function) + 'semantic-grammar-indent) + (set (make-local-variable 'fill-paragraph-function) + 'lisp-fill-paragraph) + (set (make-local-variable 'font-lock-multiline) + 'undecided) + (set (make-local-variable 'font-lock-defaults) + '((semantic-grammar-mode-keywords + semantic-grammar-mode-keywords-1 + semantic-grammar-mode-keywords-2 + semantic-grammar-mode-keywords-3) + nil ;; perform string/comment fontification + nil ;; keywords are case sensitive. + ;; This puts _ & - as a word constituant, + ;; simplifying our keywords significantly + ((?_ . "w") (?- . "w")))) + ;; Setup Semantic to parse grammar + (semantic-grammar-wy--install-parser) + (setq semantic-lex-comment-regex ";;" + semantic-lex-analyzer 'semantic-grammar-lexer + semantic-type-relation-separator-character '(":") + semantic-symbol->name-assoc-list + '( + (code . "Setup Code") + (keyword . "Keyword") + (token . "Token") + (nonterminal . "Nonterminal") + (rule . "Rule") + )) + (set (make-local-variable 'semantic-format-face-alist) + '( + (code . default) + (keyword . font-lock-keyword-face) + (token . font-lock-type-face) + (nonterminal . font-lock-function-name-face) + (rule . default) + )) + (set (make-local-variable 'semantic-stickyfunc-sticky-classes) + '(nonterminal)) + ;; Before each change, clear the cached regexp used to highlight + ;; macros local in this grammar. + (semantic-make-local-hook 'before-change-functions) + (add-hook 'before-change-functions + 'semantic--grammar-clear-macros-regexp-2 nil t) + ;; Handle safe re-parse of grammar rules. + (semantic-make-local-hook 'semantic-edits-new-change-hooks) + (add-hook 'semantic-edits-new-change-hooks + 'semantic-grammar-edits-new-change-hook-fcn + nil t) + (semantic-run-mode-hooks 'semantic-grammar-mode-hook)) + +;;;; +;;;; Useful commands +;;;; + +(defvar semantic-grammar-skip-quoted-syntax-table + (let ((st (copy-syntax-table semantic-grammar-syntax-table))) + (modify-syntax-entry ?\' "$" st) + st) + "Syntax table to skip a whole quoted expression in grammar code. +Consider quote as a \"paired delimiter\", so `forward-sexp' will skip +whole quoted expression.") + +(defsubst semantic-grammar-backward-item () + "Move point to beginning of the previous grammar item." + (forward-comment (- (point-max))) + (if (zerop (skip-syntax-backward ".")) + (if (eq (char-before) ?\') + (with-syntax-table + ;; Can't be Lisp code here! Temporarily consider quote + ;; as a "paired delimiter", so `forward-sexp' can skip + ;; the whole quoted expression. + semantic-grammar-skip-quoted-syntax-table + (forward-sexp -1)) + (forward-sexp -1)))) + +(defun semantic-grammar-anchored-indentation () + "Return indentation based on previous anchor character found." + (let (indent) + (save-excursion + (while (not indent) + (semantic-grammar-backward-item) + (cond + ((bobp) + (setq indent 0)) + ((looking-at ":\\(\\s-\\|$\\)") + (setq indent (current-column)) + (forward-char) + (skip-syntax-forward "-") + (if (eolp) (setq indent 2)) + ) + ((and (looking-at "[;%]") + (not (looking-at "\\<%prec\\>"))) + (setq indent 0) + )))) + indent)) + +(defun semantic-grammar-do-grammar-indent () + "Indent a line of grammar. +When called the point is not in Lisp code." + (let (indent n) + (save-excursion + (beginning-of-line) + (skip-syntax-forward "-") + (setq indent (current-column)) + (cond + ((or (bobp) + (looking-at "\\(\\w\\|\\s_\\)+\\s-*:") + (and (looking-at "%") + (not (looking-at "%prec\\>")))) + (setq n 0)) + ((looking-at ":") + (setq n 2)) + ((and (looking-at ";;") + (save-excursion (forward-comment (point-max)) + (looking-at ":"))) + (setq n 1)) + (t + (setq n (semantic-grammar-anchored-indentation)) + (unless (zerop n) + (cond + ((looking-at ";;") + (setq n (1- n))) + ((looking-at "[|;]") + ) + (t + (setq n (+ n 2))))))) + (when (/= n indent) + (beginning-of-line) + (delete-horizontal-space) + (indent-to n))))) + +(defvar semantic-grammar-brackets-as-parens-syntax-table + (let ((st (copy-syntax-table emacs-lisp-mode-syntax-table))) + (modify-syntax-entry ?\{ "(} " st) + (modify-syntax-entry ?\} "){ " st) + st) + "Syntax table that consider brackets as parenthesis. +So `lisp-indent-line' will work inside bracket blocks.") + +(defun semantic-grammar-do-lisp-indent () + "Maybe run the Emacs Lisp indenter on a line of code. +Return nil if not in a Lisp expression." + (condition-case nil + (save-excursion + (beginning-of-line) + (skip-chars-forward "\t ") + (let ((first (point))) + (or (>= first (semantic-grammar-epilogue-start)) + (up-list -1)) + (condition-case nil + (while t + (up-list -1)) + (error nil)) + (beginning-of-line) + (save-restriction + (narrow-to-region (point) first) + (goto-char (point-max)) + (with-syntax-table + ;; Temporarily consider brackets as parenthesis so + ;; `lisp-indent-line' can indent Lisp code inside + ;; brackets. + semantic-grammar-brackets-as-parens-syntax-table + (lisp-indent-line)))) + t) + (error nil))) + +(defun semantic-grammar-indent () + "Indent the current line. +Use the Lisp or grammar indenter depending on point location." + (interactive) + (let ((orig (point)) + first) + (or (semantic-grammar-do-lisp-indent) + (semantic-grammar-do-grammar-indent)) + (setq first (save-excursion + (beginning-of-line) + (skip-chars-forward "\t ") + (point))) + (if (or (< orig first) (/= orig (point))) + (goto-char first)))) + +(defun semantic-grammar-electric-punctuation () + "Insert and reindent for the symbol just typed in." + (interactive) + (self-insert-command 1) + (save-excursion + (semantic-grammar-indent))) + +(defun semantic-grammar-complete () + "Attempt to complete the symbol under point. +Completion is position sensitive. If the cursor is in a match section of +a rule, then nonterminals symbols are scanned. If the cursor is in a Lisp +expression then Lisp symbols are completed." + (interactive) + (if (semantic-grammar-in-lisp-p) + ;; We are in lisp code. Do lisp completion. + (lisp-complete-symbol) + ;; We are not in lisp code. Do rule completion. + (let* ((nonterms (semantic-find-tags-by-class 'nonterminal (current-buffer))) + (sym (car (semantic-ctxt-current-symbol))) + (ans (try-completion sym nonterms))) + (cond ((eq ans t) + ;; All done + (message "Symbols is already complete")) + ((and (stringp ans) (string= ans sym)) + ;; Max matchable. Show completions. + (with-output-to-temp-buffer "*Completions*" + (display-completion-list (all-completions sym nonterms))) + ) + ((stringp ans) + ;; Expand the completions + (forward-sexp -1) + (delete-region (point) (progn (forward-sexp 1) (point))) + (insert ans)) + (t (message "No Completions.")) + )) + )) + +(defun semantic-grammar-insert-keyword (name) + "Insert a new %keyword declaration with NAME. +Assumes it is typed in with the correct casing." + (interactive "sKeyword: ") + (if (not (bolp)) (insert "\n")) + (insert "%keyword " (upcase name) " \"" name "\" +%put " (upcase name) " summary +\"\"\n") + (forward-char -2)) + +;;; Macro facilities +;; + +(defsubst semantic--grammar-macro-function-tag (name) + "Search for a function tag for the grammar macro with name NAME. +Return the tag found or nil if not found." + (car (semantic-find-tags-by-class + 'function + (or (semantic-find-tags-by-name name (current-buffer)) + (and (featurep 'semanticdb) + semanticdb-current-database + (cdar (semanticdb-find-tags-by-name name nil t))))))) + +(defsubst semantic--grammar-macro-lib-part (def) + "Return the library part of the grammar macro defined by DEF." + (let ((suf (format "-%s\\'" (regexp-quote (symbol-name (car def))))) + (fun (symbol-name (cdr def)))) + (substring fun 0 (string-match suf fun)))) + +(defun semantic--grammar-macro-compl-elt (def &optional full) + "Return a completion entry for the grammar macro defined by DEF. +If optional argument FULL is non-nil qualify the macro name with the +library found in DEF." + (let ((mac (car def)) + (lib (semantic--grammar-macro-lib-part def))) + (cons (if full + (format "%s/%s" mac lib) + (symbol-name mac)) + (list mac lib)))) + +(defun semantic--grammar-macro-compl-dict () + "Return a completion dictionnary of macro definitions." + (let ((defs (semantic-grammar-macros)) + def dups dict) + (while defs + (setq def (car defs) + defs (cdr defs)) + (if (or (assoc (car def) defs) (assoc (car def) dups)) + (push def dups) + (push (semantic--grammar-macro-compl-elt def) dict))) + (while dups + (setq def (car dups) + dups (cdr dups)) + (push (semantic--grammar-macro-compl-elt def t) dict)) + dict)) + +(defun semantic-grammar-find-macro-expander (macro-name library) + "Visit the Emacs Lisp library where a grammar macro is implemented. +MACRO-NAME is a symbol that identifies a grammar macro. +LIBRARY is the name (sans extension) of the Emacs Lisp library where +to start searching the macro implementation. Lookup in included +libraries, if necessary. +Find a function tag (in current tags table) whose name contains MACRO-NAME. +Select the buffer containing the tag's definition, and move point there." + (interactive + (let* ((dic (semantic--grammar-macro-compl-dict)) + (def (assoc (completing-read "Macro: " dic nil 1) dic))) + (or (cdr def) '(nil nil)))) + (when (and macro-name library) + (let* ((lib (format "%s.el" library)) + (buf (find-file-noselect (or (locate-library lib t) lib))) + (tag (with-current-buffer buf + (semantic--grammar-macro-function-tag + (format "%s-%s" library macro-name))))) + (if tag + (progn + (require 'semantic/decorate) + (pop-to-buffer (semantic-tag-buffer tag)) + (goto-char (semantic-tag-start tag)) + (semantic-momentary-highlight-tag tag)) + (pop-to-buffer buf) + (message "No expander found in library %s for macro %s" + library macro-name))))) + +;;; Additional help +;; + +(defvar semantic-grammar-syntax-help + `( + ;; Lexical Symbols + ("symbol" . "Syntax: A symbol of alpha numeric and symbol characters") + ("number" . "Syntax: Numeric characters.") + ("punctuation" . "Syntax: Punctuation character.") + ("semantic-list" . "Syntax: A list delimited by any valid list characters") + ("open-paren" . "Syntax: Open Parenthesis character") + ("close-paren" . "Syntax: Close Parenthesis character") + ("string" . "Syntax: String character delimited text") + ("comment" . "Syntax: Comment character delimited text") + ;; Special Macros + ("EMPTY" . "Syntax: Match empty text") + ("ASSOC" . "Lambda Key: (ASSOC key1 value1 key2 value2 ...)") + ("EXPAND" . "Lambda Key: (EXPAND <list id> <rule>)") + ("EXPANDFULL" . "Lambda Key: (EXPANDFULL <list id> <rule>)") + ;; Tag Generator Macros + ("TAG" . "Generic Tag Generation: (TAG <name> <tag-class> [ :key value ]*)") + ("VARIABLE-TAG" . "(VARIABLE-TAG <name> <lang-type> <default-value> [ :key value ]*)") + ("FUNCTION-TAG" . "(FUNCTION-TAG <name> <lang-type> <arg-list> [ :key value ]*)") + ("TYPE-TAG" . "(TYPE-TAG <name> <lang-type> <part-list> <parents> [ :key value ]*)") + ("INCLUDE-TAG" . "(INCLUDE-TAG <name> <system-flag> [ :key value ]*)") + ("PACKAGE-TAG" . "(PACKAGE-TAG <name> <detail> [ :key value ]*)") + ("CODE-TAG" . "(CODE-TAG <name> <detail> [ :key value ]*)") + ("ALIAS-TAG" . "(ALIAS-TAG <name> <aliasclass> <definition> [:key value]*)") + ;; Special value macros + ("$1" . "Match Value: Value from match list in slot 1") + ("$2" . "Match Value: Value from match list in slot 2") + ("$3" . "Match Value: Value from match list in slot 3") + ("$4" . "Match Value: Value from match list in slot 4") + ("$5" . "Match Value: Value from match list in slot 5") + ("$6" . "Match Value: Value from match list in slot 6") + ("$7" . "Match Value: Value from match list in slot 7") + ("$8" . "Match Value: Value from match list in slot 8") + ("$9" . "Match Value: Value from match list in slot 9") + ;; Same, but with annoying , in front. + (",$1" . "Match Value: Value from match list in slot 1") + (",$2" . "Match Value: Value from match list in slot 2") + (",$3" . "Match Value: Value from match list in slot 3") + (",$4" . "Match Value: Value from match list in slot 4") + (",$5" . "Match Value: Value from match list in slot 5") + (",$6" . "Match Value: Value from match list in slot 6") + (",$7" . "Match Value: Value from match list in slot 7") + (",$8" . "Match Value: Value from match list in slot 8") + (",$9" . "Match Value: Value from match list in slot 9") + ) + "Association of syntax elements, and the corresponding help.") + +(defun semantic-grammar-eldoc-get-macro-docstring (macro expander) + "Return a one-line docstring for the given grammar MACRO. +EXPANDER is the name of the function that expands MACRO." + (require 'eldoc) + (if (and (eq expander (aref eldoc-last-data 0)) + (eq 'function (aref eldoc-last-data 2))) + (aref eldoc-last-data 1) + (let ((doc (help-split-fundoc (documentation expander t) expander))) + (cond + (doc + (setq doc (car doc)) + (string-match "\\`[^ )]* ?" doc) + (setq doc (concat "(" (substring doc (match-end 0))))) + (t + (setq doc (eldoc-function-argstring expander)))) + (when doc + (setq doc + (eldoc-docstring-format-sym-doc + macro (format "==> %s %s" expander doc) 'default)) + (eldoc-last-data-store expander doc 'function)) + doc))) + +(define-mode-local-override semantic-idle-summary-current-symbol-info + semantic-grammar-mode () + "Display additional eldoc information about grammar syntax elements. +Syntax element is the current symbol at point. +If it is associated a help string in `semantic-grammar-syntax-help', +return that string. +If it is a macro name, return a description of the associated expander +function parameter list. +If it is a function name, return a description of this function +parameter list. +It it is a variable name, return a brief (one-line) documentation +string for the variable. +If a default description of the current context can be obtained, +return it. +Otherwise return nil." + (require 'eldoc) + (let* ((elt (car (semantic-ctxt-current-symbol))) + (val (and elt (cdr (assoc elt semantic-grammar-syntax-help))))) + (when (and (not val) elt (semantic-grammar-in-lisp-p)) + ;; Ensure to load macro definitions before doing `intern-soft'. + (setq val (semantic-grammar-macros) + elt (intern-soft elt) + val (and elt (cdr (assq elt val)))) + (cond + ;; Grammar macro + ((and val (fboundp val)) + (setq val (semantic-grammar-eldoc-get-macro-docstring elt val))) + ;; Function + ((and elt (fboundp elt)) + (setq val (eldoc-get-fnsym-args-string elt))) + ;; Variable + ((and elt (boundp elt)) + (setq val (eldoc-get-var-docstring elt))) + (t nil))) + (or val (semantic-idle-summary-current-symbol-info-default)))) + +(define-mode-local-override semantic-tag-boundary-p + semantic-grammar-mode (tag) + "Return non-nil for tags that should have a boundary drawn. +Only tags of type 'nonterminal will be so marked." + (let ((c (semantic-tag-class tag))) + (eq c 'nonterminal))) + +(define-mode-local-override semantic-ctxt-current-function + semantic-grammar-mode (&optional point) + "Determine the name of the current function at POINT." + (save-excursion + (and point (goto-char point)) + (when (semantic-grammar-in-lisp-p) + (with-mode-local emacs-lisp-mode + (semantic-ctxt-current-function))))) + +(define-mode-local-override semantic-ctxt-current-argument + semantic-grammar-mode (&optional point) + "Determine the argument index of the called function at POINT." + (save-excursion + (and point (goto-char point)) + (when (semantic-grammar-in-lisp-p) + (with-mode-local emacs-lisp-mode + (semantic-ctxt-current-argument))))) + +(define-mode-local-override semantic-ctxt-current-assignment + semantic-grammar-mode (&optional point) + "Determine the tag being assigned into at POINT." + (save-excursion + (and point (goto-char point)) + (when (semantic-grammar-in-lisp-p) + (with-mode-local emacs-lisp-mode + (semantic-ctxt-current-assignment))))) + +(define-mode-local-override semantic-ctxt-current-class-list + semantic-grammar-mode (&optional point) + "Determine the class of tags that can be used at POINT." + (save-excursion + (and point (goto-char point)) + (if (semantic-grammar-in-lisp-p) + (with-mode-local emacs-lisp-mode + (semantic-ctxt-current-class-list)) + '(nonterminal keyword)))) + +(define-mode-local-override semantic-ctxt-current-mode + semantic-grammar-mode (&optional point) + "Return the major mode active at POINT. +POINT defaults to the value of point in current buffer. +Return `emacs-lisp-mode' is POINT is within Lisp code, otherwise +return the current major mode." + (save-excursion + (and point (goto-char point)) + (if (semantic-grammar-in-lisp-p) + 'emacs-lisp-mode + (semantic-ctxt-current-mode-default)))) + +(define-mode-local-override semantic-format-tag-abbreviate + semantic-grammar-mode (tag &optional parent color) + "Return a string abbreviation of TAG. +Optional PARENT is not used. +Optional COLOR is used to flag if color is added to the text." + (let ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color))) + (cond + ((eq class 'nonterminal) + (concat name ":")) + ((eq class 'setting) + "%settings%") + ((memq class '(rule keyword)) + name) + (t + (concat "%" (symbol-name class) " " name))))) + +(define-mode-local-override semantic-format-tag-summarize + semantic-grammar-mode (tag &optional parent color) + "Return a string summarizing TAG. +Optional PARENT is not used. +Optional argument COLOR determines if color is added to the text." + (let ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color)) + (label nil) + (desc nil)) + (cond + ((eq class 'nonterminal) + (setq label "Nonterminal: " + desc (format + " with %d match lists." + (length (semantic-tag-components tag))))) + ((eq class 'keyword) + (setq label "Keyword: ") + (let (summary) + (semantic--find-tags-by-function + #'(lambda (put) + (unless summary + (setq summary (cdr (assoc "summary" + (semantic-tag-get-attribute + put :value)))))) + ;; Get `put' tag with TAG name. + (semantic-find-tags-by-name-regexp + (regexp-quote (semantic-tag-name tag)) + (semantic-find-tags-by-class 'put (current-buffer)))) + (setq desc (concat " = " + (semantic-tag-get-attribute tag :value) + (if summary + (concat " - " (read summary)) + ""))))) + ((eq class 'token) + (setq label "Token: ") + (let ((val (semantic-tag-get-attribute tag :value)) + (names (semantic-tag-get-attribute tag :rest)) + (type (semantic-tag-type tag))) + (if names + (setq name (mapconcat 'identity (cons name names) " "))) + (setq desc (concat + (if type + (format " <%s>" type) + "") + (if val + (format "%s%S" val (if type " " "")) + ""))))) + ((eq class 'assoc) + (setq label "Assoc: ") + (let ((val (semantic-tag-get-attribute tag :value)) + (type (semantic-tag-type tag))) + (setq desc (concat + (if type + (format " <%s>" type) + "") + (if val + (concat " " (mapconcat 'identity val " ")) + ""))))) + (t + (setq desc (semantic-format-tag-abbreviate tag parent color)))) + (if (and color label) + (setq label (semantic--format-colorize-text label 'label))) + (if (and color label desc) + (setq desc (semantic--format-colorize-text desc 'comment))) + (if label + (concat label name desc) + ;; Just a description is the abbreviated version + desc))) + +;;; Semantic Analysis + +(define-mode-local-override semantic-analyze-current-context + semantic-grammar-mode (point) + "Provide a semantic analysis object describing a context in a grammar." + (require 'semantic/analyze) + (if (semantic-grammar-in-lisp-p) + (with-mode-local emacs-lisp-mode + (semantic-analyze-current-context point)) + + (let* ((context-return nil) + (prefixandbounds (semantic-ctxt-current-symbol-and-bounds)) + (prefix (car prefixandbounds)) + (bounds (nth 2 prefixandbounds)) + (prefixsym nil) + (prefixclass (semantic-ctxt-current-class-list)) + ) + + ;; Do context for rules when in a match list. + (setq prefixsym + (semantic-find-first-tag-by-name + (car prefix) + (current-buffer))) + + (setq context-return + (semantic-analyze-context + "context-for-semantic-grammar" + :buffer (current-buffer) + :scope nil + :bounds bounds + :prefix (if prefixsym + (list prefixsym) + prefix) + :prefixtypes nil + :prefixclass prefixclass + )) + + context-return))) + +(define-mode-local-override semantic-analyze-possible-completions + semantic-grammar-mode (context) + "Return a list of possible completions based on CONTEXT." + (require 'semantic/analyze/complete) + (if (semantic-grammar-in-lisp-p) + (with-mode-local emacs-lisp-mode + (semantic-analyze-possible-completions context)) + (save-excursion + (set-buffer (oref context buffer)) + (let* ((prefix (car (oref context :prefix))) + (completetext (cond ((semantic-tag-p prefix) + (semantic-tag-name prefix)) + ((stringp prefix) + prefix) + ((stringp (car prefix)) + (car prefix)))) + (tags (semantic-find-tags-for-completion completetext + (current-buffer)))) + (semantic-analyze-tags-of-class-list + tags (oref context prefixclass))) + ))) + +(provide 'semantic/grammar) + +;;; semantic/grammar.el ends here diff --git a/lisp/cedet/semantic/html.el b/lisp/cedet/semantic/html.el new file mode 100644 index 00000000000..c1d9276ff1e --- /dev/null +++ b/lisp/cedet/semantic/html.el @@ -0,0 +1,260 @@ +;;; semantic/html.el --- Semantic details for html files + +;;; Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Parse HTML files and organize them in a nice way. +;; Pay attention to anchors, including them in the tag list. +;; +;; Copied from the original semantic-texi.el. +;; +;; ToDo: Find <script> tags, and parse the contents in other +;; parsers, such as javascript, php, shtml, or others. + +;;; Code: + +(require 'semantic) +(require 'semantic/format) +(require 'sgml-mode) + +(defvar semantic-command-separation-character) + +(defvar semantic-html-super-regex + "<\\(h[1-9]\\|title\\|script\\|body\\|a +href\\)\\>" + "Regular expression used to find special sections in an HTML file.") + +(defvar semantic-html-section-list + '(("title" 1) + ("script" 1) + ("body" 1) + ("a" 11) + ("h1" 2) + ("h2" 3) + ("h3" 4) + ("h4" 5) + ("h5" 6) + ("h6" 7) + ("h7" 8) + ("h8" 9) + ("h9" 10) + ) + "Alist of sectioning commands and their relative level.") + +(define-mode-local-override semantic-parse-region + html-mode (&rest ignore) + "Parse the current html buffer for semantic tags. +INGNORE any arguments. Always parse the whole buffer. +Each tag returned is of the form: + (\"NAME\" section (:members CHILDREN)) +or + (\"NAME\" anchor)" + (mapcar 'semantic-html-expand-tag + (semantic-html-parse-headings))) + +(define-mode-local-override semantic-parse-changes + html-mode () + "We can't parse changes for HTML mode right now." + (semantic-parse-tree-set-needs-rebuild)) + +(defun semantic-html-expand-tag (tag) + "Expand the HTML tag TAG." + (let ((chil (semantic-html-components tag))) + (if chil + (semantic-tag-put-attribute + tag :members (mapcar 'semantic-html-expand-tag chil))) + (car (semantic--tag-expand tag)))) + +(defun semantic-html-components (tag) + "Return components belonging to TAG." + (semantic-tag-get-attribute tag :members)) + +(defun semantic-html-parse-headings () + "Parse the current html buffer for all semantic tags." + (let ((pass1 nil)) + ;; First search and snarf. + (save-excursion + (goto-char (point-min)) + + (let ((semantic--progress-reporter + (make-progress-reporter + (format "Parsing %s..." + (file-name-nondirectory buffer-file-name)) + (point-min) (point-max)))) + (while (re-search-forward semantic-html-super-regex nil t) + (setq pass1 (cons (match-beginning 0) pass1)) + (progress-reporter-update semantic--progress-reporter (point))) + (progress-reporter-done semantic--progress-reporter))) + + (setq pass1 (nreverse pass1)) + ;; Now, make some tags while creating a set of children. + (car (semantic-html-recursive-combobulate-list pass1 0)) + )) + +(defun semantic-html-set-endpoint (metataglist pnt) + "Set the end point of the first section tag in METATAGLIST to PNT. +METATAGLIST is a list of tags in the intermediate tag format used by the +html parser. PNT is the new point to set." + (let ((metatag nil)) + (while (and metataglist + (not (eq (semantic-tag-class (car metataglist)) 'section))) + (setq metataglist (cdr metataglist))) + (setq metatag (car metataglist)) + (when metatag + (setcar (nthcdr (1- (length metatag)) metatag) pnt) + metatag))) + +(defsubst semantic-html-new-section-tag (name members level start end) + "Create a semantic tag of class section. +NAME is the name of this section. +MEMBERS is a list of semantic tags representing the elements that make +up this section. +LEVEL is the levelling level. +START and END define the location of data described by the tag." + (let ((anchorp (eq level 11))) + (append (semantic-tag name + (cond (anchorp 'anchor) + (t 'section)) + :members members) + (list start (if anchorp (point) end)) ))) + +(defun semantic-html-extract-section-name () + "Extract a section name from the current buffer and point. +Assume the cursor is in the tag representing the section we +need the name from." + (save-excursion + ; Skip over the HTML tag. + (forward-sexp -1) + (forward-char -1) + (forward-sexp 1) + (skip-chars-forward "\n\t ") + (while (looking-at "<") + (forward-sexp 1) + (skip-chars-forward "\n\t ") + ) + (let ((start (point)) + (end nil)) + (if (re-search-forward "</" nil t) + (progn + (goto-char (match-beginning 0)) + (skip-chars-backward " \n\t") + (setq end (point)) + (buffer-substring-no-properties start end)) + "")) + )) + +(defun semantic-html-recursive-combobulate-list (sectionlist level) + "Rearrange SECTIONLIST to be a hierarchical tag list starting at LEVEL. +Return the rearranged new list, with all remaining tags from +SECTIONLIST starting at ELT 2. Sections not are not dealt with as soon as a +tag with greater section value than LEVEL is found." + (let ((newl nil) + (oldl sectionlist) + (case-fold-search t) + tag + ) + (save-excursion + (catch 'level-jump + (while oldl + (goto-char (car oldl)) + (if (looking-at "<\\(\\w+\\)") + (let* ((word (match-string 1)) + (levelmatch (assoc-string + word semantic-html-section-list t)) + text begin tmp + ) + (when (not levelmatch) + (error "Tag %s matched in regexp but is not in list" + word)) + ;; Set begin to the right location + (setq begin (point)) + ;; Get out of here if there if we made it that far. + (if (and levelmatch (<= (car (cdr levelmatch)) level)) + (progn + (when newl + (semantic-html-set-endpoint newl begin)) + (throw 'level-jump t))) + ;; When there is a match, the descriptive text + ;; consists of the rest of the line. + (goto-char (match-end 1)) + (skip-chars-forward " \t") + (setq text (semantic-html-extract-section-name)) + ;; Next, recurse into the body to find the end. + (setq tmp (semantic-html-recursive-combobulate-list + (cdr oldl) (car (cdr levelmatch)))) + ;; Build a tag + (setq tag (semantic-html-new-section-tag + text (car tmp) (car (cdr levelmatch)) begin (point-max))) + ;; Before appending the newtag, update the previous tag + ;; if it is a section tag. + (when newl + (semantic-html-set-endpoint newl begin)) + ;; Append new tag to our master list. + (setq newl (cons tag newl)) + ;; continue + (setq oldl (cdr tmp)) + ) + (error "Problem finding section in semantic/html parser")) + ;; (setq oldl (cdr oldl)) + ))) + ;; Return the list + (cons (nreverse newl) oldl))) + +(define-mode-local-override semantic-sb-tag-children-to-expand + html-mode (tag) + "The children TAG expands to." + (semantic-html-components tag)) + +;;;###autoload +(defun semantic-default-html-setup () + "Set up a buffer for parsing of HTML files." + ;; This will use our parser. + (setq semantic-parser-name "HTML" + semantic--parse-table t + imenu-create-index-function 'semantic-create-imenu-index + semantic-command-separation-character ">" + semantic-type-relation-separator-character '(":") + semantic-symbol->name-assoc-list '((section . "Section") + + ) + semantic-imenu-expandable-tag-classes '(section) + semantic-imenu-bucketize-file nil + semantic-imenu-bucketize-type-members nil + senator-step-at-start-end-tag-classes '(section) + semantic-stickyfunc-sticky-classes '(section) + ) + (semantic-install-function-overrides + '((tag-components . semantic-html-components) + ) + t) + ) + +(define-child-mode html-helper-mode html-mode + "`html-helper-mode' needs the same semantic support as `html-mode'.") + +(provide 'semantic/html) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/html" +;; End: + +;;; semantic/html.el ends here diff --git a/lisp/cedet/semantic/ia-sb.el b/lisp/cedet/semantic/ia-sb.el new file mode 100644 index 00000000000..d9f223a90d1 --- /dev/null +++ b/lisp/cedet/semantic/ia-sb.el @@ -0,0 +1,374 @@ +;;; semantic/ia-sb.el --- Speedbar analysis display interactor + +;;; Copyright (C) 2002, 2003, 2004, 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: +;; +;; Speedbar node for displaying derived context information. +;; + +(require 'semantic/analyze) +(require 'speedbar) + +;;; Code: +(defvar semantic-ia-sb-key-map nil + "Keymap used when in semantic analysis display mode.") + +(if semantic-ia-sb-key-map + nil + (setq semantic-ia-sb-key-map (speedbar-make-specialized-keymap)) + + ;; Basic featuers. + (define-key semantic-ia-sb-key-map "\C-m" 'speedbar-edit-line) + (define-key semantic-ia-sb-key-map "I" 'semantic-ia-sb-show-tag-info) + ) + +(defvar semantic-ia-sb-easymenu-definition + '( "---" +; [ "Expand" speedbar-expand-line nil ] +; [ "Contract" speedbar-contract-line nil ] + [ "Tag Information" semantic-ia-sb-show-tag-info t ] + [ "Jump to Tag" speedbar-edit-line t ] + [ "Complete" speedbar-edit-line t ] + ) + "Extra menu items Analysis mode.") + +;; Make sure our special speedbar major mode is loaded +(speedbar-add-expansion-list '("Analyze" + semantic-ia-sb-easymenu-definition + semantic-ia-sb-key-map + semantic-ia-speedbar)) + +(speedbar-add-mode-functions-list + (list "Analyze" + ;;'(speedbar-item-info . eieio-speedbar-item-info) + '(speedbar-line-directory . semantic-ia-sb-line-path))) + +;;;###autoload +(defun semantic-speedbar-analysis () + "Start Speedbar in semantic analysis mode. +The analyzer displays information about the current context, plus a smart +list of possible completions." + (interactive) + ;; Make sure that speedbar is active + (speedbar-frame-mode 1) + ;; Now, throw us into Analyze mode on speedbar. + (speedbar-change-initial-expansion-list "Analyze") + ) + +(defun semantic-ia-speedbar (directory zero) + "Create buttons in speedbar which define the current analysis at POINT. +DIRECTORY is the current directory, which is ignored, and ZERO is 0." + (let ((analysis nil) + (scope nil) + (buffer nil) + (completions nil) + (cf (selected-frame)) + (cnt nil) + (mode-local-active-mode nil) + ) + ;; Try and get some sort of analysis + (condition-case nil + (progn + (speedbar-select-attached-frame) + (setq buffer (current-buffer)) + (setq mode-local-active-mode major-mode) + (save-excursion + ;; Get the current scope + (setq scope (semantic-calculate-scope (point))) + ;; Get the analysis + (setq analysis (semantic-analyze-current-context (point))) + (setq cnt (semantic-find-tag-by-overlay)) + (when analysis + (setq completions (semantic-analyze-possible-completions analysis)) + ) + )) + (error nil)) + (select-frame cf) + (save-excursion + (set-buffer speedbar-buffer) + ;; If we have something, do something spiff with it. + (erase-buffer) + (speedbar-insert-separator "Buffer/Function") + ;; Note to self: Turn this into an expandable file name. + (speedbar-make-tag-line 'bracket ? nil nil + (buffer-name buffer) + nil nil 'speedbar-file-face 0) + + (when cnt + (semantic-ia-sb-string-list cnt + 'speedbar-tag-face + 'semantic-sb-token-jump)) + (when analysis + ;; If this analyzer happens to point at a complete symbol, then + ;; see if we can dig up some documentation for it. + (semantic-ia-sb-show-doc analysis)) + + (when analysis + ;; Let different classes draw more buttons. + (semantic-ia-sb-more-buttons analysis) + (when completions + (speedbar-insert-separator "Completions") + (semantic-ia-sb-completion-list completions + 'speedbar-tag-face + 'semantic-ia-sb-complete)) + ) + + ;; Show local variables + (when scope + (semantic-ia-sb-show-scope scope)) + + ))) + +(defmethod semantic-ia-sb-show-doc ((context semantic-analyze-context)) + "Show documentation about CONTEXT iff CONTEXT points at a complete symbol." + (let ((sym (car (reverse (oref context prefix)))) + (doc nil)) + (when (semantic-tag-p sym) + (setq doc (semantic-documentation-for-tag sym)) + (when doc + (speedbar-insert-separator "Documentation") + (insert doc) + (insert "\n") + )) + )) + +(defun semantic-ia-sb-show-scope (scope) + "Show SCOPE information." + (let ((localvars (when scope + (oref scope localvar))) + ) + (when localvars + (speedbar-insert-separator "Local Variables") + (semantic-ia-sb-string-list localvars + 'speedbar-tag-face + ;; This is from semantic-sb + 'semantic-sb-token-jump)))) + +(defmethod semantic-ia-sb-more-buttons ((context semantic-analyze-context)) + "Show a set of speedbar buttons specific to CONTEXT." + (let ((prefix (oref context prefix))) + (when prefix + (speedbar-insert-separator "Prefix") + (semantic-ia-sb-string-list prefix + 'speedbar-tag-face + 'semantic-sb-token-jump)) + )) + +(defmethod semantic-ia-sb-more-buttons ((context semantic-analyze-context-assignment)) + "Show a set of speedbar buttons specific to CONTEXT." + (call-next-method) + (let ((assignee (oref context assignee))) + (when assignee + (speedbar-insert-separator "Assignee") + (semantic-ia-sb-string-list assignee + 'speedbar-tag-face + 'semantic-sb-token-jump)))) + +(defmethod semantic-ia-sb-more-buttons ((context semantic-analyze-context-functionarg)) + "Show a set of speedbar buttons specific to CONTEXT." + (call-next-method) + (let ((func (oref context function))) + (when func + (speedbar-insert-separator "Function") + (semantic-ia-sb-string-list func + 'speedbar-tag-face + 'semantic-sb-token-jump) + ;; An index for the argument the prefix is in: + (let ((arg (oref context argument)) + (args (semantic-tag-function-arguments (car func))) + (idx 0) + ) + (speedbar-insert-separator + (format "Argument #%d" (oref context index))) + (if args + (semantic-ia-sb-string-list args + 'speedbar-tag-face + 'semantic-sb-token-jump + (oref context index) + 'speedbar-selected-face) + ;; Else, no args list, so use what the context had. + (semantic-ia-sb-string-list arg + 'speedbar-tag-face + 'semantic-sb-token-jump)) + )))) + +(defun semantic-ia-sb-string-list (list face function &optional idx idxface) + "Create some speedbar buttons from LIST. +Each button will use FACE, and be activated with FUNCTION. +Optional IDX is an index into LIST to apply IDXFACE instead." + (let ((count 1)) + (while list + (let* ((usefn nil) + (string (cond ((stringp (car list)) + (car list)) + ((semantic-tag-p (car list)) + (setq usefn (semantic-tag-with-position-p (car list))) + (semantic-format-tag-uml-concise-prototype (car list))) + (t "<No Tag>"))) + (localface (if (or (not idx) (/= idx count)) + face + idxface)) + ) + (if (semantic-tag-p (car list)) + (speedbar-make-tag-line 'angle ?i + 'semantic-ia-sb-tag-info (car list) + string (if usefn function) (car list) localface + 0) + (speedbar-make-tag-line 'statictag ?? + nil nil + string (if usefn function) (car list) localface + 0)) + (setq list (cdr list) + count (1+ count))) + ))) + +(defun semantic-ia-sb-completion-list (list face function) + "Create some speedbar buttons from LIST. +Each button will use FACE, and be activated with FUNCTION." + (while list + (let* ((documentable nil) + (string (cond ((stringp (car list)) + (car list)) + ((semantic-tag-p (car list)) + (setq documentable t) + (semantic-format-tag-uml-concise-prototype (car list))) + (t "foo")))) + (if documentable + (speedbar-make-tag-line 'angle ?i + 'semantic-ia-sb-tag-info + (car list) + string function (car list) face + 0) + (speedbar-make-tag-line 'statictag ? nil nil + string function (car list) face + 0)) + (setq list (cdr list))))) + +(defun semantic-ia-sb-show-tag-info () + "Display information about the tag on the current line. +Same as clicking on the <i> button. +See `semantic-ia-sb-tag-info' for more." + (interactive) + (let ((tok nil)) + (save-excursion + (end-of-line) + (forward-char -1) + (setq tok (get-text-property (point) 'speedbar-token))) + (semantic-ia-sb-tag-info nil tok 0))) + +(defun semantic-ia-sb-tag-info (text tag indent) + "Display as much information as we can about tag. +Show the information in a shrunk split-buffer and expand +out as many details as possible. +TEXT, TAG, and INDENT are speedbar function arguments." + (when (semantic-tag-p tag) + (unwind-protect + (let ((ob nil)) + (speedbar-select-attached-frame) + (setq ob (current-buffer)) + (with-output-to-temp-buffer "*Tag Information*" + ;; Output something about this tag: + (save-excursion + (set-buffer "*Tag Information*") + (goto-char (point-max)) + (insert + (semantic-format-tag-prototype tag nil t) + "\n") + (let ((typetok + (condition-case nil + (save-excursion + (set-buffer ob) + ;; @todo - We need a context to derive a scope from. + (semantic-analyze-tag-type tag nil)) + (error nil)))) + (if typetok + (insert (semantic-format-tag-prototype + typetok nil t)) + ;; No type found by the analyzer + ;; The below used to try and select the buffer from the last + ;; analysis, but since we are already in the correct buffer, I + ;; don't think that is needed. + (let ((type (semantic-tag-type tag))) + (cond ((semantic-tag-p type) + (setq type (semantic-tag-name type))) + ((listp type) + (setq type (car type)))) + (if (semantic-lex-keyword-p type) + (setq typetok + (semantic-lex-keyword-get type 'summary)))) + (if typetok + (insert typetok)) + )) + )) + ;; Make it small + (shrink-window-if-larger-than-buffer + (get-buffer-window "*Tag Information*"))) + (select-frame speedbar-frame)))) + +(defun semantic-ia-sb-line-path (&optional depth) + "Return the file name associated with DEPTH." + (save-match-data + (let* ((tok (speedbar-line-token)) + (buff (if (semantic-tag-buffer tok) + (semantic-tag-buffer tok) + (current-buffer)))) + (buffer-file-name buff)))) + +(defun semantic-ia-sb-complete (text tag indent) + "At point in the attached buffer, complete the symbol clicked on. +TEXT TAG and INDENT are the details." + ;; Find the specified bounds from the current analysis. + (speedbar-select-attached-frame) + (unwind-protect + (let* ((a (semantic-analyze-current-context (point))) + (bounds (oref a bounds)) + (movepoint nil) + ) + (save-excursion + (if (and (<= (point) (cdr bounds)) (>= (point) (car bounds))) + (setq movepoint t)) + (goto-char (car bounds)) + (delete-region (car bounds) (cdr bounds)) + (insert (semantic-tag-name tag)) + (if movepoint (setq movepoint (point))) + ;; I'd like to use this to add fancy () or what not at the end + ;; but we need the parent file whih requires an upgrade to the + ;; analysis tool. + ;;(semantic-insert-foreign-tag tag ??)) + ) + (if movepoint + (let ((cf (selected-frame))) + (speedbar-select-attached-frame) + (goto-char movepoint) + (select-frame cf)))) + (select-frame speedbar-frame))) + +(provide 'semantic/ia-sb) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/ia-sb" +;; End: + +;;; semantic/ia-sb.el ends here diff --git a/lisp/cedet/semantic/ia.el b/lisp/cedet/semantic/ia.el new file mode 100644 index 00000000000..1036a9b5b98 --- /dev/null +++ b/lisp/cedet/semantic/ia.el @@ -0,0 +1,422 @@ +;;; semantic/ia.el --- Interactive Analysis functions + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, +;;; 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: +;; +;; Interactive access to `semantic-analyze'. +;; +;; These routines are fairly simple, and show how to use the Semantic +;; analyzer to provide things such as completion lists, summaries, +;; locations, or documentation. +;; + +;;; TODO +;; +;; fast-jump. For a virtual method, offer some of the possible +;; implementations in various sub-classes. + +(require 'semantic/analyze) +(require 'semantic/format) +(require 'pulse) +(eval-when-compile + (require 'semantic/analyze) + (require 'semantic/analyze/refs)) + +(declare-function imenu--mouse-menu "imenu") + +;;; Code: + +;;; COMPLETION +;; +;; This set of routines provides some simplisting completion +;; functions. + +(defcustom semantic-ia-completion-format-tag-function + 'semantic-prototype-nonterminal + "*Function used to convert a tag to a string during completion." + :group 'semantic + :type semantic-format-tag-custom-list) + +(defvar semantic-ia-cache nil + "Cache of the last completion request. +Of the form ( POINT . COMPLETIONS ) where POINT is a location in the +buffer where the completion was requested. COMPLETONS is the list +of semantic tag names that provide logical completions from that +location.") +(make-variable-buffer-local 'semantic-ia-cache) + +;;; COMPLETION HELPER +;; +;; This overload function handles inserting a tag +;; into a buffer for these local completion routines. +;; +;; By creating the functions as overloadable, it can be +;; customized. For example, the default will put a paren "(" +;; character after function names. For Lisp, it might check +;; to put a "(" in front of a function name. + +(define-overloadable-function semantic-ia-insert-tag (tag) + "Insert TAG into the current buffer based on completion.") + +(defun semantic-ia-insert-tag-default (tag) + "Insert TAG into the current buffer based on completion." + (insert (semantic-tag-name tag)) + (let ((tt (semantic-tag-class tag))) + (cond ((eq tt 'function) + (insert "(")) + (t nil)))) + +(declare-function semantic-analyze-possible-completions + "semantic/analyze/complete") + +(defun semantic-ia-get-completions (context point) + "Fetch the completion of CONTEXT at POINT. +Supports caching." + ;; Cache the current set of symbols so that we can get at + ;; them quickly the second time someone presses the + ;; complete button. + (let ((symbols + (if (and semantic-ia-cache + (= point (car semantic-ia-cache))) + (cdr semantic-ia-cache) + (semantic-analyze-possible-completions context)))) + ;; Set the cache + (setq semantic-ia-cache (cons point symbols)) + symbols)) + +;;;###autoload +(defun semantic-ia-complete-symbol (point) + "Complete the current symbol at POINT. +Completion options are calculated with `semantic-analyze-possible-completions'." + (interactive "d") + ;; Calculating completions is a two step process. + ;; + ;; The first analyzer the current context, which finds tags + ;; for all the stuff that may be references by the code around + ;; POINT. + ;; + ;; The second step derives completions from that context. + (let* ((a (semantic-analyze-current-context point)) + (syms (semantic-ia-get-completions a point)) + (pre (car (reverse (oref a prefix)))) + ) + ;; If PRE was actually an already completed symbol, it doesn't + ;; come in as a string, but as a tag instead. + (if (semantic-tag-p pre) + ;; We will try completions on it anyway. + (setq pre (semantic-tag-name pre))) + ;; Complete this symbol. + (if (null syms) + (progn + ;(message "No smart completions found. Trying senator-complete-symbol.") + (if (semantic-analyze-context-p a) + ;; This is a clever hack. If we were unable to find any + ;; smart completions, lets divert to how senator derives + ;; completions. + ;; + ;; This is a way of making this fcn more useful since the + ;; smart completion engine sometimes failes. + (semantic-complete-symbol))) + ;; Use try completion to seek a common substring. + (let ((tc (try-completion (or pre "") syms))) + (if (and (stringp tc) (not (string= tc (or pre "")))) + (let ((tok (semantic-find-first-tag-by-name + tc syms))) + ;; Delete what came before... + (when (and (car (oref a bounds)) (cdr (oref a bounds))) + (delete-region (car (oref a bounds)) + (cdr (oref a bounds))) + (goto-char (car (oref a bounds)))) + ;; We have some new text. Stick it in. + (if tok + (semantic-ia-insert-tag tok) + (insert tc))) + ;; We don't have new text. Show all completions. + (when (cdr (oref a bounds)) + (goto-char (cdr (oref a bounds)))) + (with-output-to-temp-buffer "*Completions*" + (display-completion-list + (mapcar semantic-ia-completion-format-tag-function syms)) + )))))) + +(defcustom semantic-ia-completion-menu-format-tag-function + 'semantic-uml-concise-prototype-nonterminal + "*Function used to convert a tag to a string during completion." + :group 'semantic + :type semantic-format-tag-custom-list) + +;;; Completions Tip +;; +;; This functions shows how to get the list of completions, +;; to place in a tooltip. It doesn't actually do any completion. + +;;;###autoload +(defun semantic-ia-complete-tip (point) + "Pop up a tooltip for completion at POINT." + (interactive "d") + (let* ((a (semantic-analyze-current-context point)) + (syms (semantic-ia-get-completions a point)) + (x (mod (- (current-column) (window-hscroll)) + (window-width))) + (y (save-excursion + (save-restriction + (widen) + (narrow-to-region (window-start) (point)) + (goto-char (point-min)) + (1+ (vertical-motion (buffer-size)))))) + (str (mapconcat #'semantic-tag-name + syms + "\n")) + ) + (cond ((fboundp 'x-show-tip) + (x-show-tip str + (selected-frame) + nil + nil + x y) + ) + (t (message str)) + ))) + +;;; Summary +;; +;; Like idle-summary-mode, this shows how to get something to +;; show a summary on. + +;;;###autoload +(defun semantic-ia-show-summary (point) + "Display a summary for the symbol under POINT." + (interactive "P") + (let* ((ctxt (semantic-analyze-current-context point)) + (pf (when ctxt + ;; The CTXT is an EIEIO object. The below + ;; method will attempt to pick the most interesting + ;; tag associated with the current context. + (semantic-analyze-interesting-tag ctxt))) + ) + (when pf + (message "%s" (semantic-format-tag-summarize pf nil t))))) + +;;; FAST Jump +;; +;; Jump to a destination based on the local context. +;; +;; This shows how to use the analyzer context, and the +;; analyer references objects to choose a good destination. + +(defun semantic-ia--fast-jump-helper (dest) + "Jump to DEST, a Semantic tag. +This helper manages the mark, buffer switching, and pulsing." + ;; We have a tag, but in C++, we usually get a prototype instead + ;; because of header files. Lets try to find the actual + ;; implementaion instead. + (when (semantic-tag-prototype-p dest) + (let* ((refs (semantic-analyze-tag-references dest)) + (impl (semantic-analyze-refs-impl refs t)) + ) + (when impl (setq dest (car impl))))) + + ;; Make sure we have a place to go... + (if (not (and (or (semantic-tag-with-position-p dest) + (semantic-tag-get-attribute dest :line)) + (semantic-tag-file-name dest))) + (error "Tag %s has no buffer information" + (semantic-format-tag-name dest))) + + ;; Once we have the tag, we can jump to it. Here + ;; are the key bits to the jump: + + ;; 1) Push the mark, so you can pop global mark back, or + ;; use semantic-mru-bookmark mode to do so. + (push-mark) + (when (fboundp 'push-tag-mark) + (push-tag-mark)) + ;; 2) Visits the tag. + (semantic-go-to-tag dest) + ;; 3) go-to-tag doesn't switch the buffer in the current window, + ;; so it is like find-file-noselect. Bring it forward. + (switch-to-buffer (current-buffer)) + ;; 4) Fancy pulsing. + (pulse-momentary-highlight-one-line (point)) + ) + +(declare-function semantic-decoration-include-visit "semantic/decorate/include") + +;;;###autoload +(defun semantic-ia-fast-jump (point) + "Jump to the tag referred to by the code at POINT. +Uses `semantic-analyze-current-context' output to identify an accurate +origin of the code at point." + (interactive "d") + (let* ((ctxt (semantic-analyze-current-context point)) + (pf (and ctxt (reverse (oref ctxt prefix)))) + ;; In the analyzer context, the PREFIX is the list of items + ;; that makes up the code context at point. Thus the c++ code + ;; this.that().theothe + ;; would make a list: + ;; ( ("this" variable ..) ("that" function ...) "theothe") + ;; Where the first two elements are the semantic tags of the prefix. + ;; + ;; PF is the reverse of this list. If the first item is a string, + ;; then it is an incomplete symbol, thus we pick the second. + ;; The second cannot be a string, as that would have been an error. + (first (car pf)) + (second (nth 1 pf)) + ) + (cond + ((semantic-tag-p first) + ;; We have a match. Just go there. + (semantic-ia--fast-jump-helper first)) + + ((semantic-tag-p second) + ;; Because FIRST failed, we should visit our second tag. + ;; HOWEVER, the tag we actually want that was only an unfound + ;; string may be related to some take in the datatype that belongs + ;; to SECOND. Thus, instead of visiting second directly, we + ;; can offer to find the type of SECOND, and go there. + (let ((secondclass (car (reverse (oref ctxt prefixtypes))))) + (cond + ((and (semantic-tag-with-position-p secondclass) + (y-or-n-p (format "Could not find `%s'. Jump to %s? " + first (semantic-tag-name secondclass)))) + (semantic-ia--fast-jump-helper secondclass) + ) + ;; If we missed out on the class of the second item, then + ;; just visit SECOND. + ((and (semantic-tag-p second) + (y-or-n-p (format "Could not find `%s'. Jump to %s? " + first (semantic-tag-name second)))) + (semantic-ia--fast-jump-helper second) + )))) + + ((semantic-tag-of-class-p (semantic-current-tag) 'include) + ;; Just borrow this cool fcn. + (require 'semantic/decorate/include) + (semantic-decoration-include-visit) + ) + + (t + (error "Could not find suitable jump point for %s" + first)) + ))) + +;;;###autoload +(defun semantic-ia-fast-mouse-jump (evt) + "Jump to the tag referred to by the point clicked on. +See `semantic-ia-fast-jump' for details on how it works. + This command is meant to be bound to a mouse event." + (interactive "e") + (semantic-ia-fast-jump + (save-excursion + (posn-set-point (event-end evt)) + (point)))) + +;;; DOC/DESCRIBE +;; +;; These routines show how to get additional information about a tag +;; for purposes of describing or showing documentation about them. +;;;###autoload +(defun semantic-ia-show-doc (point) + "Display the code-level documentation for the symbol at POINT." + (interactive "d") + (let* ((ctxt (semantic-analyze-current-context point)) + (pf (reverse (oref ctxt prefix))) + ) + ;; If PF, the prefix is non-nil, then the last element is either + ;; a string (incomplete type), or a semantic TAG. If it is a TAG + ;; then we should be able to find DOC for it. + (cond + ((stringp (car pf)) + (message "Incomplete symbol name.")) + ((semantic-tag-p (car pf)) + ;; The `semantic-documentation-for-tag' fcn is language + ;; specific. If it doesn't return what you expect, you may + ;; need to implement something for your language. + ;; + ;; The default tries to find a comment in front of the tag + ;; and then strings off comment prefixes. + (let ((doc (semantic-documentation-for-tag (car pf)))) + (with-output-to-temp-buffer "*TAG DOCUMENTATION*" + (princ "Tag: ") + (princ (semantic-format-tag-prototype (car pf))) + (princ "\n") + (princ "\n") + (princ "Snarfed Documentation: ") + (princ "\n") + (princ "\n") + (if doc + (princ doc) + (princ " Documentation unavailable.")) + ))) + (t + (message "Unknown tag."))) + )) + +;;;###autoload +(defun semantic-ia-describe-class (typename) + "Display all known parts for the datatype TYPENAME. +If the type in question is a class, all methods and other accessible +parts of the parent classes are displayed." + ;; @todo - use a fancy completing reader. + (interactive "sType Name: ") + + ;; When looking for a tag of any name there are a couple ways to do + ;; it. The simple `semanticdb-find-tag-by-...' are simple, and + ;; you need to pass it the exact name you want. + ;; + ;; The analyzer function `semantic-analyze-tag-name' will take + ;; more complex names, such as the cpp symbol foo::bar::baz, + ;; and break it up, and dive through the namespaces. + (let ((class (semantic-analyze-find-tag typename))) + + (when (not (semantic-tag-p class)) + (error "Cannot find class %s" class)) + (with-output-to-temp-buffer "*TAG DOCUMENTATION*" + ;; There are many semantic-format-tag-* fcns. + ;; The summarize routine is a fairly generic one. + (princ (semantic-format-tag-summarize class)) + (princ "\n") + (princ " Type Members:\n") + ;; The type tag contains all the parts of the type. + ;; In complex languages with inheritance, not all the + ;; parts are in the tag. This analyzer fcn will traverse + ;; the inheritance tree, and find all the pieces that + ;; are inherited. + (let ((parts (semantic-analyze-scoped-type-parts class))) + (while parts + (princ " ") + (princ (semantic-format-tag-summarize (car parts))) + (princ "\n") + (setq parts (cdr parts))) + ) + ))) + +(provide 'semantic/ia) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/ia" +;; End: + +;;; semantic/ia.el ends here diff --git a/lisp/cedet/semantic/idle.el b/lisp/cedet/semantic/idle.el new file mode 100644 index 00000000000..02170154298 --- /dev/null +++ b/lisp/cedet/semantic/idle.el @@ -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) +(require 'semantic/ctxt) +(require 'semantic/format) +(require 'semantic/tag) +(require 'timer) + +;; For the semantic-find-tags-by-name macro. +(eval-when-compile (require 'semantic/find)) + +(declare-function eldoc-message "eldoc") +(declare-function semantic-analyze-interesting-tag "semantic/analyze") +(declare-function semantic-complete-analyze-inline-idle "semantic/complete") +(declare-function semanticdb-deep-find-tags-by-name "semantic/db-find") +(declare-function semanticdb-save-all-db-idle "semantic/db") +(declare-function semanticdb-typecache-refresh-for-buffer "semantic/db-typecache") +(declare-function semantic-decorate-flush-pending-decorations + "semantic/decorate/mode") +(declare-function pulse-momentary-highlight-region "pulse") +(declare-function pulse-momentary-highlight-overlay "pulse") +(declare-function semantic-symref-hits-in-region "semantic/symref/filter") + +;;; 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 1 + "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. +;; +;;;###autoload +(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) + +(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 (and (boundp 'semantic-debug-enabled) + 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) + +;;; 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.") + +(defun semantic-idle-scheduler-add (function) + "Schedule FUNCTION to occur during idle time." + (add-to-list 'semantic-idle-scheduler-queue function)) + +(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))) + )) + ) + + (when (and (featurep 'semantic/db) (semanticdb-minor-mode-p)) + ;; 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" + (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) + )))) + )) + + +;;; REPARSING +;; +;; Reparsing is installed as semantic idle service. +;; This part ALWAYS happens, and other services occur +;; afterwards. + +(defvar semantic-before-idle-scheduler-reparse-hook nil + "Hook run before option `semantic-idle-scheduler' begins parsing. +If any hook function throws an error, this variable is reset to nil. +This hook is not protected from lexical errors.") + +(defvar semantic-after-idle-scheduler-reparse-hook nil + "Hook run after option `semantic-idle-scheduler' has parsed. +If any hook function throws an error, this variable is reset to nil. +This hook is not protected from lexical errors.") + +(semantic-varalias-obsolete 'semantic-before-idle-scheduler-reparse-hooks + 'semantic-before-idle-scheduler-reparse-hook) +(semantic-varalias-obsolete 'semantic-after-idle-scheduler-reparse-hooks + 'semantic-after-idle-scheduler-reparse-hook) + +(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-hook) + (error (setq semantic-before-idle-scheduler-reparse-hook 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-hook) + (error (setq semantic-after-idle-scheduler-reparse-hook nil)))) + ;; Return if we are lexically safe (from prog1) + lexically-safe))) + + ;; After updating the tags, handle any pending decorations for this + ;; buffer. + (require 'semantic/decorate/mode) + (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 `" (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) + + (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 + +(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 + (require 'semantic/db-find)) + (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 + (require 'semantic/analyze) + (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)))) + +;;; 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." + (require 'pulse) + (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." + (require 'pulse) + (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) + (require 'semantic/symref/filter) + (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. + (require 'semantic/complete) + (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) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/idle" +;; End: + +;;; semantic-idle.el ends here diff --git a/lisp/cedet/semantic/java.el b/lisp/cedet/semantic/java.el new file mode 100644 index 00000000000..b7f2e9a16b0 --- /dev/null +++ b/lisp/cedet/semantic/java.el @@ -0,0 +1,462 @@ +;;; semantic/java.el --- Semantic functions for Java + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> + +;; 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: +;; +;; Common function for Java parsers. + +;;; Code: +(require 'semantic) +(require 'semantic/ctxt) +(require 'semantic/doc) +(require 'semantic/format) + +(eval-when-compile + (require 'semantic/find) + (require 'semantic/dep)) + + +;;; Lexical analysis +;; +(defconst semantic-java-number-regexp + (eval-when-compile + (concat "\\(" + "\\<[0-9]+[.][0-9]+\\([eE][-+]?[0-9]+\\)?[fFdD]?\\>" + "\\|" + "\\<[0-9]+[.][eE][-+]?[0-9]+[fFdD]?\\>" + "\\|" + "\\<[0-9]+[.][fFdD]\\>" + "\\|" + "\\<[0-9]+[.]" + "\\|" + "[.][0-9]+\\([eE][-+]?[0-9]+\\)?[fFdD]?\\>" + "\\|" + "\\<[0-9]+[eE][-+]?[0-9]+[fFdD]?\\>" + "\\|" + "\\<0[xX][0-9a-fA-F]+[lL]?\\>" + "\\|" + "\\<[0-9]+[lLfFdD]?\\>" + "\\)" + )) + "Lexer regexp to match Java number terminals. +Following is the specification of Java number literals. + +DECIMAL_LITERAL: + [1-9][0-9]* + ; +HEX_LITERAL: + 0[xX][0-9a-fA-F]+ + ; +OCTAL_LITERAL: + 0[0-7]* + ; +INTEGER_LITERAL: + <DECIMAL_LITERAL>[lL]? + | <HEX_LITERAL>[lL]? + | <OCTAL_LITERAL>[lL]? + ; +EXPONENT: + [eE][+-]?[09]+ + ; +FLOATING_POINT_LITERAL: + [0-9]+[.][0-9]*<EXPONENT>?[fFdD]? + | [.][0-9]+<EXPONENT>?[fFdD]? + | [0-9]+<EXPONENT>[fFdD]? + | [0-9]+<EXPONENT>?[fFdD] + ;") + +;;; Parsing +;; +(defsubst semantic-java-dim (id) + "Split ID string into a pair (NAME . DIM). +NAME is ID without trailing brackets: \"[]\". +DIM is the dimension of NAME deduced from the number of trailing +brackets, or 0 if there is no trailing brackets." + (let ((dim (string-match "\\(\\[]\\)+\\'" id))) + (if dim + (cons (substring id 0 dim) + (/ (length (match-string 0 id)) 2)) + (cons id 0)))) + +(defsubst semantic-java-type (tag) + "Return the type of TAG, taking care of array notation." + (let ((type (semantic-tag-type tag)) + (dim (semantic-tag-get-attribute tag :dereference))) + (when dim + (while (> dim 0) + (setq type (concat type "[]") + dim (1- dim)))) + type)) + +(defun semantic-java-expand-tag (tag) + "Expand compound declarations found in TAG into separate tags. +TAG contains compound declarations when its class is `variable', and +its name is a list of elements (NAME START . END), where NAME is a +compound variable name, and START/END are the bounds of the +corresponding compound declaration." + (let* ((class (semantic-tag-class tag)) + (elts (semantic-tag-name tag)) + dim type dim0 elt clone start end xpand) + (cond + ((and (eq class 'function) + (> (cdr (setq dim (semantic-java-dim elts))) 0)) + (setq clone (semantic-tag-clone tag (car dim)) + xpand (cons clone xpand)) + (semantic-tag-put-attribute clone :dereference (cdr dim))) + ((eq class 'variable) + (or (consp elts) (setq elts (list (list elts)))) + (setq dim (semantic-java-dim (semantic-tag-get-attribute tag :type)) + type (car dim) + dim0 (cdr dim)) + (while elts + ;; For each compound element, clone the initial tag with the + ;; name and bounds of the compound variable declaration. + (setq elt (car elts) + elts (cdr elts) + start (if elts (cadr elt) (semantic-tag-start tag)) + end (if xpand (cddr elt) (semantic-tag-end tag)) + dim (semantic-java-dim (car elt)) + clone (semantic-tag-clone tag (car dim)) + xpand (cons clone xpand)) + (semantic-tag-put-attribute clone :type type) + (semantic-tag-put-attribute clone :dereference (+ dim0 (cdr dim))) + (semantic-tag-set-bounds clone start end))) + ) + xpand)) + +;;; Environment +;; +(defcustom-mode-local-semantic-dependency-system-include-path + java-mode semantic-java-dependency-system-include-path + ;; @todo - Use JDEE to get at the include path, or something else? + nil + "The system include path used by Java langauge.") + +;; Local context +;; +(define-mode-local-override semantic-ctxt-scoped-types + java-mode (&optional point) + "Return a list of type names currently in scope at POINT." + (mapcar 'semantic-tag-name + (semantic-find-tags-by-class + 'type (semantic-find-tag-by-overlay point)))) + +;; Prototype handler +;; +(defun semantic-java-prototype-function (tag &optional parent color) + "Return a function (method) prototype for TAG. +Optional argument PARENT is a parent (containing) item. +Optional argument COLOR indicates that color should be mixed in. +See also `semantic-format-tag-prototype'." + (let ((name (semantic-tag-name tag)) + (type (semantic-java-type tag)) + (tmpl (semantic-tag-get-attribute tag :template-specifier)) + (args (semantic-tag-function-arguments tag)) + (argp "") + arg argt) + (while args + (setq arg (car args) + args (cdr args)) + (if (semantic-tag-p arg) + (setq argt (if color + (semantic--format-colorize-text + (semantic-java-type arg) 'type) + (semantic-java-type arg)) + argp (concat argp argt (if args "," ""))))) + (when color + (when type + (setq type (semantic--format-colorize-text type 'type))) + (setq name (semantic--format-colorize-text name 'function))) + (concat (or tmpl "") (if tmpl " " "") + (or type "") (if type " " "") + name "(" argp ")"))) + +(defun semantic-java-prototype-variable (tag &optional parent color) + "Return a variable (field) prototype for TAG. +Optional argument PARENT is a parent (containing) item. +Optional argument COLOR indicates that color should be mixed in. +See also `semantic-format-tag-prototype'." + (let ((name (semantic-tag-name tag)) + (type (semantic-java-type tag))) + (concat (if color + (semantic--format-colorize-text type 'type) + type) + " " + (if color + (semantic--format-colorize-text name 'variable) + name)))) + +(defun semantic-java-prototype-type (tag &optional parent color) + "Return a type (class/interface) prototype for TAG. +Optional argument PARENT is a parent (containing) item. +Optional argument COLOR indicates that color should be mixed in. +See also `semantic-format-tag-prototype'." + (let ((name (semantic-tag-name tag)) + (type (semantic-tag-type tag)) + (tmpl (semantic-tag-get-attribute tag :template-specifier))) + (concat type " " + (if color + (semantic--format-colorize-text name 'type) + name) + (or tmpl "")))) + +(define-mode-local-override semantic-format-tag-prototype + java-mode (tag &optional parent color) + "Return a prototype for TOKEN. +Optional argument PARENT is a parent (containing) item. +Optional argument COLOR indicates that color should be mixed in." + (let ((f (intern-soft (format "semantic-java-prototype-%s" + (semantic-tag-class tag))))) + (funcall (if (fboundp f) + f + 'semantic-format-tag-prototype-default) + tag parent color))) + +(semantic-alias-obsolete 'semantic-java-prototype-nonterminal + 'semantic-format-tag-prototype-java-mode) + +;; Include Tag Name +;; + +;; Thanks Bruce Stephens +(define-mode-local-override semantic-tag-include-filename java-mode (tag) + "Return a suitable path for (some) Java imports" + (let ((name (semantic-tag-name tag))) + (concat (mapconcat 'identity (split-string name "\\.") "/") ".java"))) + + +;; Documentation handler +;; +(defsubst semantic-java-skip-spaces-backward () + "Move point backward, skipping Java whitespaces." + (skip-chars-backward " \n\r\t")) + +(defsubst semantic-java-skip-spaces-forward () + "Move point forward, skipping Java whitespaces." + (skip-chars-forward " \n\r\t")) + +(define-mode-local-override semantic-documentation-for-tag + java-mode (&optional tag nosnarf) + "Find documentation from TAG and return it as a clean string. +Java have documentation set in a comment preceeding TAG's definition. +Attempt to strip out comment syntactic sugar, unless optional argument +NOSNARF is non-nil. +If NOSNARF is 'lex, then return the semantic lex token." + (when (or tag (setq tag (semantic-current-tag))) + (with-current-buffer (semantic-tag-buffer tag) + (save-excursion + ;; Move the point at token start + (goto-char (semantic-tag-start tag)) + (semantic-java-skip-spaces-forward) + ;; If the point already at "/**" (this occurs after a doc fix) + (if (looking-at "/\\*\\*") + nil + ;; Skip previous spaces + (semantic-java-skip-spaces-backward) + ;; Ensure point is after "*/" (javadoc block comment end) + (condition-case nil + (backward-char 2) + (error nil)) + (when (looking-at "\\*/") + ;; Move the point backward across the comment + (forward-char 2) ; return just after "*/" + (forward-comment -1) ; to skip the entire block + )) + ;; Verify the point is at "/**" (javadoc block comment start) + (if (looking-at "/\\*\\*") + (let ((p (point)) + (c (semantic-doc-snarf-comment-for-tag 'lex))) + (when c + ;; Verify that the token just following the doc + ;; comment is the current one! + (goto-char (semantic-lex-token-end c)) + (semantic-java-skip-spaces-forward) + (when (eq tag (semantic-current-tag)) + (goto-char p) + (semantic-doc-snarf-comment-for-tag nosnarf))))) + )))) + +;;; Javadoc facilities +;; + +;; Javadoc elements +;; +(defvar semantic-java-doc-line-tags nil + "Valid javadoc line tags. +Ordered following Sun's Tag Convention at +<http://java.sun.com/products/jdk/javadoc/writingdoccomments/index.html>") + +(defvar semantic-java-doc-with-name-tags nil + "Javadoc tags which have a name.") + +(defvar semantic-java-doc-with-ref-tags nil + "Javadoc tags which have a reference.") + +;; Optional javadoc tags by classes of semantic tag +;; +(defvar semantic-java-doc-extra-type-tags nil + "Optional tags used in class/interface documentation. +Ordered following Sun's Tag Convention.") + +(defvar semantic-java-doc-extra-function-tags nil + "Optional tags used in method/constructor documentation. +Ordered following Sun's Tag Convention.") + +(defvar semantic-java-doc-extra-variable-tags nil + "Optional tags used in field documentation. +Ordered following Sun's Tag Convention.") + +;; All javadoc tags by classes of semantic tag +;; +(defvar semantic-java-doc-type-tags nil + "Tags allowed in class/interface documentation. +Ordered following Sun's Tag Convention.") + +(defvar semantic-java-doc-function-tags nil + "Tags allowed in method/constructor documentation. +Ordered following Sun's Tag Convention.") + +(defvar semantic-java-doc-variable-tags nil + "Tags allowed in field documentation. +Ordered following Sun's Tag Convention.") + +;; Access to Javadoc elements +;; +(defmacro semantic-java-doc-tag (name) + "Return doc tag from NAME. +That is @NAME." + `(concat "@" ,name)) + +(defsubst semantic-java-doc-tag-name (tag) + "Return name of the doc TAG symbol. +That is TAG `symbol-name' without the leading '@'." + (substring (symbol-name tag) 1)) + +(defun semantic-java-doc-keyword-before-p (k1 k2) + "Return non-nil if javadoc keyword K1 is before K2." + (let* ((t1 (semantic-java-doc-tag k1)) + (t2 (semantic-java-doc-tag k2)) + (seq1 (and (semantic-lex-keyword-p t1) + (plist-get (semantic-lex-keyword-get t1 'javadoc) + 'seq))) + (seq2 (and (semantic-lex-keyword-p t2) + (plist-get (semantic-lex-keyword-get t2 'javadoc) + 'seq)))) + (if (and (numberp seq1) (numberp seq2)) + (<= seq1 seq2) + ;; Unknown tags (probably custom ones) are always after official + ;; ones and are not themselves ordered. + (or (numberp seq1) + (and (not seq1) (not seq2)))))) + +(defun semantic-java-doc-keywords-map (fun &optional property) + "Run function FUN for each javadoc keyword. +Return the list of FUN results. If optional PROPERTY is non nil only +call FUN for javadoc keyword which have a value for PROPERTY. FUN +receives two arguments: the javadoc keyword and its associated +'javadoc property list. It can return any value. Nil values are +removed from the result list." + (delq nil + (mapcar + #'(lambda (k) + (let* ((tag (semantic-java-doc-tag k)) + (plist (semantic-lex-keyword-get tag 'javadoc))) + (if (or (not property) (plist-get plist property)) + (funcall fun k plist)))) + semantic-java-doc-line-tags))) + + +;;; Mode setup +;; + +(defun semantic-java-doc-setup () + "Lazy initialization of javadoc elements." + (or semantic-java-doc-line-tags + (setq semantic-java-doc-line-tags + (sort (mapcar #'semantic-java-doc-tag-name + (semantic-lex-keywords 'javadoc)) + #'semantic-java-doc-keyword-before-p))) + + (or semantic-java-doc-with-name-tags + (setq semantic-java-doc-with-name-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + k) + 'with-name))) + + (or semantic-java-doc-with-ref-tags + (setq semantic-java-doc-with-ref-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + k) + 'with-ref))) + + (or semantic-java-doc-extra-type-tags + (setq semantic-java-doc-extra-type-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + (if (memq 'type (plist-get p 'usage)) + k)) + 'opt))) + + (or semantic-java-doc-extra-function-tags + (setq semantic-java-doc-extra-function-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + (if (memq 'function (plist-get p 'usage)) + k)) + 'opt))) + + (or semantic-java-doc-extra-variable-tags + (setq semantic-java-doc-extra-variable-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + (if (memq 'variable (plist-get p 'usage)) + k)) + 'opt))) + + (or semantic-java-doc-type-tags + (setq semantic-java-doc-type-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + (if (memq 'type (plist-get p 'usage)) + k))))) + + (or semantic-java-doc-function-tags + (setq semantic-java-doc-function-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + (if (memq 'function (plist-get p 'usage)) + k))))) + + (or semantic-java-doc-variable-tags + (setq semantic-java-doc-variable-tags + (semantic-java-doc-keywords-map + #'(lambda (k p) + (if (memq 'variable (plist-get p 'usage)) + k))))) + + ) + +(provide 'semantic/java) + +;;; semantic/java.el ends here diff --git a/lisp/cedet/semantic/lex-spp.el b/lisp/cedet/semantic/lex-spp.el new file mode 100644 index 00000000000..edd377f2ab4 --- /dev/null +++ b/lisp/cedet/semantic/lex-spp.el @@ -0,0 +1,1198 @@ +;;; lex-spp.el --- Semantic Lexical Pre-processor + +;;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; The Semantic Preprocessor works with semantic-lex to provide a phase +;; during lexical analysis to do the work of a pre-processor. +;; +;; A pre-processor identifies lexical syntax mixed in with another language +;; and replaces some keyword tokens with streams of alternate tokens. +;; +;; If you use SPP in your language, be sure to specify this in your +;; semantic language setup function: +;; +;; (add-hook 'semantic-lex-reset-hooks 'semantic-lex-spp-reset-hook nil t) +;; +;; +;; Special Lexical Tokens: +;; +;; There are several special lexical tokens that are used by the +;; Semantic PreProcessor lexer. They are: +;; +;; Declarations: +;; spp-macro-def - A definition of a lexical macro. +;; spp-macro-undef - A removal of a definition of a lexical macro. +;; spp-system-include - A system level include file +;; spp-include - An include file +;; spp-concat - A lexical token representing textual concatenation +;; of symbol parts. +;; +;; Operational tokens: +;; spp-arg-list - Represents an argument list to a macro. +;; spp-symbol-merge - A request for multiple symbols to be textually merged. +;; +;;; TODO: +;; +;; Use `semantic-push-parser-warning' for situations where there are likely +;; macros that are undefined unexpectedly, or other problem. +;; +;; TODO: +;; +;; Try to handle the case of: +;; +;; #define NN namespace nn { +;; #define NN_END } +;; +;; NN +;; int mydecl() {} +;; NN_END +;; + +(require 'semantic) +(require 'semantic/lex) + +;;; Code: +(defvar semantic-lex-spp-macro-symbol-obarray nil + "Table of macro keywords used by the Semantic Preprocessor. +These symbols will be used in addition to those in +`semantic-lex-spp-dynamic-macro-symbol-obarray'.") +(make-variable-buffer-local 'semantic-lex-spp-macro-symbol-obarray) + +(defvar semantic-lex-spp-project-macro-symbol-obarray nil + "Table of macro keywords for this project. +These symbols will be used in addition to those in +`semantic-lex-spp-dynamic-macro-symbol-obarray'.") +(make-variable-buffer-local 'semantic-lex-spp-project-macro-symbol-obarray) + +(defvar semantic-lex-spp-dynamic-macro-symbol-obarray nil + "Table of macro keywords used during lexical analysis. +Macros are lexical symbols which are replaced by other lexical +tokens during lexical analysis. During analysis symbols can be +added and removed from this symbol table.") +(make-variable-buffer-local 'semantic-lex-spp-dynamic-macro-symbol-obarray) + +(defvar semantic-lex-spp-dynamic-macro-symbol-obarray-stack nil + "A stack of obarrays for temporarilly scoped macro values.") +(make-variable-buffer-local 'semantic-lex-spp-dynamic-macro-symbol-obarray-stack) + +(defvar semantic-lex-spp-expanded-macro-stack nil + "The stack of lexical SPP macros we have expanded.") +;; The above is not buffer local. Some macro expansions need to be +;; dumped into a secondary buffer for re-lexing. + +;;; NON-RECURSIVE MACRO STACK +;; C Pre-processor does not allow recursive macros. Here are some utils +;; for managing the symbol stack of where we've been. + +(defmacro semantic-lex-with-macro-used (name &rest body) + "With the macro NAME currently being expanded, execute BODY. +Pushes NAME into the macro stack. The above stack is checked +by `semantic-lex-spp-symbol' to not return true for any symbol +currently being expanded." + `(unwind-protect + (progn + (push ,name semantic-lex-spp-expanded-macro-stack) + ,@body) + (pop semantic-lex-spp-expanded-macro-stack))) +(put 'semantic-lex-with-macro-used 'lisp-indent-function 1) + +(add-hook + 'edebug-setup-hook + #'(lambda () + + (def-edebug-spec semantic-lex-with-macro-used + (symbolp def-body) + ) + + )) + +;;; MACRO TABLE UTILS +;; +;; The dynamic macro table is a buffer local variable that is modified +;; during the analysis. OBARRAYs are used, so the language must +;; have symbols that are compatible with Emacs Lisp symbols. +;; +(defsubst semantic-lex-spp-symbol (name) + "Return spp symbol with NAME or nil if not found. +The searcy priority is: + 1. DYNAMIC symbols + 2. PROJECT specified symbols. + 3. SYSTEM specified symbols." + (and + ;; Only strings... + (stringp name) + ;; Make sure we don't recurse. + (not (member name semantic-lex-spp-expanded-macro-stack)) + ;; Do the check of the various tables. + (or + ;; DYNAMIC + (and (arrayp semantic-lex-spp-dynamic-macro-symbol-obarray) + (intern-soft name semantic-lex-spp-dynamic-macro-symbol-obarray)) + ;; PROJECT + (and (arrayp semantic-lex-spp-project-macro-symbol-obarray) + (intern-soft name semantic-lex-spp-project-macro-symbol-obarray)) + ;; SYSTEM + (and (arrayp semantic-lex-spp-macro-symbol-obarray) + (intern-soft name semantic-lex-spp-macro-symbol-obarray)) + ;; ... + ))) + +(defsubst semantic-lex-spp-symbol-p (name) + "Return non-nil if a keyword with NAME exists in any keyword table." + (if (semantic-lex-spp-symbol name) + t)) + +(defsubst semantic-lex-spp-dynamic-map () + "Return the dynamic macro map for the current buffer." + (or semantic-lex-spp-dynamic-macro-symbol-obarray + (setq semantic-lex-spp-dynamic-macro-symbol-obarray + (make-vector 13 0)))) + +(defsubst semantic-lex-spp-dynamic-map-stack () + "Return the dynamic macro map for the current buffer." + (or semantic-lex-spp-dynamic-macro-symbol-obarray-stack + (setq semantic-lex-spp-dynamic-macro-symbol-obarray-stack + (make-vector 13 0)))) + +(defun semantic-lex-spp-symbol-set (name value &optional obarray-in) + "Set value of spp symbol with NAME to VALUE and return VALUE. +If optional OBARRAY-IN is non-nil, then use that obarray instead of +the dynamic map." + (if (and (stringp value) (string= value "")) (setq value nil)) + (set (intern name (or obarray-in + (semantic-lex-spp-dynamic-map))) + value)) + +(defsubst semantic-lex-spp-symbol-remove (name &optional obarray) + "Remove the spp symbol with NAME. +If optional OBARRAY is non-nil, then use that obarray instead of +the dynamic map." + (unintern name (or obarray + (semantic-lex-spp-dynamic-map)))) + +(defun semantic-lex-spp-symbol-push (name value) + "Push macro NAME with VALUE into the map. +Reverse with `semantic-lex-spp-symbol-pop'." + (let* ((map (semantic-lex-spp-dynamic-map)) + (stack (semantic-lex-spp-dynamic-map-stack)) + (mapsym (intern name map)) + (stacksym (intern name stack)) + (mapvalue (when (boundp mapsym) (symbol-value mapsym))) + ) + (when (boundp mapsym) + ;; Make sure there is a stack + (if (not (boundp stacksym)) (set stacksym nil)) + ;; If there is a value to push, then push it. + (set stacksym (cons mapvalue (symbol-value stacksym))) + ) + ;; Set our new value here. + (set mapsym value) + )) + +(defun semantic-lex-spp-symbol-pop (name) + "Pop macro NAME from the stackmap into the orig map. +Reverse with `semantic-lex-spp-symbol-pop'." + (let* ((map (semantic-lex-spp-dynamic-map)) + (stack (semantic-lex-spp-dynamic-map-stack)) + (mapsym (intern name map)) + (stacksym (intern name stack)) + (oldvalue nil) + ) + (if (or (not (boundp stacksym) ) + (= (length (symbol-value stacksym)) 0)) + ;; Nothing to pop, remove it. + (unintern name map) + ;; If there is a value to pop, then add it to the map. + (set mapsym (car (symbol-value stacksym))) + (set stacksym (cdr (symbol-value stacksym))) + ))) + +(defsubst semantic-lex-spp-symbol-stream (name) + "Return replacement stream of macro with NAME." + (let ((spp (semantic-lex-spp-symbol name))) + (if spp + (symbol-value spp)))) + +(defun semantic-lex-make-spp-table (specs) + "Convert spp macro list SPECS into an obarray and return it. +SPECS must be a list of (NAME . REPLACEMENT) elements, where: + +NAME is the name of the spp macro symbol to define. +REPLACEMENT a string that would be substituted in for NAME." + + ;; Create the symbol hash table + (let ((semantic-lex-spp-macro-symbol-obarray (make-vector 13 0)) + spec) + ;; fill it with stuff + (while specs + (setq spec (car specs) + specs (cdr specs)) + (semantic-lex-spp-symbol-set + (car spec) + (cdr spec) + semantic-lex-spp-macro-symbol-obarray)) + semantic-lex-spp-macro-symbol-obarray)) + +(defun semantic-lex-spp-save-table () + "Return a list of spp macros and values. +The return list is meant to be saved in a semanticdb table." + (let (macros) + (when (arrayp semantic-lex-spp-dynamic-macro-symbol-obarray) + (mapatoms + #'(lambda (symbol) + (setq macros (cons (cons (symbol-name symbol) + (symbol-value symbol)) + macros))) + semantic-lex-spp-dynamic-macro-symbol-obarray)) + macros)) + +(defun semantic-lex-spp-macros () + "Return a list of spp macros as Lisp symbols. +The value of each symbol is the replacement stream." + (let (macros) + (when (arrayp semantic-lex-spp-macro-symbol-obarray) + (mapatoms + #'(lambda (symbol) + (setq macros (cons symbol macros))) + semantic-lex-spp-macro-symbol-obarray)) + (when (arrayp semantic-lex-spp-project-macro-symbol-obarray) + (mapatoms + #'(lambda (symbol) + (setq macros (cons symbol macros))) + semantic-lex-spp-project-macro-symbol-obarray)) + (when (arrayp semantic-lex-spp-dynamic-macro-symbol-obarray) + (mapatoms + #'(lambda (symbol) + (setq macros (cons symbol macros))) + semantic-lex-spp-dynamic-macro-symbol-obarray)) + macros)) + +(defun semantic-lex-spp-set-dynamic-table (new-entries) + "Set the dynamic symbol table to NEW-ENTRIES. +For use with semanticdb restoration of state." + (dolist (e new-entries) + ;; Default obarray for below is the dynamic map. + (semantic-lex-spp-symbol-set (car e) (cdr e)))) + +(defun semantic-lex-spp-reset-hook (start end) + "Reset anything needed by SPP for parsing. +In this case, reset the dynamic macro symbol table if +START is (point-min). +END is not used." + (when (= start (point-min)) + (setq semantic-lex-spp-dynamic-macro-symbol-obarray nil + semantic-lex-spp-dynamic-macro-symbol-obarray-stack nil + ;; This shouldn't not be nil, but reset just in case. + semantic-lex-spp-expanded-macro-stack nil) + )) + +;;; MACRO EXPANSION: Simple cases +;; +;; If a user fills in the table with simple strings, we can +;; support that by converting them into tokens with the +;; various analyzers that are available. + +(defun semantic-lex-spp-extract-regex-and-compare (analyzer value) + "Extract a regexp from an ANALYZER and use to match VALUE. +Return non-nil if it matches" + (let* ((condition (car analyzer)) + (regex (cond ((eq (car condition) 'looking-at) + (nth 1 condition)) + (t + nil)))) + (when regex + (string-match regex value)) + )) + +(defun semantic-lex-spp-simple-macro-to-macro-stream (val beg end argvalues) + "Convert lexical macro contents VAL into a macro expansion stream. +These are for simple macro expansions that a user may have typed in directly. +As such, we need to analyze the input text, to figure out what kind of real +lexical token we should be inserting in its place. + +Argument VAL is the value of some macro to be converted into a stream. +BEG and END are the token bounds of the macro to be expanded +that will somehow gain a much longer token stream. +ARGVALUES are values for any arg list, or nil." + (cond + ;; We perform a replacement. Technically, this should + ;; be a full lexical step over the "val" string, but take + ;; a guess that its just a keyword or existing symbol. + ;; + ;; Probably a really bad idea. See how it goes. + ((semantic-lex-spp-extract-regex-and-compare + semantic-lex-symbol-or-keyword val) + (semantic-lex-push-token + (semantic-lex-token (or (semantic-lex-keyword-p val) 'symbol) + beg end + val))) + + ;; Ok, the rest of these are various types of syntax. + ;; Conveniences for users that type in their symbol table. + ((semantic-lex-spp-extract-regex-and-compare + semantic-lex-punctuation val) + (semantic-lex-token 'punctuation beg end val)) + ((semantic-lex-spp-extract-regex-and-compare + semantic-lex-number val) + (semantic-lex-token 'number beg end val)) + ((semantic-lex-spp-extract-regex-and-compare + semantic-lex-paren-or-list val) + (semantic-lex-token 'semantic-list beg end val)) + ((semantic-lex-spp-extract-regex-and-compare + semantic-lex-string val) + (semantic-lex-token 'string beg end val)) + (t nil) + )) + +;;; MACRO EXPANSION : Lexical token replacement +;; +;; When substituting in a macro from a token stream of formatted +;; semantic lex tokens, things can be much more complicated. +;; +;; Some macros have arguments that get set into the dynamic macro +;; table during replacement. +;; +;; In general, the macro tokens are substituted into the regular +;; token stream, but placed under the characters of the original +;; macro symbol. +;; +;; Argument lists are saved as a lexical token at the beginning +;; of a replacement value. + +(defun semantic-lex-spp-one-token-to-txt (tok &optional blocktok) + "Convert the token TOK into a string. +If TOK is made of multiple tokens, convert those to text. This +conversion is needed if a macro has a merge symbol in it that +combines the text of two previously distinct symbols. For +exampe, in c: + +#define (a,b) a ## b; + +If optional string BLOCKTOK matches the expanded value, then do not +continue processing recursively." + (let ((txt (semantic-lex-token-text tok)) + (sym nil) + ) + (cond + ;; Recursion prevention + ((and (stringp blocktok) (string= txt blocktok)) + blocktok) + ;; A complex symbol + ((and (eq (car tok) 'symbol) + (setq sym (semantic-lex-spp-symbol txt)) + (not (semantic-lex-spp-macro-with-args (symbol-value sym))) + ) + ;; Now that we have a symbol, + (let ((val (symbol-value sym))) + (cond + ;; This is another lexical token. + ((and (consp val) + (symbolp (car val))) + (semantic-lex-spp-one-token-to-txt val txt)) + ;; This is a list of tokens. + ((and (consp val) + (consp (car val)) + (symbolp (car (car val)))) + (mapconcat (lambda (subtok) + (semantic-lex-spp-one-token-to-txt subtok)) + val + "")) + ;; If val is nil, that's probably wrong. + ;; Found a system header case where this was true. + ((null val) "") + ;; Debug wierd stuff. + (t (debug))) + )) + ((stringp txt) + txt) + (t nil)) + )) + +(defun semantic-lex-spp-macro-with-args (val) + "If the macro value VAL has an argument list, return the arglist." + (when (and val (consp val) (consp (car val)) + (eq 'spp-arg-list (car (car val)))) + (car (cdr (car val))))) + +(defun semantic-lex-spp-token-macro-to-macro-stream (val beg end argvalues) + "Convert lexical macro contents VAL into a macro expansion stream. +Argument VAL is the value of some macro to be converted into a stream. +BEG and END are the token bounds of the macro to be expanded +that will somehow gain a much longer token stream. +ARGVALUES are values for any arg list, or nil. +See comments in code for information about how token streams are processed +and what valid VAL values are." + + ;; A typical VAL value might be either a stream of tokens. + ;; Tokens saved into a macro stream always includes the text from the + ;; buffer, since the locations specified probably don't represent + ;; that text anymore, or even the same buffer. + ;; + ;; CASE 1: Simple token stream + ;; + ;; #define SUPER mysuper:: + ;; ==> + ;;((symbol "mysuper" 480 . 487) + ;; (punctuation ":" 487 . 488) + ;; (punctuation ":" 488 . 489)) + ;; + ;; CASE 2: Token stream with argument list + ;; + ;; #define INT_FCN(name) int name (int in) + ;; ==> + ;; ((spp-arg-list ("name") 558 . 564) + ;; (INT "int" 565 . 568) + ;; (symbol "name" 569 . 573) + ;; (semantic-list "(int in)" 574 . 582)) + ;; + ;; In the second case, a macro with an argument list as the a rgs as the + ;; first entry. + ;; + ;; CASE 3: Symbol text merge + ;; + ;; #define TMP(a) foo_ ## a + ;; ==> + ;; ((spp-arg-list ("a") 20 . 23) + ;; (spp-symbol-merge ((symbol "foo_" 24 . 28) (symbol "a" 32 . 33)) + ;; 24 . 33)) + ;; + ;; Usually in conjunction with a macro with an argument, merging symbol + ;; parts is a way of fabricating new symbols from pieces inside the macro. + ;; These macros use `spp-symbol-merge' tokens whose TEXT part is another + ;; token stream. This sub-stream ought to consist of only 2 SYMBOL pieces, + ;; though I suppose keywords might be ok. The end result of this example + ;; merge symbol would be (symbol "foo_A" 24 . 33) where A is the symbol + ;; passed in from the arg list "a". + ;; + ;; CASE 4: Nested token streams + ;; + ;; #define FOO(f) f + ;; #define BLA bla FOO(foo) + ;; ==> + ;; ((INT "int" 82 . 85) + ;; (symbol "FOO" 86 . 89) + ;; (semantic-list "(foo)" 89 . 94)) + ;; + ;; Nested token FOO shows up in the table of macros, and gets replace + ;; inline. This is the same as case 2. + + (let ((arglist (semantic-lex-spp-macro-with-args val)) + (argalist nil) + (val-tmp nil) + (v nil) + ) + ;; CASE 2: Dealing with the arg list. + (when arglist + ;; Skip the arg list. + (setq val (cdr val)) + + ;; Push args into the replacement list. + (let ((AV argvalues)) + (dolist (A arglist) + (let* ((argval (car AV))) + + (semantic-lex-spp-symbol-push A argval) + (setq argalist (cons (cons A argval) argalist)) + (setq AV (cdr AV))))) + ) + + ;; Set val-tmp after stripping arguments. + (setq val-tmp val) + + ;; CASE 1: Push everything else onto the list. + ;; Once the arg list is stripped off, CASE 2 is the same + ;; as CASE 1. + (while val-tmp + (setq v (car val-tmp)) + (setq val-tmp (cdr val-tmp)) + + (let* (;; The text of the current lexical token. + (txt (car (cdr v))) + ;; Try to convert txt into a macro declaration. If it is + ;; not a macro, use nil. + (txt-macro-or-nil (semantic-lex-spp-symbol txt)) + ;; If our current token is a macro, then pull off the argument + ;; list. + (macro-and-args + (when txt-macro-or-nil + (semantic-lex-spp-macro-with-args (symbol-value txt-macro-or-nil))) + ) + ;; We need to peek at the next token when testing for + ;; used macros with arg lists. + (next-tok-class (semantic-lex-token-class (car val-tmp))) + ) + + (cond + ;; CASE 3: Merge symbols together. + ((eq (semantic-lex-token-class v) 'spp-symbol-merge) + ;; We need to merge the tokens in the 'text segement together, + ;; and produce a single symbol from it. + (let ((newsym + (mapconcat (lambda (tok) + (semantic-lex-spp-one-token-to-txt tok)) + txt + ""))) + (semantic-lex-push-token + (semantic-lex-token 'symbol beg end newsym)) + )) + + ;; CASE 2: Argument replacement. If a discovered symbol is in + ;; the active list of arguments, then we need to substitute + ;; in the new value. + ((and (eq (semantic-lex-token-class v) 'symbol) txt-macro-or-nil + (or (and macro-and-args (eq next-tok-class 'semantic-list)) + (not macro-and-args)) + ) + (let ((AV nil)) + (when macro-and-args + (setq AV + (semantic-lex-spp-stream-for-arglist (car val-tmp))) + ;; We used up these args. Pull from the stream. + (setq val-tmp (cdr val-tmp)) + ) + + (semantic-lex-with-macro-used txt + ;; Don't recurse directly into this same fcn, because it is + ;; convenient to have plain string replacements too. + (semantic-lex-spp-macro-to-macro-stream + (symbol-value txt-macro-or-nil) + beg end AV)) + )) + + ;; This is a HACK for the C parser. The 'macros text + ;; property is some storage so that the parser can do + ;; some C specific text manipulations. + ((eq (semantic-lex-token-class v) 'semantic-list) + ;; Push our arg list onto the semantic list. + (when argalist + (setq txt (concat txt)) ; Copy the text. + (put-text-property 0 1 'macros argalist txt)) + (semantic-lex-push-token + (semantic-lex-token (semantic-lex-token-class v) beg end txt)) + ) + + ;; CASE 1: Just another token in the stream. + (t + ;; Nothing new. + (semantic-lex-push-token + (semantic-lex-token (semantic-lex-token-class v) beg end txt)) + ) + ))) + + ;; CASE 2: The arg list we pushed onto the symbol table + ;; must now be removed. + (dolist (A arglist) + (semantic-lex-spp-symbol-pop A)) + )) + +;;; Macro Merging +;; +;; Used when token streams from different macros include eachother. +;; Merged macro streams perform in place replacements. + +(defun semantic-lex-spp-merge-streams (raw-stream) + "Merge elements from the RAW-STREAM together. +Handle spp-concat symbol concatenation. +Handle Nested macro replacements. +Return the cooked stream." + (let ((cooked-stream nil)) + ;; Merge the stream + (while raw-stream + (cond ((eq (semantic-lex-token-class (car raw-stream)) 'spp-concat) + ;; handle hashhash, by skipping it. + (setq raw-stream (cdr raw-stream)) + ;; Now merge the symbols. + (let ((prev-tok (car cooked-stream)) + (next-tok (car raw-stream))) + (setq cooked-stream (cdr cooked-stream)) + (push (semantic-lex-token + 'spp-symbol-merge + (semantic-lex-token-start prev-tok) + (semantic-lex-token-end next-tok) + (list prev-tok next-tok)) + cooked-stream) + )) + (t + (push (car raw-stream) cooked-stream)) + ) + (setq raw-stream (cdr raw-stream)) + ) + + (nreverse cooked-stream)) + ) + +;;; MACRO EXPANSION +;; +;; There are two types of expansion. +;; +;; 1. Expansion using a value made up of lexical tokens. +;; 2. User input replacement from a plain string. + +(defun semantic-lex-spp-macro-to-macro-stream (val beg end argvalues) + "Convert lexical macro contents VAL into a macro expansion stream. +Argument VAL is the value of some macro to be converted into a stream. +BEG and END are the token bounds of the macro to be expanded +that will somehow gain a much longer token stream. +ARGVALUES are values for any arg list, or nil." + (cond + ;; If val is nil, then just skip it. + ((null val) t) + ;; If it is a token, then return that token rebuilt. + ((and (consp val) (car val) (symbolp (car val))) + (semantic-lex-push-token + (semantic-lex-token (car val) beg end (semantic-lex-token-text val)))) + ;; Test for a token list. + ((and (consp val) (consp (car val)) (car (car val)) + (symbolp (car (car val)))) + (semantic-lex-spp-token-macro-to-macro-stream val beg end argvalues)) + ;; Test for miscellaneous strings. + ((stringp val) + (semantic-lex-spp-simple-macro-to-macro-stream val beg end argvalues)) + )) + +;;; -------------------------------------------------------- +;;; +;;; ANALYZERS: +;;; + +;;; Symbol Is Macro +;; +;; An analyser that will push tokens from a macro in place +;; of the macro symbol. +;; +(defun semantic-lex-spp-anlyzer-do-replace (sym val beg end) + "Do the lexical replacement for SYM with VAL. +Argument BEG and END specify the bounds of SYM in the buffer." + (if (not val) + (setq semantic-lex-end-point end) + (let ((arg-in nil) + (arg-parsed nil) + (arg-split nil) + ) + + ;; Check for arguments. + (setq arg-in (semantic-lex-spp-macro-with-args val)) + + (when arg-in + (save-excursion + (goto-char end) + (setq arg-parsed + (semantic-lex-spp-one-token-and-move-for-macro + (point-at-eol))) + (setq end (semantic-lex-token-end arg-parsed)) + + (when (and (listp arg-parsed) (eq (car arg-parsed) 'semantic-list)) + (setq arg-split + ;; Use lex to split up the contents of the argument list. + (semantic-lex-spp-stream-for-arglist arg-parsed) + )) + )) + + ;; if we have something to sub in, then do it. + (semantic-lex-spp-macro-to-macro-stream val beg end arg-split) + (setq semantic-lex-end-point end) + ) + )) + +(defvar semantic-lex-spp-replacements-enabled t + "Non-nil means do replacements when finding keywords. +Disable this only to prevent recursive expansion issues.") + +(defun semantic-lex-spp-analyzer-push-tokens-for-symbol (str beg end) + "Push lexical tokens for the symbol or keyword STR. +STR occurs in the current buffer between BEG and END." + (let (sym val count) + (cond + ;; + ;; It is a macro. Prepare for a replacement. + ((and semantic-lex-spp-replacements-enabled + (semantic-lex-spp-symbol-p str)) + (setq sym (semantic-lex-spp-symbol str) + val (symbol-value sym) + count 0) + + (let ((semantic-lex-spp-expanded-macro-stack + semantic-lex-spp-expanded-macro-stack)) + + (semantic-lex-with-macro-used str + ;; Do direct replacements of single value macros of macros. + ;; This solves issues with a macro containing one symbol that + ;; is another macro, and get arg lists passed around. + (while (and val (consp val) + (semantic-lex-token-p (car val)) + (eq (length val) 1) + (eq (semantic-lex-token-class (car val)) 'symbol) + (semantic-lex-spp-symbol-p (semantic-lex-token-text (car val))) + (< count 10) + ) + (setq str (semantic-lex-token-text (car val))) + (setq sym (semantic-lex-spp-symbol str) + val (symbol-value sym)) + ;; Prevent recursion + (setq count (1+ count)) + ;; This prevents a different kind of recursion. + (push str semantic-lex-spp-expanded-macro-stack) + ) + + (semantic-lex-spp-anlyzer-do-replace sym val beg end)) + + )) + ;; Anything else. + (t + ;; A regular keyword. + (semantic-lex-push-token + (semantic-lex-token (or (semantic-lex-keyword-p str) 'symbol) + beg end)))) + )) + +(define-lex-regex-analyzer semantic-lex-spp-replace-or-symbol-or-keyword + "Like 'semantic-lex-symbol-or-keyword' plus preprocessor macro replacement." + "\\(\\sw\\|\\s_\\)+" + (let ((str (match-string 0)) + (beg (match-beginning 0)) + (end (match-end 0))) + (semantic-lex-spp-analyzer-push-tokens-for-symbol str beg end))) + +;;; ANALYZERS FOR NEW MACROS +;; +;; These utilities and analyzer declaration function are for +;; creating an analyzer which produces new macros in the macro table. +;; +;; There are two analyzers. One for new macros, and one for removing +;; a macro. + +(defun semantic-lex-spp-first-token-arg-list (token) + "If TOKEN is a semantic-list, turn it into a an SPP ARG LIST." + (when (and (consp token) + (symbolp (car token)) + (eq 'semantic-list (car token))) + ;; Convert TOKEN in place. + (let ((argsplit (split-string (semantic-lex-token-text token) + "[(), ]" t))) + (setcar token 'spp-arg-list) + (setcar (nthcdr 1 token) argsplit)) + )) + +(defun semantic-lex-spp-one-token-and-move-for-macro (max) + "Lex up one token, and move to end of that token. +Don't go past MAX." + (let ((ans (semantic-lex (point) max 0 0))) + (if (not ans) + (progn (goto-char max) + nil) + (when (> (semantic-lex-token-end (car ans)) max) + (let ((bounds (semantic-lex-token-bounds (car ans)))) + (setcdr bounds max))) + (goto-char (semantic-lex-token-end (car ans))) + (car ans)) + )) + +(defun semantic-lex-spp-stream-for-arglist (token) + "Lex up the contents of the arglist TOKEN. +Parsing starts inside the parens, and ends at the end of TOKEN." + (let ((end (semantic-lex-token-end token)) + (fresh-toks nil) + (toks nil)) + (save-excursion + + (if (stringp (nth 1 token)) + ;; If the 2nd part of the token is a string, then we have + ;; a token specifically extracted from a buffer. Possibly + ;; a different buffer. This means we need to do something + ;; nice to parse its contents. + (let ((txt (semantic-lex-token-text token))) + (semantic-lex-spp-lex-text-string + (substring txt 1 (1- (length txt))))) + + ;; This part is like the original + (goto-char (semantic-lex-token-start token)) + ;; A cheat for going into the semantic list. + (forward-char 1) + (setq fresh-toks (semantic-lex-spp-stream-for-macro (1- end))) + (dolist (tok fresh-toks) + (when (memq (semantic-lex-token-class tok) '(symbol semantic-list)) + (setq toks (cons tok toks)))) + + (nreverse toks))))) + +(defvar semantic-lex-spp-hack-depth 0 + "Current depth of recursive calls to `semantic-lex-spp-lex-text-string'.") + +(defun semantic-lex-spp-lex-text-string (text) + "Lex the text string TEXT using the current buffer's state. +Use this to parse text extracted from a macro as if it came from +the current buffer. Since the lexer is designed to only work in +a buffer, we need to create a new buffer, and populate it with rules +and variable state from the current buffer." + (let* ((semantic-lex-spp-hack-depth (1+ semantic-lex-spp-hack-depth)) + (buf (get-buffer-create (format " *SPP parse hack %d*" + semantic-lex-spp-hack-depth))) + (mode major-mode) + (fresh-toks nil) + (toks nil) + (origbuff (current-buffer)) + (important-vars '(semantic-lex-spp-macro-symbol-obarray + semantic-lex-spp-project-macro-symbol-obarray + semantic-lex-spp-dynamic-macro-symbol-obarray + semantic-lex-spp-dynamic-macro-symbol-obarray-stack + semantic-lex-spp-expanded-macro-stack + )) + ) + (save-excursion + (set-buffer buf) + (erase-buffer) + ;; Below is a painful hack to make sure everything is setup correctly. + (when (not (eq major-mode mode)) + (save-match-data + + ;; Protect against user-hooks that throw errors. + (condition-case nil + (funcall mode) + (error nil)) + + ;; Hack in mode-local + (activate-mode-local-bindings) + ;; CHEATER! The following 3 lines are from + ;; `semantic-new-buffer-fcn', but we don't want to turn + ;; on all the other annoying modes for this little task. + (setq semantic-new-buffer-fcn-was-run t) + (semantic-lex-init) + (semantic-clear-toplevel-cache) + (remove-hook 'semantic-lex-reset-hooks 'semantic-lex-spp-reset-hook + t) + )) + + ;; Second Cheat: copy key variables regarding macro state from the + ;; the originating buffer we are parsing. We need to do this every time + ;; since the state changes. + (dolist (V important-vars) + (set V (semantic-buffer-local-value V origbuff))) + (insert text) + (goto-char (point-min)) + + (setq fresh-toks (semantic-lex-spp-stream-for-macro (point-max)))) + + (dolist (tok fresh-toks) + (when (memq (semantic-lex-token-class tok) '(symbol semantic-list)) + (setq toks (cons tok toks)))) + + (nreverse toks))) + +;;;; FIRST DRAFT +;; This is the fist version of semantic-lex-spp-stream-for-arglist +;; that worked pretty well. It doesn't work if the TOKEN was derived +;; from some other buffer, in which case it can get the wrong answer +;; or throw an error if the token location in the originating buffer is +;; larger than the current buffer. +;;(defun semantic-lex-spp-stream-for-arglist-orig (token) +;; "Lex up the contents of the arglist TOKEN. +;; Parsing starts inside the parens, and ends at the end of TOKEN." +;; (save-excursion +;; (let ((end (semantic-lex-token-end token)) +;; (fresh-toks nil) +;; (toks nil)) +;; (goto-char (semantic-lex-token-start token)) +;; ;; A cheat for going into the semantic list. +;; (forward-char 1) +;; (setq fresh-toks (semantic-lex-spp-stream-for-macro (1- end))) +;; (dolist (tok fresh-toks) +;; (when (memq (semantic-lex-token-class tok) '(symbol semantic-list)) +;; (setq toks (cons tok toks)))) +;; (nreverse toks)) +;; )) + +;;;; USING SPLIT +;; This doesn't work, because some arguments passed into a macro +;; might contain non-simple symbol words, which this doesn't handle. +;; +;; Thus, you need a full lex to occur. +;; (defun semantic-lex-spp-stream-for-arglist-split (token) +;; "Lex up the contents of the arglist TOKEN. +;; Parsing starts inside the parens, and ends at the end of TOKEN." +;; (let* ((txt (semantic-lex-token-text token)) +;; (split (split-string (substring txt 1 (1- (length txt))) +;; "(), " t)) +;; ;; Hack for lexing. +;; (semantic-lex-spp-analyzer-push-tokens-for-symbol nil)) +;; (dolist (S split) +;; (semantic-lex-spp-analyzer-push-tokens-for-symbol S 0 1)) +;; (reverse semantic-lex-spp-analyzer-push-tokens-for-symbol))) + + +(defun semantic-lex-spp-stream-for-macro (eos) + "Lex up a stream of tokens for a #define statement. +Parsing starts at the current point location. +EOS is the end of the stream to lex for this macro." + (let ((stream nil)) + (while (< (point) eos) + (let* ((tok (semantic-lex-spp-one-token-and-move-for-macro eos)) + (str (when tok + (semantic-lex-token-text tok))) + ) + (if str + (push (semantic-lex-token (semantic-lex-token-class tok) + (semantic-lex-token-start tok) + (semantic-lex-token-end tok) + str) + stream) + ;; Nothing to push. + nil))) + (goto-char eos) + ;; Fix the order + (nreverse stream) + )) + +(defmacro define-lex-spp-macro-declaration-analyzer (name doc regexp tokidx + &rest valform) + "Define a lexical analyzer for defining new MACROS. +NAME is the name of the analyzer. +DOC is the documentation for the analyzer. +REGEXP is a regular expression for the analyzer to match. +See `define-lex-regex-analyzer' for more on regexp. +TOKIDX is an index into REGEXP for which a new lexical token +of type `spp-macro-def' is to be created. +VALFORM are forms that return the value to be saved for this macro, or nil. +When implementing a macro, you can use `semantic-lex-spp-stream-for-macro' +to convert text into a lexical stream for storage in the macro." + (let ((start (make-symbol "start")) + (end (make-symbol "end")) + (val (make-symbol "val")) + (startpnt (make-symbol "startpnt")) + (endpnt (make-symbol "endpnt"))) + `(define-lex-regex-analyzer ,name + ,doc + ,regexp + (let ((,start (match-beginning ,tokidx)) + (,end (match-end ,tokidx)) + (,startpnt semantic-lex-end-point) + (,val (save-match-data ,@valform)) + (,endpnt semantic-lex-end-point)) + (semantic-lex-spp-symbol-set + (buffer-substring-no-properties ,start ,end) + ,val) + (semantic-lex-push-token + (semantic-lex-token 'spp-macro-def + ,start ,end)) + ;; Preserve setting of the end point from the calling macro. + (when (and (/= ,startpnt ,endpnt) + (/= ,endpnt semantic-lex-end-point)) + (setq semantic-lex-end-point ,endpnt)) + )))) + +(defmacro define-lex-spp-macro-undeclaration-analyzer (name doc regexp tokidx) + "Undefine a lexical analyzer for defining new MACROS. +NAME is the name of the analyzer. +DOC is the documentation for the analyzer. +REGEXP is a regular expression for the analyzer to match. +See `define-lex-regex-analyzer' for more on regexp. +TOKIDX is an index into REGEXP for which a new lexical token +of type `spp-macro-undef' is to be created." + (let ((start (make-symbol "start")) + (end (make-symbol "end"))) + `(define-lex-regex-analyzer ,name + ,doc + ,regexp + (let ((,start (match-beginning ,tokidx)) + (,end (match-end ,tokidx)) + ) + (semantic-lex-spp-symbol-remove + (buffer-substring-no-properties ,start ,end)) + (semantic-lex-push-token + (semantic-lex-token 'spp-macro-undef + ,start ,end)) + )))) + +;;; INCLUDES +;; +;; These analyzers help a language define how include files +;; are identified. These are ONLY for languages that perform +;; an actual textual includesion, and not for imports. +;; +;; This section is supposed to allow the macros from the headers to be +;; added to the local dynamic macro table, but that hasn't been +;; written yet. +;; +(defcustom semantic-lex-spp-use-headers-flag nil + "*Non-nil means to pre-parse headers as we go. +For languages that use the Semantic pre-processor, this can +improve the accuracy of parsed files where include files +can change the state of what's parsed in the current file. + +Note: Note implemented yet" + :group 'semantic + :type 'boolean) + +(defun semantic-lex-spp-merge-header (name) + "Extract and merge any macros from the header with NAME. +Finds the header file belonging to NAME, gets the macros +from that file, and then merge the macros with our current +symbol table." + (when semantic-lex-spp-use-headers-flag + ;; @todo - do this someday, ok? + )) + +(defmacro define-lex-spp-include-analyzer (name doc regexp tokidx + &rest valform) + "Define a lexical analyzer for defining a new INCLUDE lexical token. +Macros defined in the found include will be added to our running table +at the time the include statement is found. +NAME is the name of the analyzer. +DOC is the documentation for the analyzer. +REGEXP is a regular expression for the analyzer to match. +See `define-lex-regex-analyzer' for more on regexp. +TOKIDX is an index into REGEXP for which a new lexical token +of type `spp-macro-include' is to be created. +VALFORM are forms that return the name of the thing being included, and the +type of include. The return value should be of the form: + (NAME . TYPE) +where NAME is the name of the include, and TYPE is the type of the include, +where a valid symbol is 'system, or nil." + (let ((start (make-symbol "start")) + (end (make-symbol "end")) + (val (make-symbol "val")) + (startpnt (make-symbol "startpnt")) + (endpnt (make-symbol "endpnt"))) + `(define-lex-regex-analyzer ,name + ,doc + ,regexp + (let ((,start (match-beginning ,tokidx)) + (,end (match-end ,tokidx)) + (,startpnt semantic-lex-end-point) + (,val (save-match-data ,@valform)) + (,endpnt semantic-lex-end-point)) + ;;(message "(car ,val) -> %S" (car ,val)) + (semantic-lex-spp-merge-header (car ,val)) + (semantic-lex-push-token + (semantic-lex-token (if (eq (cdr ,val) 'system) + 'spp-system-include + 'spp-include) + ,start ,end + (car ,val))) + ;; Preserve setting of the end point from the calling macro. + (when (and (/= ,startpnt ,endpnt) + (/= ,endpnt semantic-lex-end-point)) + (setq semantic-lex-end-point ,endpnt)) + )))) + +;;; EIEIO USAGE +;; +;; Semanticdb can save off macro tables for quick lookup later. +;; +;; These routines are for saving macro lists into an EIEIO persistent +;; file. +(defvar semantic-lex-spp-macro-max-length-to-save 200 + "*Maximum length of an SPP macro before we opt to not save it.") + +;;;###autoload +(defun semantic-lex-spp-table-write-slot-value (value) + "Write out the VALUE of a slot for EIEIO. +The VALUE is a spp lexical table." + (if (not value) + (princ "nil") + (princ "\n '(") + ;(princ value) + (dolist (sym value) + (princ "(") + (prin1 (car sym)) + (let* ((first (car (cdr sym))) + (rest (cdr sym))) + (when (not (listp first)) + (error "Error in macro \"%s\"" (car sym))) + (when (eq (car first) 'spp-arg-list) + (princ " ") + (prin1 first) + (setq rest (cdr rest)) + ) + + (when rest + (princ " . ") + (let ((len (length (cdr rest)))) + (cond ((< len 2) + (condition-case nil + (prin1 rest) + (error + (princ "nil ;; Error writing macro\n")))) + ((< len semantic-lex-spp-macro-max-length-to-save) + (princ "\n ") + (condition-case nil + (prin1 rest) + (error + (princ "nil ;; Error writing macro\n "))) + ) + (t ;; Too Long! + (princ "nil ;; Too Long!\n ") + )))) + ) + (princ ")\n ") + ) + (princ ")\n")) +) + +;;; MACRO TABLE DEBUG +;; +(defun semantic-lex-spp-describe (&optional buffer) + "Describe the current list of spp macros for BUFFER. +If BUFFER is not provided, use the current buffer." + (interactive) + (let ((syms (save-excursion + (if buffer (set-buffer buffer)) + (semantic-lex-spp-macros))) + (sym nil)) + (with-output-to-temp-buffer "*SPP MACROS*" + (princ "Macro\t\tValue\n") + (while syms + (setq sym (car syms) + syms (cdr syms)) + (princ (symbol-name sym)) + (princ "\t") + (if (< (length (symbol-name sym)) 8) + (princ "\t")) + (prin1 (symbol-value sym)) + (princ "\n") + )))) + +;;; EDEBUG Handlers +;; +(add-hook + 'edebug-setup-hook + #'(lambda () + + (def-edebug-spec define-lex-spp-macro-declaration-analyzer + (&define name stringp stringp form def-body) + ) + + (def-edebug-spec define-lex-spp-macro-undeclaration-analyzer + (&define name stringp stringp form) + ) + + (def-edebug-spec define-lex-spp-include-analyzer + (&define name stringp stringp form def-body)))) + +(provide 'semantic/lex-spp) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/lex-spp" +;; End: + +;;; semantic-lex-spp.el ends here diff --git a/lisp/cedet/semantic/lex.el b/lisp/cedet/semantic/lex.el new file mode 100644 index 00000000000..eb6d46df473 --- /dev/null +++ b/lisp/cedet/semantic/lex.el @@ -0,0 +1,2053 @@ +;;; semantic/lex.el --- Lexical Analyzer builder + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; This file handles the creation of lexical analyzers for different +;; languages in Emacs Lisp. The purpose of a lexical analyzer is to +;; convert a buffer into a list of lexical tokens. Each token +;; contains the token class (such as 'number, 'symbol, 'IF, etc) and +;; the location in the buffer it was found. Optionally, a token also +;; contains a string representing what is at the designated buffer +;; location. +;; +;; Tokens are pushed onto a token stream, which is basically a list of +;; all the lexical tokens from the analyzed region. The token stream +;; is then handed to the grammar which parsers the file. +;; +;;; How it works +;; +;; Each analyzer specifies a condition and forms. These conditions +;; and forms are assembled into a function by `define-lex' that does +;; the lexical analysis. +;; +;; In the lexical analyzer created with `define-lex', each condition +;; is tested for a given point. When the conditin is true, the forms +;; run. +;; +;; The forms can push a lexical token onto the token stream. The +;; analyzer forms also must move the current analyzer point. If the +;; analyzer point is moved without pushing a token, then tne matched +;; syntax is effectively ignored, or skipped. +;; +;; Thus, starting at the beginning of a region to be analyzed, each +;; condition is tested. One will match, and a lexical token might be +;; pushed, and the point is moved to the end of the lexical token +;; identified. At the new position, the process occurs again until +;; the end of the specified region is reached. +;; +;;; How to use semantic-lex +;; +;; To create a lexer for a language, use the `define-lex' macro. +;; +;; The `define-lex' macro accepts a list of lexical analyzers. Each +;; analyzer is created with `define-lex-analyzer', or one of the +;; derivitive macros. A single analyzer defines a regular expression +;; to match text in a buffer, and a short segment of code to create +;; one lexical token. +;; +;; Each analyzer has a NAME, DOC, a CONDITION, and possibly some +;; FORMS. The NAME is the name used in `define-lex'. The DOC +;; describes what the analyzer should do. +;; +;; The CONDITION evaluates the text at the current point in the +;; current buffer. If CONDITION is true, then the FORMS will be +;; executed. +;; +;; The purpose of the FORMS is to push new lexical tokens onto the +;; list of tokens for the current buffer, and to move point after the +;; matched text. +;; +;; Some macros for creating one analyzer are: +;; +;; define-lex-analyzer - A generic analyzer associating any style of +;; condition to forms. +;; define-lex-regex-analyzer - Matches a regular expression. +;; define-lex-simple-regex-analyzer - Matches a regular expressions, +;; and pushes the match. +;; define-lex-block-analyzer - Matches list syntax, and defines +;; handles open/close delimiters. +;; +;; These macros are used by the grammar compiler when lexical +;; information is specified in a grammar: +;; define-lex- * -type-analyzer - Matches syntax specified in +;; a grammar, and pushes one token for it. The * would +;; be `sexp' for things like lists or strings, and +;; `string' for things that need to match some special +;; string, such as "\\." where a literal match is needed. +;; +;;; Lexical Tables +;; +;; There are tables of different symbols managed in semantic-lex.el. +;; They are: +;; +;; Lexical keyword table - A Table of symbols declared in a grammar +;; file with the %keyword declaration. +;; Keywords are used by `semantic-lex-symbol-or-keyword' +;; to create lexical tokens based on the keyword. +;; +;; Lexical type table - A table of symbols declared in a grammer +;; file with the %type declaration. +;; The grammar compiler uses the type table to create new +;; lexical analyzers. These analyzers are then used to when +;; a new lexical analyzer is made for a language. +;; +;;; Lexical Types +;; +;; A lexical type defines a kind of lexical analyzer that will be +;; automatically generated from a grammar file based on some +;; predetermined attributes. For now these two attributes are +;; recognized : +;; +;; * matchdatatype : define the kind of lexical analyzer. That is : +;; +;; - regexp : define a regexp analyzer (see +;; `define-lex-regex-type-analyzer') +;; +;; - string : define a string analyzer (see +;; `define-lex-string-type-analyzer') +;; +;; - block : define a block type analyzer (see +;; `define-lex-block-type-analyzer') +;; +;; - sexp : define a sexp analyzer (see +;; `define-lex-sexp-type-analyzer') +;; +;; - keyword : define a keyword analyzer (see +;; `define-lex-keyword-type-analyzer') +;; +;; * syntax : define the syntax that matches a syntactic +;; expression. When syntax is matched the corresponding type +;; analyzer is entered and the resulting match data will be +;; interpreted based on the kind of analyzer (see matchdatatype +;; above). +;; +;; The following lexical types are predefined : +;; +;; +-------------+---------------+--------------------------------+ +;; | type | matchdatatype | syntax | +;; +-------------+---------------+--------------------------------+ +;; | punctuation | string | "\\(\\s.\\|\\s$\\|\\s'\\)+" | +;; | keyword | keyword | "\\(\\sw\\|\\s_\\)+" | +;; | symbol | regexp | "\\(\\sw\\|\\s_\\)+" | +;; | string | sexp | "\\s\"" | +;; | number | regexp | semantic-lex-number-expression | +;; | block | block | "\\s(\\|\\s)" | +;; +-------------+---------------+--------------------------------+ +;; +;; In a grammar you must use a %type expression to automatically generate +;; the corresponding analyzers of that type. +;; +;; Here is an example to auto-generate punctuation analyzers +;; with 'matchdatatype and 'syntax predefined (see table above) +;; +;; %type <punctuation> ;; will auto-generate this kind of analyzers +;; +;; It is equivalent to write : +;; +;; %type <punctuation> syntax "\\(\\s.\\|\\s$\\|\\s'\\)+" matchdatatype string +;; +;; ;; Some punctuations based on the type defines above +;; +;; %token <punctuation> NOT "!" +;; %token <punctuation> NOTEQ "!=" +;; %token <punctuation> MOD "%" +;; %token <punctuation> MODEQ "%=" +;; + +;;; On the Semantic 1.x lexer +;; +;; In semantic 1.x, the lexical analyzer was an all purpose routine. +;; To boost efficiency, the analyzer is now a series of routines that +;; are constructed at build time into a single routine. This will +;; eliminate unneeded if statements to speed the lexer. + +(require 'semantic/fw) + +;;; Code: + +;;; Semantic 2.x lexical analysis +;; +(defun semantic-lex-map-symbols (fun table &optional property) + "Call function FUN on every symbol in TABLE. +If optional PROPERTY is non-nil, call FUN only on every symbol which +as a PROPERTY value. FUN receives a symbol as argument." + (if (arrayp table) + (mapatoms + #'(lambda (symbol) + (if (or (null property) (get symbol property)) + (funcall fun symbol))) + table))) + +;;; Lexical keyword table handling. +;; +;; These keywords are keywords defined for using in a grammar with the +;; %keyword declaration, and are not keywords used in Emacs Lisp. + +(defvar semantic-flex-keywords-obarray nil + "Buffer local keyword obarray for the lexical analyzer. +These keywords are matched explicitly, and converted into special symbols.") +(make-variable-buffer-local 'semantic-flex-keywords-obarray) + +(defmacro semantic-lex-keyword-invalid (name) + "Signal that NAME is an invalid keyword name." + `(signal 'wrong-type-argument '(semantic-lex-keyword-p ,name))) + +(defsubst semantic-lex-keyword-symbol (name) + "Return keyword symbol with NAME or nil if not found." + (and (arrayp semantic-flex-keywords-obarray) + (stringp name) + (intern-soft name semantic-flex-keywords-obarray))) + +(defsubst semantic-lex-keyword-p (name) + "Return non-nil if a keyword with NAME exists in the keyword table. +Return nil otherwise." + (and (setq name (semantic-lex-keyword-symbol name)) + (symbol-value name))) + +(defsubst semantic-lex-keyword-set (name value) + "Set value of keyword with NAME to VALUE and return VALUE." + (set (intern name semantic-flex-keywords-obarray) value)) + +(defsubst semantic-lex-keyword-value (name) + "Return value of keyword with NAME. +Signal an error if a keyword with NAME does not exist." + (let ((keyword (semantic-lex-keyword-symbol name))) + (if keyword + (symbol-value keyword) + (semantic-lex-keyword-invalid name)))) + +(defsubst semantic-lex-keyword-put (name property value) + "For keyword with NAME, set its PROPERTY to VALUE." + (let ((keyword (semantic-lex-keyword-symbol name))) + (if keyword + (put keyword property value) + (semantic-lex-keyword-invalid name)))) + +(defsubst semantic-lex-keyword-get (name property) + "For keyword with NAME, return its PROPERTY value." + (let ((keyword (semantic-lex-keyword-symbol name))) + (if keyword + (get keyword property) + (semantic-lex-keyword-invalid name)))) + +(defun semantic-lex-make-keyword-table (specs &optional propspecs) + "Convert keyword SPECS into an obarray and return it. +SPECS must be a list of (NAME . TOKSYM) elements, where: + + NAME is the name of the keyword symbol to define. + TOKSYM is the lexical token symbol of that keyword. + +If optional argument PROPSPECS is non nil, then interpret it, and +apply those properties. +PROPSPECS must be a list of (NAME PROPERTY VALUE) elements." + ;; Create the symbol hash table + (let ((semantic-flex-keywords-obarray (make-vector 13 0)) + spec) + ;; fill it with stuff + (while specs + (setq spec (car specs) + specs (cdr specs)) + (semantic-lex-keyword-set (car spec) (cdr spec))) + ;; Apply all properties + (while propspecs + (setq spec (car propspecs) + propspecs (cdr propspecs)) + (semantic-lex-keyword-put (car spec) (nth 1 spec) (nth 2 spec))) + semantic-flex-keywords-obarray)) + +(defsubst semantic-lex-map-keywords (fun &optional property) + "Call function FUN on every lexical keyword. +If optional PROPERTY is non-nil, call FUN only on every keyword which +as a PROPERTY value. FUN receives a lexical keyword as argument." + (semantic-lex-map-symbols + fun semantic-flex-keywords-obarray property)) + +(defun semantic-lex-keywords (&optional property) + "Return a list of lexical keywords. +If optional PROPERTY is non-nil, return only keywords which have a +PROPERTY set." + (let (keywords) + (semantic-lex-map-keywords + #'(lambda (symbol) (setq keywords (cons symbol keywords))) + property) + keywords)) + +;;; Inline functions: + +(defvar semantic-lex-unterminated-syntax-end-function) +(defvar semantic-lex-analysis-bounds) +(defvar semantic-lex-end-point) + +(defsubst semantic-lex-token-bounds (token) + "Fetch the start and end locations of the lexical token TOKEN. +Return a pair (START . END)." + (if (not (numberp (car (cdr token)))) + (cdr (cdr token)) + (cdr token))) + +(defsubst semantic-lex-token-start (token) + "Fetch the start position of the lexical token TOKEN. +See also the function `semantic-lex-token'." + (car (semantic-lex-token-bounds token))) + +(defsubst semantic-lex-token-end (token) + "Fetch the end position of the lexical token TOKEN. +See also the function `semantic-lex-token'." + (cdr (semantic-lex-token-bounds token))) + +(defsubst semantic-lex-unterminated-syntax-detected (syntax) + "Inside a lexical analyzer, use this when unterminated syntax was found. +Argument SYNTAX indicates the type of syntax that is unterminated. +The job of this function is to move (point) to a new logical location +so that analysis can continue, if possible." + (goto-char + (funcall semantic-lex-unterminated-syntax-end-function + syntax + (car semantic-lex-analysis-bounds) + (cdr semantic-lex-analysis-bounds) + )) + (setq semantic-lex-end-point (point))) + +;;; Type table handling. +;; +;; The lexical type table manages types that occur in a grammar file +;; with the %type declaration. Types represent different syntaxes. +;; See code for `semantic-lex-preset-default-types' for the classic +;; types of syntax. +(defvar semantic-lex-types-obarray nil + "Buffer local types obarray for the lexical analyzer.") +(make-variable-buffer-local 'semantic-lex-types-obarray) + +(defmacro semantic-lex-type-invalid (type) + "Signal that TYPE is an invalid lexical type name." + `(signal 'wrong-type-argument '(semantic-lex-type-p ,type))) + +(defsubst semantic-lex-type-symbol (type) + "Return symbol with TYPE or nil if not found." + (and (arrayp semantic-lex-types-obarray) + (stringp type) + (intern-soft type semantic-lex-types-obarray))) + +(defsubst semantic-lex-type-p (type) + "Return non-nil if a symbol with TYPE name exists." + (and (setq type (semantic-lex-type-symbol type)) + (symbol-value type))) + +(defsubst semantic-lex-type-set (type value) + "Set value of symbol with TYPE name to VALUE and return VALUE." + (set (intern type semantic-lex-types-obarray) value)) + +(defsubst semantic-lex-type-value (type &optional noerror) + "Return value of symbol with TYPE name. +If optional argument NOERROR is non-nil return nil if a symbol with +TYPE name does not exist. Otherwise signal an error." + (let ((sym (semantic-lex-type-symbol type))) + (if sym + (symbol-value sym) + (unless noerror + (semantic-lex-type-invalid type))))) + +(defsubst semantic-lex-type-put (type property value &optional add) + "For symbol with TYPE name, set its PROPERTY to VALUE. +If optional argument ADD is non-nil, create a new symbol with TYPE +name if it does not already exist. Otherwise signal an error." + (let ((sym (semantic-lex-type-symbol type))) + (unless sym + (or add (semantic-lex-type-invalid type)) + (semantic-lex-type-set type nil) + (setq sym (semantic-lex-type-symbol type))) + (put sym property value))) + +(defsubst semantic-lex-type-get (type property &optional noerror) + "For symbol with TYPE name, return its PROPERTY value. +If optional argument NOERROR is non-nil return nil if a symbol with +TYPE name does not exist. Otherwise signal an error." + (let ((sym (semantic-lex-type-symbol type))) + (if sym + (get sym property) + (unless noerror + (semantic-lex-type-invalid type))))) + +(defun semantic-lex-preset-default-types () + "Install useful default properties for well known types." + (semantic-lex-type-put "punctuation" 'matchdatatype 'string t) + (semantic-lex-type-put "punctuation" 'syntax "\\(\\s.\\|\\s$\\|\\s'\\)+") + (semantic-lex-type-put "keyword" 'matchdatatype 'keyword t) + (semantic-lex-type-put "keyword" 'syntax "\\(\\sw\\|\\s_\\)+") + (semantic-lex-type-put "symbol" 'matchdatatype 'regexp t) + (semantic-lex-type-put "symbol" 'syntax "\\(\\sw\\|\\s_\\)+") + (semantic-lex-type-put "string" 'matchdatatype 'sexp t) + (semantic-lex-type-put "string" 'syntax "\\s\"") + (semantic-lex-type-put "number" 'matchdatatype 'regexp t) + (semantic-lex-type-put "number" 'syntax 'semantic-lex-number-expression) + (semantic-lex-type-put "block" 'matchdatatype 'block t) + (semantic-lex-type-put "block" 'syntax "\\s(\\|\\s)") + ) + +(defun semantic-lex-make-type-table (specs &optional propspecs) + "Convert type SPECS into an obarray and return it. +SPECS must be a list of (TYPE . TOKENS) elements, where: + + TYPE is the name of the type symbol to define. + TOKENS is an list of (TOKSYM . MATCHER) elements, where: + + TOKSYM is any lexical token symbol. + MATCHER is a string or regexp a text must match to be a such + lexical token. + +If optional argument PROPSPECS is non nil, then interpret it, and +apply those properties. +PROPSPECS must be a list of (TYPE PROPERTY VALUE)." + ;; Create the symbol hash table + (let* ((semantic-lex-types-obarray (make-vector 13 0)) + spec type tokens token alist default) + ;; fill it with stuff + (while specs + (setq spec (car specs) + specs (cdr specs) + type (car spec) + tokens (cdr spec) + default nil + alist nil) + (while tokens + (setq token (car tokens) + tokens (cdr tokens)) + (if (cdr token) + (setq alist (cons token alist)) + (setq token (car token)) + (if default + (message + "*Warning* default value of <%s> tokens changed to %S, was %S" + type default token)) + (setq default token))) + ;; Ensure the default matching spec is the first one. + (semantic-lex-type-set type (cons default (nreverse alist)))) + ;; Install useful default types & properties + (semantic-lex-preset-default-types) + ;; Apply all properties + (while propspecs + (setq spec (car propspecs) + propspecs (cdr propspecs)) + ;; Create the type if necessary. + (semantic-lex-type-put (car spec) (nth 1 spec) (nth 2 spec) t)) + semantic-lex-types-obarray)) + +(defsubst semantic-lex-map-types (fun &optional property) + "Call function FUN on every lexical type. +If optional PROPERTY is non-nil, call FUN only on every type symbol +which as a PROPERTY value. FUN receives a type symbol as argument." + (semantic-lex-map-symbols + fun semantic-lex-types-obarray property)) + +(defun semantic-lex-types (&optional property) + "Return a list of lexical type symbols. +If optional PROPERTY is non-nil, return only type symbols which have +PROPERTY set." + (let (types) + (semantic-lex-map-types + #'(lambda (symbol) (setq types (cons symbol types))) + property) + types)) + +;;; Lexical Analyzer framework settings +;; + +(defvar semantic-lex-analyzer 'semantic-flex + "The lexical analyzer used for a given buffer. +See `semantic-lex' for documentation. +For compatibility with Semantic 1.x it defaults to `semantic-flex'.") +(make-variable-buffer-local 'semantic-lex-analyzer) + +(defvar semantic-lex-tokens + '( + (bol) + (charquote) + (close-paren) + (comment) + (newline) + (open-paren) + (punctuation) + (semantic-list) + (string) + (symbol) + (whitespace) + ) + "An alist of of semantic token types. +As of December 2001 (semantic 1.4beta13), this variable is not used in +any code. The only use is to refer to the doc-string from elsewhere. + +The key to this alist is the symbol representing token type that +\\[semantic-flex] returns. These are + + - bol: Empty string matching a beginning of line. + This token is produced with + `semantic-lex-beginning-of-line'. + + - charquote: String sequences that match `\\s\\+' regexp. + This token is produced with `semantic-lex-charquote'. + + - close-paren: Characters that match `\\s)' regexp. + These are typically `)', `}', `]', etc. + This token is produced with + `semantic-lex-close-paren'. + + - comment: A comment chunk. These token types are not + produced by default. + This token is produced with `semantic-lex-comments'. + Comments are ignored with `semantic-lex-ignore-comments'. + Comments are treated as whitespace with + `semantic-lex-comments-as-whitespace'. + + - newline Characters matching `\\s-*\\(\n\\|\\s>\\)' regexp. + This token is produced with `semantic-lex-newline'. + + - open-paren: Characters that match `\\s(' regexp. + These are typically `(', `{', `[', etc. + If `semantic-lex-paren-or-list' is used, + then `open-paren' is not usually generated unless + the `depth' argument to \\[semantic-lex] is + greater than 0. + This token is always produced if the analyzer + `semantic-lex-open-paren' is used. + + - punctuation: Characters matching `{\\(\\s.\\|\\s$\\|\\s'\\)' + regexp. + This token is produced with `semantic-lex-punctuation'. + Always specify this analyzer after the comment + analyzer. + + - semantic-list: String delimited by matching parenthesis, braces, + etc. that the lexer skipped over, because the + `depth' parameter to \\[semantic-flex] was not high + enough. + This token is produced with `semantic-lex-paren-or-list'. + + - string: Quoted strings, i.e., string sequences that start + and end with characters matching `\\s\"' + regexp. The lexer relies on @code{forward-sexp} to + find the matching end. + This token is produced with `semantic-lex-string'. + + - symbol: String sequences that match `\\(\\sw\\|\\s_\\)+' + regexp. + This token is produced with + `semantic-lex-symbol-or-keyword'. Always add this analyzer + after `semantic-lex-number', or other analyzers that + match its regular expression. + + - whitespace: Characters that match `\\s-+' regexp. + This token is produced with `semantic-lex-whitespace'.") + +(defvar semantic-lex-syntax-modifications nil + "Changes to the syntax table for this buffer. +These changes are active only while the buffer is being flexed. +This is a list where each element has the form: + (CHAR CLASS) +CHAR is the char passed to `modify-syntax-entry', +and CLASS is the string also passed to `modify-syntax-entry' to define +what syntax class CHAR has.") +(make-variable-buffer-local 'semantic-lex-syntax-modifications) + +(defvar semantic-lex-syntax-table nil + "Syntax table used by lexical analysis. +See also `semantic-lex-syntax-modifications'.") +(make-variable-buffer-local 'semantic-lex-syntax-table) + +(defvar semantic-lex-comment-regex nil + "Regular expression for identifying comment start during lexical analysis. +This may be automatically set when semantic initializes in a mode, but +may need to be overriden for some special languages.") +(make-variable-buffer-local 'semantic-lex-comment-regex) + +(defvar semantic-lex-number-expression + ;; This expression was written by David Ponce for Java, and copied + ;; here for C and any other similar language. + (eval-when-compile + (concat "\\(" + "\\<[0-9]+[.][0-9]+\\([eE][-+]?[0-9]+\\)?[fFdD]?\\>" + "\\|" + "\\<[0-9]+[.][eE][-+]?[0-9]+[fFdD]?\\>" + "\\|" + "\\<[0-9]+[.][fFdD]\\>" + "\\|" + "\\<[0-9]+[.]" + "\\|" + "[.][0-9]+\\([eE][-+]?[0-9]+\\)?[fFdD]?\\>" + "\\|" + "\\<[0-9]+[eE][-+]?[0-9]+[fFdD]?\\>" + "\\|" + "\\<0[xX][0-9a-fA-F]+[lL]?\\>" + "\\|" + "\\<[0-9]+[lLfFdD]?\\>" + "\\)" + )) + "Regular expression for matching a number. +If this value is nil, no number extraction is done during lex. +This expression tries to match C and Java like numbers. + +DECIMAL_LITERAL: + [1-9][0-9]* + ; +HEX_LITERAL: + 0[xX][0-9a-fA-F]+ + ; +OCTAL_LITERAL: + 0[0-7]* + ; +INTEGER_LITERAL: + <DECIMAL_LITERAL>[lL]? + | <HEX_LITERAL>[lL]? + | <OCTAL_LITERAL>[lL]? + ; +EXPONENT: + [eE][+-]?[09]+ + ; +FLOATING_POINT_LITERAL: + [0-9]+[.][0-9]*<EXPONENT>?[fFdD]? + | [.][0-9]+<EXPONENT>?[fFdD]? + | [0-9]+<EXPONENT>[fFdD]? + | [0-9]+<EXPONENT>?[fFdD] + ;") +(make-variable-buffer-local 'semantic-lex-number-expression) + +(defvar semantic-lex-depth 0 + "Default lexing depth. +This specifies how many lists to create tokens in.") +(make-variable-buffer-local 'semantic-lex-depth) + +(defvar semantic-lex-unterminated-syntax-end-function + (lambda (syntax syntax-start lex-end) lex-end) + "Function called when unterminated syntax is encountered. +This should be set to one function. That function should take three +parameters. The SYNTAX, or type of syntax which is unterminated. +SYNTAX-START where the broken syntax begins. +LEX-END is where the lexical analysis was asked to end. +This function can be used for languages that can intelligently fix up +broken syntax, or the exit lexical analysis via `throw' or `signal' +when finding unterminated syntax.") + +;;; Interactive testing commands + +(declare-function semantic-elapsed-time "semantic") + +(defun semantic-lex-test (arg) + "Test the semantic lexer in the current buffer. +If universal argument ARG, then try the whole buffer." + (interactive "P") + (require 'semantic) + (let* ((start (current-time)) + (result (semantic-lex + (if arg (point-min) (point)) + (point-max))) + (end (current-time))) + (message "Elapsed Time: %.2f seconds." + (semantic-elapsed-time start end)) + (pop-to-buffer "*Lexer Output*") + (require 'pp) + (erase-buffer) + (insert (pp-to-string result)) + (goto-char (point-min)) + )) + +(defvar semantic-lex-debug nil + "When non-nil, debug the local lexical analyzer.") + +(defun semantic-lex-debug (arg) + "Debug the semantic lexer in the current buffer. +Argument ARG specifies of the analyze the whole buffer, or start at point. +While engaged, each token identified by the lexer will be highlighted +in the target buffer A description of the current token will be +displayed in the minibuffer. Press SPC to move to the next lexical token." + (interactive "P") + (require 'semantic/debug) + (let ((semantic-lex-debug t)) + (semantic-lex-test arg))) + +(defun semantic-lex-highlight-token (token) + "Highlight the lexical TOKEN. +TOKEN is a lexical token with a START And END position. +Return the overlay." + (let ((o (semantic-make-overlay (semantic-lex-token-start token) + (semantic-lex-token-end token)))) + (semantic-overlay-put o 'face 'highlight) + o)) + +(defsubst semantic-lex-debug-break (token) + "Break during lexical analysis at TOKEN." + (when semantic-lex-debug + (let ((o nil)) + (unwind-protect + (progn + (when token + (setq o (semantic-lex-highlight-token token))) + (semantic-read-event + (format "%S :: SPC - continue" token)) + ) + (when o + (semantic-overlay-delete o)))))) + +;;; Lexical analyzer creation +;; +;; Code for creating a lex function from lists of analyzers. +;; +;; A lexical analyzer is created from a list of individual analyzers. +;; Each individual analyzer specifies a single match, and code that +;; goes with it. +;; +;; Creation of an analyzer assembles these analyzers into a new function +;; with the behaviors of all the individual analyzers. +;; +(defmacro semantic-lex-one-token (analyzers) + "Calculate one token from the current buffer at point. +Uses locally bound variables from `define-lex'. +Argument ANALYZERS is the list of analyzers being used." + (cons 'cond (mapcar #'symbol-value analyzers))) + +(defvar semantic-lex-end-point nil + "The end point as tracked through lexical functions.") + +(defvar semantic-lex-current-depth nil + "The current depth as tracked through lexical functions.") + +(defvar semantic-lex-maximum-depth nil + "The maximum depth of parenthisis as tracked through lexical functions.") + +(defvar semantic-lex-token-stream nil + "The current token stream we are collecting.") + +(defvar semantic-lex-analysis-bounds nil + "The bounds of the current analysis.") + +(defvar semantic-lex-block-streams nil + "Streams of tokens inside collapsed blocks. +This is an alist of (ANCHOR . STREAM) elements where ANCHOR is the +start position of the block, and STREAM is the list of tokens in that +block.") + +(defvar semantic-lex-reset-hooks nil + "Abnormal hook used by major-modes to reset lexical analyzers. +Hook functions are called with START and END values for the +current lexical pass. Should be set with `add-hook', specifying +a LOCAL option.") + +;; Stack of nested blocks. +(defvar semantic-lex-block-stack nil) +;;(defvar semantic-lex-timeout 5 +;; "*Number of sections of lexing before giving up.") + +(defmacro define-lex (name doc &rest analyzers) + "Create a new lexical analyzer with NAME. +DOC is a documentation string describing this analyzer. +ANALYZERS are small code snippets of analyzers to use when +building the new NAMED analyzer. Only use analyzers which +are written to be used in `define-lex'. +Each analyzer should be an analyzer created with `define-lex-analyzer'. +Note: The order in which analyzers are listed is important. +If two analyzers can match the same text, it is important to order the +analyzers so that the one you want to match first occurs first. For +example, it is good to put a numbe analyzer in front of a symbol +analyzer which might mistake a number for as a symbol." + `(defun ,name (start end &optional depth length) + ,(concat doc "\nSee `semantic-lex' for more information.") + ;; Make sure the state of block parsing starts over. + (setq semantic-lex-block-streams nil) + ;; Allow specialty reset items. + (run-hook-with-args 'semantic-lex-reset-hooks start end) + ;; Lexing state. + (let* (;(starttime (current-time)) + (starting-position (point)) + (semantic-lex-token-stream nil) + (semantic-lex-block-stack nil) + (tmp-start start) + (semantic-lex-end-point start) + (semantic-lex-current-depth 0) + ;; Use the default depth when not specified. + (semantic-lex-maximum-depth + (or depth semantic-lex-depth)) + ;; Bounds needed for unterminated syntax + (semantic-lex-analysis-bounds (cons start end)) + ;; This entry prevents text properties from + ;; confusing our lexical analysis. See Emacs 22 (CVS) + ;; version of C++ mode with template hack text properties. + (parse-sexp-lookup-properties nil) + ) + ;; Maybe REMOVE THIS LATER. + ;; Trying to find incremental parser bug. + (when (> end (point-max)) + (error ,(format "%s: end (%%d) > point-max (%%d)" name) + end (point-max))) + (with-syntax-table semantic-lex-syntax-table + (goto-char start) + (while (and (< (point) end) + (or (not length) + (<= (length semantic-lex-token-stream) length))) + (semantic-lex-one-token ,analyzers) + (when (eq semantic-lex-end-point tmp-start) + (error ,(format "%s: endless loop at %%d, after %%S" name) + tmp-start (car semantic-lex-token-stream))) + (setq tmp-start semantic-lex-end-point) + (goto-char semantic-lex-end-point) + ;;(when (> (semantic-elapsed-time starttime (current-time)) + ;; semantic-lex-timeout) + ;; (error "Timeout during lex at char %d" (point))) + (semantic-throw-on-input 'lex) + (semantic-lex-debug-break (car semantic-lex-token-stream)) + )) + ;; Check that there is no unterminated block. + (when semantic-lex-block-stack + (let* ((last (pop semantic-lex-block-stack)) + (blk last)) + (while blk + (message + ,(format "%s: `%%s' block from %%S is unterminated" name) + (car blk) (cadr blk)) + (setq blk (pop semantic-lex-block-stack))) + (semantic-lex-unterminated-syntax-detected (car last)))) + ;; Return to where we started. + ;; Do not wrap in protective stuff so that if there is an error + ;; thrown, the user knows where. + (goto-char starting-position) + ;; Return the token stream + (nreverse semantic-lex-token-stream)))) + +;;; Collapsed block tokens delimited by any tokens. +;; +(defun semantic-lex-start-block (syntax) + "Mark the last read token as the beginning of a SYNTAX block." + (if (or (not semantic-lex-maximum-depth) + (< semantic-lex-current-depth semantic-lex-maximum-depth)) + (setq semantic-lex-current-depth (1+ semantic-lex-current-depth)) + (push (list syntax (car semantic-lex-token-stream)) + semantic-lex-block-stack))) + +(defun semantic-lex-end-block (syntax) + "Process the end of a previously marked SYNTAX block. +That is, collapse the tokens inside that block, including the +beginning and end of block tokens, into a high level block token of +class SYNTAX. +The token at beginning of block is the one marked by a previous call +to `semantic-lex-start-block'. The current token is the end of block. +The collapsed tokens are saved in `semantic-lex-block-streams'." + (if (null semantic-lex-block-stack) + (setq semantic-lex-current-depth (1- semantic-lex-current-depth)) + (let* ((stream semantic-lex-token-stream) + (blk (pop semantic-lex-block-stack)) + (bstream (cdr blk)) + (first (car bstream)) + (last (pop stream)) ;; The current token mark the EOBLK + tok) + (if (not (eq (car blk) syntax)) + ;; SYNTAX doesn't match the syntax of the current block in + ;; the stack. So we encountered the end of the SYNTAX block + ;; before the end of the current one in the stack which is + ;; signaled unterminated. + (semantic-lex-unterminated-syntax-detected (car blk)) + ;; Move tokens found inside the block from the main stream + ;; into a separate block stream. + (while (and stream (not (eq (setq tok (pop stream)) first))) + (push tok bstream)) + ;; The token marked as beginning of block was not encountered. + ;; This should not happen! + (or (eq tok first) + (error "Token %S not found at beginning of block `%s'" + first syntax)) + ;; Save the block stream for future reuse, to avoid to redo + ;; the lexical analysis of the block content! + ;; Anchor the block stream with its start position, so we can + ;; use: (cdr (assq start semantic-lex-block-streams)) to + ;; quickly retrieve the lexical stream associated to a block. + (setcar blk (semantic-lex-token-start first)) + (setcdr blk (nreverse bstream)) + (push blk semantic-lex-block-streams) + ;; In the main stream, replace the tokens inside the block by + ;; a high level block token of class SYNTAX. + (setq semantic-lex-token-stream stream) + (semantic-lex-push-token + (semantic-lex-token + syntax (car blk) (semantic-lex-token-end last))) + )))) + +;;; Lexical token API +;; +;; Functions for accessing parts of a token. Use these functions +;; instead of accessing the list structure directly because the +;; contents of the lexical may change. +;; +(defmacro semantic-lex-token (symbol start end &optional str) + "Create a lexical token. +SYMBOL is a symbol representing the class of syntax found. +START and END define the bounds of the token in the current buffer. +Optional STR is the string for the token iff the the bounds +in the buffer do not cover the string they represent. (As from +macro expansion.)" + ;; This if statement checks the existance of a STR argument at + ;; compile time, where STR is some symbol or constant. If the + ;; variable STr (runtime) is nil, this will make an incorrect decision. + ;; + ;; It is like this to maintain the original speed of the compiled + ;; code. + (if str + `(cons ,symbol (cons ,str (cons ,start ,end))) + `(cons ,symbol (cons ,start ,end)))) + +(defun semantic-lex-token-p (thing) + "Return non-nil if THING is a semantic lex token. +This is an exhaustively robust check." + (and (consp thing) + (symbolp (car thing)) + (or (and (numberp (nth 1 thing)) + (numberp (nthcdr 2 thing))) + (and (stringp (nth 1 thing)) + (numberp (nth 2 thing)) + (numberp (nthcdr 3 thing))) + )) + ) + +(defun semantic-lex-token-with-text-p (thing) + "Return non-nil if THING is a semantic lex token. +This is an exhaustively robust check." + (and (consp thing) + (symbolp (car thing)) + (= (length thing) 4) + (stringp (nth 1 thing)) + (numberp (nth 2 thing)) + (numberp (nth 3 thing))) + ) + +(defun semantic-lex-token-without-text-p (thing) + "Return non-nil if THING is a semantic lex token. +This is an exhaustively robust check." + (and (consp thing) + (symbolp (car thing)) + (= (length thing) 3) + (numberp (nth 1 thing)) + (numberp (nth 2 thing))) + ) + +(eval-and-compile + +(defun semantic-lex-expand-block-specs (specs) + "Expand block specifications SPECS into a Lisp form. +SPECS is a list of (BLOCK BEGIN END) elements where BLOCK, BEGIN, and +END are token class symbols that indicate to produce one collapsed +BLOCK token from tokens found between BEGIN and END ones. +BLOCK must be a non-nil symbol, and at least one of the BEGIN or END +symbols must be non-nil too. +When BEGIN is non-nil, generate a call to `semantic-lex-start-block' +when a BEGIN token class is encountered. +When END is non-nil, generate a call to `semantic-lex-end-block' when +an END token class is encountered." + (let ((class (make-symbol "class")) + (form nil)) + (dolist (spec specs) + (when (car spec) + (when (nth 1 spec) + (push `((eq ',(nth 1 spec) ,class) + (semantic-lex-start-block ',(car spec))) + form)) + (when (nth 2 spec) + (push `((eq ',(nth 2 spec) ,class) + (semantic-lex-end-block ',(car spec))) + form)))) + (when form + `((let ((,class (semantic-lex-token-class + (car semantic-lex-token-stream)))) + (cond ,@(nreverse form)))) + ))) +) + +(defmacro semantic-lex-push-token (token &rest blockspecs) + "Push TOKEN in the lexical analyzer token stream. +Return the lexical analysis current end point. +If optional arguments BLOCKSPECS is non-nil, it specifies to process +collapsed block tokens. See `semantic-lex-expand-block-specs' for +more details. +This macro should only be called within the bounds of +`define-lex-analyzer'. It changes the values of the lexical analyzer +variables `token-stream' and `semantic-lex-end-point'. If you need to +move `semantic-lex-end-point' somewhere else, just modify this +variable after calling `semantic-lex-push-token'." + `(progn + (push ,token semantic-lex-token-stream) + ,@(semantic-lex-expand-block-specs blockspecs) + (setq semantic-lex-end-point + (semantic-lex-token-end (car semantic-lex-token-stream))) + )) + +(defsubst semantic-lex-token-class (token) + "Fetch the class of the lexical token TOKEN. +See also the function `semantic-lex-token'." + (car token)) + +(defsubst semantic-lex-token-text (token) + "Fetch the text associated with the lexical token TOKEN. +See also the function `semantic-lex-token'." + (if (stringp (car (cdr token))) + (car (cdr token)) + (buffer-substring-no-properties + (semantic-lex-token-start token) + (semantic-lex-token-end token)))) + +(defun semantic-lex-init () + "Initialize any lexical state for this buffer." + (unless semantic-lex-comment-regex + (setq semantic-lex-comment-regex + (if comment-start-skip + (concat "\\(\\s<\\|" comment-start-skip "\\)") + "\\(\\s<\\)"))) + ;; Setup the lexer syntax-table + (setq semantic-lex-syntax-table (copy-syntax-table (syntax-table))) + (dolist (mod semantic-lex-syntax-modifications) + (modify-syntax-entry + (car mod) (nth 1 mod) semantic-lex-syntax-table))) + +;;;###autoload +(define-overloadable-function semantic-lex (start end &optional depth length) + "Lexically analyze text in the current buffer between START and END. +Optional argument DEPTH indicates at what level to scan over entire +lists. The last argument, LENGTH specifies that `semantic-lex' +should only return LENGTH tokens. The return value is a token stream. +Each element is a list, such of the form + (symbol start-expression . end-expression) +where SYMBOL denotes the token type. +See `semantic-lex-tokens' variable for details on token types. END +does not mark the end of the text scanned, only the end of the +beginning of text scanned. Thus, if a string extends past END, the +end of the return token will be larger than END. To truly restrict +scanning, use `narrow-to-region'." + (funcall semantic-lex-analyzer start end depth length)) + +(defsubst semantic-lex-buffer (&optional depth) + "Lex the current buffer. +Optional argument DEPTH is the depth to scan into lists." + (semantic-lex (point-min) (point-max) depth)) + +(defsubst semantic-lex-list (semlist depth) + "Lex the body of SEMLIST to DEPTH." + (semantic-lex (semantic-lex-token-start semlist) + (semantic-lex-token-end semlist) + depth)) + +;;; Analyzer creation macros +;; +;; An individual analyzer is a condition and code that goes with it. +;; +;; Created analyzers become variables with the code associated with them +;; as the symbol value. These analyzers are assembled into a lexer +;; to create new lexical analyzers. + +(defcustom semantic-lex-debug-analyzers nil + "Non nil means to debug analyzers with syntax protection. +Only in effect if `debug-on-error' is also non-nil." + :group 'semantic + :type 'boolean) + +(defmacro semantic-lex-unterminated-syntax-protection (syntax &rest forms) + "For SYNTAX, execute FORMS with protection for unterminated syntax. +If FORMS throws an error, treat this as a syntax problem, and +execute the unterminated syntax code. FORMS should return a position. +Irreguardless of an error, the cursor should be moved to the end of +the desired syntax, and a position returned. +If `debug-on-error' is set, errors are not caught, so that you can +debug them. +Avoid using a large FORMS since it is duplicated." + `(if (and debug-on-error semantic-lex-debug-analyzers) + (progn ,@forms) + (condition-case nil + (progn ,@forms) + (error + (semantic-lex-unterminated-syntax-detected ,syntax))))) +(put 'semantic-lex-unterminated-syntax-protection + 'lisp-indent-function 1) + +(defmacro define-lex-analyzer (name doc condition &rest forms) + "Create a single lexical analyzer NAME with DOC. +When an analyzer is called, the current buffer and point are +positioned in a buffer at the location to be analyzed. +CONDITION is an expression which returns t if FORMS should be run. +Within the bounds of CONDITION and FORMS, the use of backquote +can be used to evaluate expressions at compile time. +While forms are running, the following variables will be locally bound: + `semantic-lex-analysis-bounds' - The bounds of the current analysis. + of the form (START . END) + `semantic-lex-maximum-depth' - The maximum depth of semantic-list + for the current analysis. + `semantic-lex-current-depth' - The current depth of `semantic-list' that has + been decended. + `semantic-lex-end-point' - End Point after match. + Analyzers should set this to a buffer location if their + match string does not represent the end of the matched text. + `semantic-lex-token-stream' - The token list being collected. + Add new lexical tokens to this list. +Proper action in FORMS is to move the value of `semantic-lex-end-point' to +after the location of the analyzed entry, and to add any discovered tokens +at the beginning of `semantic-lex-token-stream'. +This can be done by using `semantic-lex-push-token'." + `(eval-and-compile + (defvar ,name nil ,doc) + (defun ,name nil) + ;; Do this part separately so that re-evaluation rebuilds this code. + (setq ,name '(,condition ,@forms)) + ;; Build a single lexical analyzer function, so the doc for + ;; function help is automatically provided, and perhaps the + ;; function could be useful for testing and debugging one + ;; analyzer. + (fset ',name (lambda () ,doc + (let ((semantic-lex-token-stream nil) + (semantic-lex-end-point (point)) + (semantic-lex-analysis-bounds + (cons (point) (point-max))) + (semantic-lex-current-depth 0) + (semantic-lex-maximum-depth + semantic-lex-depth) + ) + (when ,condition ,@forms) + semantic-lex-token-stream))) + )) + +(defmacro define-lex-regex-analyzer (name doc regexp &rest forms) + "Create a lexical analyzer with NAME and DOC that will match REGEXP. +FORMS are evaluated upon a successful match. +See `define-lex-analyzer' for more about analyzers." + `(define-lex-analyzer ,name + ,doc + (looking-at ,regexp) + ,@forms + )) + +(defmacro define-lex-simple-regex-analyzer (name doc regexp toksym + &optional index + &rest forms) + "Create a lexical analyzer with NAME and DOC that match REGEXP. +TOKSYM is the symbol to use when creating a semantic lexical token. +INDEX is the index into the match that defines the bounds of the token. +Index should be a plain integer, and not specified in the macro as an +expression. +FORMS are evaluated upon a successful match BEFORE the new token is +created. It is valid to ignore FORMS. +See `define-lex-analyzer' for more about analyzers." + `(define-lex-analyzer ,name + ,doc + (looking-at ,regexp) + ,@forms + (semantic-lex-push-token + (semantic-lex-token ,toksym + (match-beginning ,(or index 0)) + (match-end ,(or index 0)))) + )) + +(defmacro define-lex-block-analyzer (name doc spec1 &rest specs) + "Create a lexical analyzer NAME for paired delimiters blocks. +It detects a paired delimiters block or the corresponding open or +close delimiter depending on the value of the variable +`semantic-lex-current-depth'. DOC is the documentation string of the lexical +analyzer. SPEC1 and SPECS specify the token symbols and open, close +delimiters used. Each SPEC has the form: + +\(BLOCK-SYM (OPEN-DELIM OPEN-SYM) (CLOSE-DELIM CLOSE-SYM)) + +where BLOCK-SYM is the symbol returned in a block token. OPEN-DELIM +and CLOSE-DELIM are respectively the open and close delimiters +identifying a block. OPEN-SYM and CLOSE-SYM are respectively the +symbols returned in open and close tokens." + (let ((specs (cons spec1 specs)) + spec open olist clist) + (while specs + (setq spec (car specs) + specs (cdr specs) + open (nth 1 spec) + ;; build alist ((OPEN-DELIM OPEN-SYM BLOCK-SYM) ...) + olist (cons (list (car open) (cadr open) (car spec)) olist) + ;; build alist ((CLOSE-DELIM CLOSE-SYM) ...) + clist (cons (nth 2 spec) clist))) + `(define-lex-analyzer ,name + ,doc + (and + (looking-at "\\(\\s(\\|\\s)\\)") + (let ((text (match-string 0)) match) + (cond + ((setq match (assoc text ',olist)) + (if (or (not semantic-lex-maximum-depth) + (< semantic-lex-current-depth semantic-lex-maximum-depth)) + (progn + (setq semantic-lex-current-depth (1+ semantic-lex-current-depth)) + (semantic-lex-push-token + (semantic-lex-token + (nth 1 match) + (match-beginning 0) (match-end 0)))) + (semantic-lex-push-token + (semantic-lex-token + (nth 2 match) + (match-beginning 0) + (save-excursion + (semantic-lex-unterminated-syntax-protection (nth 2 match) + (forward-list 1) + (point))) + )) + )) + ((setq match (assoc text ',clist)) + (setq semantic-lex-current-depth (1- semantic-lex-current-depth)) + (semantic-lex-push-token + (semantic-lex-token + (nth 1 match) + (match-beginning 0) (match-end 0))))))) + ))) + +;;; Analyzers +;; +;; Pre-defined common analyzers. +;; +(define-lex-analyzer semantic-lex-default-action + "The default action when no other lexical actions match text. +This action will just throw an error." + t + (error "Unmatched Text during Lexical Analysis")) + +(define-lex-analyzer semantic-lex-beginning-of-line + "Detect and create a beginning of line token (BOL)." + (and (bolp) + ;; Just insert a (bol N . N) token in the token stream, + ;; without moving the point. N is the point at the + ;; beginning of line. + (semantic-lex-push-token (semantic-lex-token 'bol (point) (point))) + nil) ;; CONTINUE + ;; We identify and add the BOL token onto the stream, but since + ;; semantic-lex-end-point doesn't move, we always fail CONDITION, and have no + ;; FORMS body. + nil) + +(define-lex-simple-regex-analyzer semantic-lex-newline + "Detect and create newline tokens." + "\\s-*\\(\n\\|\\s>\\)" 'newline 1) + +(define-lex-regex-analyzer semantic-lex-newline-as-whitespace + "Detect and create newline tokens. +Use this ONLY if newlines are not whitespace characters (such as when +they are comment end characters) AND when you want whitespace tokens." + "\\s-*\\(\n\\|\\s>\\)" + ;; Language wants whitespaces. Create a token for it. + (if (eq (semantic-lex-token-class (car semantic-lex-token-stream)) + 'whitespace) + ;; Merge whitespace tokens together if they are adjacent. Two + ;; whitespace tokens may be sperated by a comment which is not in + ;; the token stream. + (setcdr (semantic-lex-token-bounds (car semantic-lex-token-stream)) + (match-end 0)) + (semantic-lex-push-token + (semantic-lex-token + 'whitespace (match-beginning 0) (match-end 0))))) + +(define-lex-regex-analyzer semantic-lex-ignore-newline + "Detect and ignore newline tokens. +Use this ONLY if newlines are not whitespace characters (such as when +they are comment end characters)." + "\\s-*\\(\n\\|\\s>\\)" + (setq semantic-lex-end-point (match-end 0))) + +(define-lex-regex-analyzer semantic-lex-whitespace + "Detect and create whitespace tokens." + ;; catch whitespace when needed + "\\s-+" + ;; Language wants whitespaces. Create a token for it. + (if (eq (semantic-lex-token-class (car semantic-lex-token-stream)) + 'whitespace) + ;; Merge whitespace tokens together if they are adjacent. Two + ;; whitespace tokens may be sperated by a comment which is not in + ;; the token stream. + (progn + (setq semantic-lex-end-point (match-end 0)) + (setcdr (semantic-lex-token-bounds (car semantic-lex-token-stream)) + semantic-lex-end-point)) + (semantic-lex-push-token + (semantic-lex-token + 'whitespace (match-beginning 0) (match-end 0))))) + +(define-lex-regex-analyzer semantic-lex-ignore-whitespace + "Detect and skip over whitespace tokens." + ;; catch whitespace when needed + "\\s-+" + ;; Skip over the detected whitespace, do not create a token for it. + (setq semantic-lex-end-point (match-end 0))) + +(define-lex-simple-regex-analyzer semantic-lex-number + "Detect and create number tokens. +See `semantic-lex-number-expression' for details on matching numbers, +and number formats." + semantic-lex-number-expression 'number) + +(define-lex-regex-analyzer semantic-lex-symbol-or-keyword + "Detect and create symbol and keyword tokens." + "\\(\\sw\\|\\s_\\)+" + (semantic-lex-push-token + (semantic-lex-token + (or (semantic-lex-keyword-p (match-string 0)) 'symbol) + (match-beginning 0) (match-end 0)))) + +(define-lex-simple-regex-analyzer semantic-lex-charquote + "Detect and create charquote tokens." + ;; Character quoting characters (ie, \n as newline) + "\\s\\+" 'charquote) + +(define-lex-simple-regex-analyzer semantic-lex-punctuation + "Detect and create punctuation tokens." + "\\(\\s.\\|\\s$\\|\\s'\\)" 'punctuation) + +(define-lex-analyzer semantic-lex-punctuation-type + "Detect and create a punctuation type token. +Recognized punctuations are defined in the current table of lexical +types, as the value of the `punctuation' token type." + (and (looking-at "\\(\\s.\\|\\s$\\|\\s'\\)+") + (let* ((key (match-string 0)) + (pos (match-beginning 0)) + (end (match-end 0)) + (len (- end pos)) + (lst (semantic-lex-type-value "punctuation" t)) + (def (car lst)) ;; default lexical symbol or nil + (lst (cdr lst)) ;; alist of (LEX-SYM . PUNCT-STRING) + (elt nil)) + (if lst + ;; Starting with the longest one, search if the + ;; punctuation string is defined for this language. + (while (and (> len 0) (not (setq elt (rassoc key lst)))) + (setq len (1- len) + key (substring key 0 len)))) + (if elt ;; Return the punctuation token found + (semantic-lex-push-token + (semantic-lex-token (car elt) pos (+ pos len))) + (if def ;; Return a default generic token + (semantic-lex-push-token + (semantic-lex-token def pos end)) + ;; Nothing match + ))))) + +(define-lex-regex-analyzer semantic-lex-paren-or-list + "Detect open parenthesis. +Return either a paren token or a semantic list token depending on +`semantic-lex-current-depth'." + "\\s(" + (if (or (not semantic-lex-maximum-depth) + (< semantic-lex-current-depth semantic-lex-maximum-depth)) + (progn + (setq semantic-lex-current-depth (1+ semantic-lex-current-depth)) + (semantic-lex-push-token + (semantic-lex-token + 'open-paren (match-beginning 0) (match-end 0)))) + (semantic-lex-push-token + (semantic-lex-token + 'semantic-list (match-beginning 0) + (save-excursion + (semantic-lex-unterminated-syntax-protection 'semantic-list + (forward-list 1) + (point)) + ))) + )) + +(define-lex-simple-regex-analyzer semantic-lex-open-paren + "Detect and create an open parenthisis token." + "\\s(" 'open-paren 0 (setq semantic-lex-current-depth (1+ semantic-lex-current-depth))) + +(define-lex-simple-regex-analyzer semantic-lex-close-paren + "Detect and create a close paren token." + "\\s)" 'close-paren 0 (setq semantic-lex-current-depth (1- semantic-lex-current-depth))) + +(define-lex-regex-analyzer semantic-lex-string + "Detect and create a string token." + "\\s\"" + ;; Zing to the end of this string. + (semantic-lex-push-token + (semantic-lex-token + 'string (point) + (save-excursion + (semantic-lex-unterminated-syntax-protection 'string + (forward-sexp 1) + (point)) + )))) + +(define-lex-regex-analyzer semantic-lex-comments + "Detect and create a comment token." + semantic-lex-comment-regex + (save-excursion + (forward-comment 1) + ;; Generate newline token if enabled + (if (bolp) (backward-char 1)) + (setq semantic-lex-end-point (point)) + ;; Language wants comments or want them as whitespaces, + ;; link them together. + (if (eq (semantic-lex-token-class (car semantic-lex-token-stream)) 'comment) + (setcdr (semantic-lex-token-bounds (car semantic-lex-token-stream)) + semantic-lex-end-point) + (semantic-lex-push-token + (semantic-lex-token + 'comment (match-beginning 0) semantic-lex-end-point))))) + +(define-lex-regex-analyzer semantic-lex-comments-as-whitespace + "Detect comments and create a whitespace token." + semantic-lex-comment-regex + (save-excursion + (forward-comment 1) + ;; Generate newline token if enabled + (if (bolp) (backward-char 1)) + (setq semantic-lex-end-point (point)) + ;; Language wants comments or want them as whitespaces, + ;; link them together. + (if (eq (semantic-lex-token-class (car semantic-lex-token-stream)) 'whitespace) + (setcdr (semantic-lex-token-bounds (car semantic-lex-token-stream)) + semantic-lex-end-point) + (semantic-lex-push-token + (semantic-lex-token + 'whitespace (match-beginning 0) semantic-lex-end-point))))) + +(define-lex-regex-analyzer semantic-lex-ignore-comments + "Detect and create a comment token." + semantic-lex-comment-regex + (let ((comment-start-point (point))) + (forward-comment 1) + (if (eq (point) comment-start-point) + ;; In this case our start-skip string failed + ;; to work properly. Lets try and move over + ;; whatever white space we matched to begin + ;; with. + (skip-syntax-forward "-.'" + (save-excursion + (end-of-line) + (point))) + ;; We may need to back up so newlines or whitespace is generated. + (if (bolp) + (backward-char 1))) + (if (eq (point) comment-start-point) + (error "Strange comment syntax prevents lexical analysis")) + (setq semantic-lex-end-point (point)))) + +;;; Comment lexer +;; +;; Predefined lexers that could be used instead of creating new +;; analyers. + +(define-lex semantic-comment-lexer + "A simple lexical analyzer that handles comments. +This lexer will only return comment tokens. It is the default lexer +used by `semantic-find-doc-snarf-comment' to snarf up the comment at +point." + semantic-lex-ignore-whitespace + semantic-lex-ignore-newline + semantic-lex-comments + semantic-lex-default-action) + +;;; Test Lexer +;; +(define-lex semantic-simple-lexer + "A simple lexical analyzer that handles simple buffers. +This lexer ignores comments and whitespace, and will return +syntax as specified by the syntax table." + semantic-lex-ignore-whitespace + semantic-lex-ignore-newline + semantic-lex-number + semantic-lex-symbol-or-keyword + semantic-lex-charquote + semantic-lex-paren-or-list + semantic-lex-close-paren + semantic-lex-string + semantic-lex-ignore-comments + semantic-lex-punctuation + semantic-lex-default-action) + +;;; Analyzers generated from grammar. +;; +;; Some analyzers are hand written. Analyzers created with these +;; functions are generated from the grammar files. + +(defmacro define-lex-keyword-type-analyzer (name doc syntax) + "Define a keyword type analyzer NAME with DOC string. +SYNTAX is the regexp that matches a keyword syntactic expression." + (let ((key (make-symbol "key"))) + `(define-lex-analyzer ,name + ,doc + (and (looking-at ,syntax) + (let ((,key (semantic-lex-keyword-p (match-string 0)))) + (when ,key + (semantic-lex-push-token + (semantic-lex-token + ,key (match-beginning 0) (match-end 0))))))) + )) + +(defmacro define-lex-sexp-type-analyzer (name doc syntax token) + "Define a sexp type analyzer NAME with DOC string. +SYNTAX is the regexp that matches the beginning of the s-expression. +TOKEN is the lexical token returned when SYNTAX matches." + `(define-lex-regex-analyzer ,name + ,doc + ,syntax + (semantic-lex-push-token + (semantic-lex-token + ,token (point) + (save-excursion + (semantic-lex-unterminated-syntax-protection ,token + (forward-sexp 1) + (point)))))) + ) + +(defmacro define-lex-regex-type-analyzer (name doc syntax matches default) + "Define a regexp type analyzer NAME with DOC string. +SYNTAX is the regexp that matches a syntactic expression. +MATCHES is an alist of lexical elements used to refine the syntactic +expression. +DEFAULT is the default lexical token returned when no MATCHES." + (if matches + (let* ((val (make-symbol "val")) + (lst (make-symbol "lst")) + (elt (make-symbol "elt")) + (pos (make-symbol "pos")) + (end (make-symbol "end"))) + `(define-lex-analyzer ,name + ,doc + (and (looking-at ,syntax) + (let* ((,val (match-string 0)) + (,pos (match-beginning 0)) + (,end (match-end 0)) + (,lst ,matches) + ,elt) + (while (and ,lst (not ,elt)) + (if (string-match (cdar ,lst) ,val) + (setq ,elt (caar ,lst)) + (setq ,lst (cdr ,lst)))) + (semantic-lex-push-token + (semantic-lex-token (or ,elt ,default) ,pos ,end)))) + )) + `(define-lex-simple-regex-analyzer ,name + ,doc + ,syntax ,default) + )) + +(defmacro define-lex-string-type-analyzer (name doc syntax matches default) + "Define a string type analyzer NAME with DOC string. +SYNTAX is the regexp that matches a syntactic expression. +MATCHES is an alist of lexical elements used to refine the syntactic +expression. +DEFAULT is the default lexical token returned when no MATCHES." + (if matches + (let* ((val (make-symbol "val")) + (lst (make-symbol "lst")) + (elt (make-symbol "elt")) + (pos (make-symbol "pos")) + (end (make-symbol "end")) + (len (make-symbol "len"))) + `(define-lex-analyzer ,name + ,doc + (and (looking-at ,syntax) + (let* ((,val (match-string 0)) + (,pos (match-beginning 0)) + (,end (match-end 0)) + (,len (- ,end ,pos)) + (,lst ,matches) + ,elt) + ;; Starting with the longest one, search if a lexical + ;; value match a token defined for this language. + (while (and (> ,len 0) (not (setq ,elt (rassoc ,val ,lst)))) + (setq ,len (1- ,len) + ,val (substring ,val 0 ,len))) + (when ,elt ;; Adjust token end position. + (setq ,elt (car ,elt) + ,end (+ ,pos ,len))) + (semantic-lex-push-token + (semantic-lex-token (or ,elt ,default) ,pos ,end)))) + )) + `(define-lex-simple-regex-analyzer ,name + ,doc + ,syntax ,default) + )) + +(defmacro define-lex-block-type-analyzer (name doc syntax matches) + "Define a block type analyzer NAME with DOC string. + +SYNTAX is the regexp that matches block delimiters, typically the +open (`\\\\s(') and close (`\\\\s)') parenthesis syntax classes. + +MATCHES is a pair (OPEN-SPECS . CLOSE-SPECS) that defines blocks. + + OPEN-SPECS is a list of (OPEN-DELIM OPEN-TOKEN BLOCK-TOKEN) elements + where: + + OPEN-DELIM is a string: the block open delimiter character. + + OPEN-TOKEN is the lexical token class associated to the OPEN-DELIM + delimiter. + + BLOCK-TOKEN is the lexical token class associated to the block + that starts at the OPEN-DELIM delimiter. + + CLOSE-SPECS is a list of (CLOSE-DELIM CLOSE-TOKEN) elements where: + + CLOSE-DELIM is a string: the block end delimiter character. + + CLOSE-TOKEN is the lexical token class associated to the + CLOSE-DELIM delimiter. + +Each element in OPEN-SPECS must have a corresponding element in +CLOSE-SPECS. + +The lexer will return a BLOCK-TOKEN token when the value of +`semantic-lex-current-depth' is greater than or equal to the maximum +depth of parenthesis tracking (see also the function `semantic-lex'). +Otherwise it will return OPEN-TOKEN and CLOSE-TOKEN tokens. + +TO DO: Put the following in the developer's guide and just put a +reference here. + +In the grammar: + +The value of a block token must be a string that contains a readable +sexp of the form: + + \"(OPEN-TOKEN CLOSE-TOKEN)\" + +OPEN-TOKEN and CLOSE-TOKEN represent the block delimiters, and must be +lexical tokens of respectively `open-paren' and `close-paren' types. +Their value is the corresponding delimiter character as a string. + +Here is a small example to analyze a parenthesis block: + + %token <block> PAREN_BLOCK \"(LPAREN RPAREN)\" + %token <open-paren> LPAREN \"(\" + %token <close-paren> RPAREN \")\" + +When the lexer encounters the open-paren delimiter \"(\": + + - If the maximum depth of parenthesis tracking is not reached (that + is, current depth < max depth), it returns a (LPAREN start . end) + token, then continue analysis inside the block. Later, when the + corresponding close-paren delimiter \")\" will be encountered, it + will return a (RPAREN start . end) token. + + - If the maximum depth of parenthesis tracking is reached (current + depth >= max depth), it returns the whole parenthesis block as + a (PAREN_BLOCK start . end) token." + (let* ((val (make-symbol "val")) + (lst (make-symbol "lst")) + (elt (make-symbol "elt"))) + `(define-lex-analyzer ,name + ,doc + (and + (looking-at ,syntax) ;; "\\(\\s(\\|\\s)\\)" + (let ((,val (match-string 0)) + (,lst ,matches) + ,elt) + (cond + ((setq ,elt (assoc ,val (car ,lst))) + (if (or (not semantic-lex-maximum-depth) + (< semantic-lex-current-depth semantic-lex-maximum-depth)) + (progn + (setq semantic-lex-current-depth (1+ semantic-lex-current-depth)) + (semantic-lex-push-token + (semantic-lex-token + (nth 1 ,elt) + (match-beginning 0) (match-end 0)))) + (semantic-lex-push-token + (semantic-lex-token + (nth 2 ,elt) + (match-beginning 0) + (save-excursion + (semantic-lex-unterminated-syntax-protection (nth 2 ,elt) + (forward-list 1) + (point))))))) + ((setq ,elt (assoc ,val (cdr ,lst))) + (setq semantic-lex-current-depth (1- semantic-lex-current-depth)) + (semantic-lex-push-token + (semantic-lex-token + (nth 1 ,elt) + (match-beginning 0) (match-end 0)))) + )))) + )) + +;;; Lexical Safety +;; +;; The semantic lexers, unlike other lexers, can throw errors on +;; unbalanced syntax. Since editing is all about changeging test +;; we need to provide a convenient way to protect against syntactic +;; inequalities. + +(defmacro semantic-lex-catch-errors (symbol &rest forms) + "Using SYMBOL, execute FORMS catching lexical errors. +If FORMS results in a call to the parser that throws a lexical error, +the error will be caught here without the buffer's cache being thrown +out of date. +If there is an error, the syntax that failed is returned. +If there is no error, then the last value of FORMS is returned." + (let ((ret (make-symbol "ret")) + (syntax (make-symbol "syntax")) + (start (make-symbol "start")) + (end (make-symbol "end"))) + `(let* ((semantic-lex-unterminated-syntax-end-function + (lambda (,syntax ,start ,end) + (throw ',symbol ,syntax))) + ;; Delete the below when semantic-flex is fully retired. + (semantic-flex-unterminated-syntax-end-function + semantic-lex-unterminated-syntax-end-function) + (,ret (catch ',symbol + (save-excursion + ,@forms + nil)))) + ;; Great Sadness. Assume that FORMS execute within the + ;; confines of the current buffer only! Mark this thing + ;; unparseable iff the special symbol was thrown. This + ;; will prevent future calls from parsing, but will allow + ;; then to still return the cache. + (when ,ret + ;; Leave this message off. If an APP using this fcn wants + ;; a message, they can do it themselves. This cleans up + ;; problems with the idle scheduler obscuring useful data. + ;;(message "Buffer not currently parsable (%S)." ,ret) + (semantic-parse-tree-unparseable)) + ,ret))) +(put 'semantic-lex-catch-errors 'lisp-indent-function 1) + + +;;; Interfacing with edebug +;; +(add-hook + 'edebug-setup-hook + #'(lambda () + + (def-edebug-spec define-lex + (&define name stringp (&rest symbolp)) + ) + (def-edebug-spec define-lex-analyzer + (&define name stringp form def-body) + ) + (def-edebug-spec define-lex-regex-analyzer + (&define name stringp form def-body) + ) + (def-edebug-spec define-lex-simple-regex-analyzer + (&define name stringp form symbolp [ &optional form ] def-body) + ) + (def-edebug-spec define-lex-block-analyzer + (&define name stringp form (&rest form)) + ) + (def-edebug-spec semantic-lex-catch-errors + (symbolp def-body) + ) + + )) + +;;; Compatibility with Semantic 1.x lexical analysis +;; +;; NOTE: DELETE THIS SOMEDAY SOON + +(semantic-alias-obsolete 'semantic-flex-start 'semantic-lex-token-start) +(semantic-alias-obsolete 'semantic-flex-end 'semantic-lex-token-end) +(semantic-alias-obsolete 'semantic-flex-text 'semantic-lex-token-text) +(semantic-alias-obsolete 'semantic-flex-make-keyword-table 'semantic-lex-make-keyword-table) +(semantic-alias-obsolete 'semantic-flex-keyword-p 'semantic-lex-keyword-p) +(semantic-alias-obsolete 'semantic-flex-keyword-put 'semantic-lex-keyword-put) +(semantic-alias-obsolete 'semantic-flex-keyword-get 'semantic-lex-keyword-get) +(semantic-alias-obsolete 'semantic-flex-map-keywords 'semantic-lex-map-keywords) +(semantic-alias-obsolete 'semantic-flex-keywords 'semantic-lex-keywords) +(semantic-alias-obsolete 'semantic-flex-buffer 'semantic-lex-buffer) +(semantic-alias-obsolete 'semantic-flex-list 'semantic-lex-list) + +;; This simple scanner uses the syntax table to generate a stream of +;; simple tokens of the form: +;; +;; (SYMBOL START . END) +;; +;; Where symbol is the type of thing it is. START and END mark that +;; objects boundary. + +(defvar semantic-flex-tokens semantic-lex-tokens + "An alist of of semantic token types. +See variable `semantic-lex-tokens'.") + +(defvar semantic-flex-unterminated-syntax-end-function + (lambda (syntax syntax-start flex-end) flex-end) + "Function called when unterminated syntax is encountered. +This should be set to one function. That function should take three +parameters. The SYNTAX, or type of syntax which is unterminated. +SYNTAX-START where the broken syntax begins. +FLEX-END is where the lexical analysis was asked to end. +This function can be used for languages that can intelligently fix up +broken syntax, or the exit lexical analysis via `throw' or `signal' +when finding unterminated syntax.") + +(defvar semantic-flex-extensions nil + "Buffer local extensions to the lexical analyzer. +This should contain an alist with a key of a regex and a data element of +a function. The function should both move point, and return a lexical +token of the form: + ( TYPE START . END) +nil is also a valid return value. +TYPE can be any type of symbol, as long as it doesn't occur as a +nonterminal in the language definition.") +(make-variable-buffer-local 'semantic-flex-extensions) + +(defvar semantic-flex-syntax-modifications nil + "Changes to the syntax table for this buffer. +These changes are active only while the buffer is being flexed. +This is a list where each element has the form: + (CHAR CLASS) +CHAR is the char passed to `modify-syntax-entry', +and CLASS is the string also passed to `modify-syntax-entry' to define +what syntax class CHAR has.") +(make-variable-buffer-local 'semantic-flex-syntax-modifications) + +(defvar semantic-ignore-comments t + "Default comment handling. +t means to strip comments when flexing. Nil means to keep comments +as part of the token stream.") +(make-variable-buffer-local 'semantic-ignore-comments) + +(defvar semantic-flex-enable-newlines nil + "When flexing, report 'newlines as syntactic elements. +Useful for languages where the newline is a special case terminator. +Only set this on a per mode basis, not globally.") +(make-variable-buffer-local 'semantic-flex-enable-newlines) + +(defvar semantic-flex-enable-whitespace nil + "When flexing, report 'whitespace as syntactic elements. +Useful for languages where the syntax is whitespace dependent. +Only set this on a per mode basis, not globally.") +(make-variable-buffer-local 'semantic-flex-enable-whitespace) + +(defvar semantic-flex-enable-bol nil + "When flexing, report beginning of lines as syntactic elements. +Useful for languages like python which are indentation sensitive. +Only set this on a per mode basis, not globally.") +(make-variable-buffer-local 'semantic-flex-enable-bol) + +(defvar semantic-number-expression semantic-lex-number-expression + "See variable `semantic-lex-number-expression'.") +(make-variable-buffer-local 'semantic-number-expression) + +(defvar semantic-flex-depth 0 + "Default flexing depth. +This specifies how many lists to create tokens in.") +(make-variable-buffer-local 'semantic-flex-depth) + +(defun semantic-flex (start end &optional depth length) + "Using the syntax table, do something roughly equivalent to flex. +Semantically check between START and END. Optional argument DEPTH +indicates at what level to scan over entire lists. +The return value is a token stream. Each element is a list, such of +the form (symbol start-expression . end-expression) where SYMBOL +denotes the token type. +See `semantic-flex-tokens' variable for details on token types. +END does not mark the end of the text scanned, only the end of the +beginning of text scanned. Thus, if a string extends past END, the +end of the return token will be larger than END. To truly restrict +scanning, use `narrow-to-region'. +The last argument, LENGTH specifies that `semantic-flex' should only +return LENGTH tokens." + (message "`semantic-flex' is an obsolete function. Use `define-lex' to create lexers.") + (if (not semantic-flex-keywords-obarray) + (setq semantic-flex-keywords-obarray [ nil ])) + (let ((ts nil) + (pos (point)) + (ep nil) + (curdepth 0) + (cs (if comment-start-skip + (concat "\\(\\s<\\|" comment-start-skip "\\)") + (concat "\\(\\s<\\)"))) + (newsyntax (copy-syntax-table (syntax-table))) + (mods semantic-flex-syntax-modifications) + ;; Use the default depth if it is not specified. + (depth (or depth semantic-flex-depth))) + ;; Update the syntax table + (while mods + (modify-syntax-entry (car (car mods)) (car (cdr (car mods))) newsyntax) + (setq mods (cdr mods))) + (with-syntax-table newsyntax + (goto-char start) + (while (and (< (point) end) (or (not length) (<= (length ts) length))) + (cond + ;; catch beginning of lines when needed. + ;; Must be done before catching any other tokens! + ((and semantic-flex-enable-bol + (bolp) + ;; Just insert a (bol N . N) token in the token stream, + ;; without moving the point. N is the point at the + ;; beginning of line. + (setq ts (cons (cons 'bol (cons (point) (point))) ts)) + nil)) ;; CONTINUE + ;; special extensions, includes whitespace, nl, etc. + ((and semantic-flex-extensions + (let ((fe semantic-flex-extensions) + (r nil)) + (while fe + (if (looking-at (car (car fe))) + (setq ts (cons (funcall (cdr (car fe))) ts) + r t + fe nil + ep (point))) + (setq fe (cdr fe))) + (if (and r (not (car ts))) (setq ts (cdr ts))) + r))) + ;; catch newlines when needed + ((looking-at "\\s-*\\(\n\\|\\s>\\)") + (if semantic-flex-enable-newlines + (setq ep (match-end 1) + ts (cons (cons 'newline + (cons (match-beginning 1) ep)) + ts)))) + ;; catch whitespace when needed + ((looking-at "\\s-+") + (if semantic-flex-enable-whitespace + ;; Language wants whitespaces, link them together. + (if (eq (car (car ts)) 'whitespace) + (setcdr (cdr (car ts)) (match-end 0)) + (setq ts (cons (cons 'whitespace + (cons (match-beginning 0) + (match-end 0))) + ts))))) + ;; numbers + ((and semantic-number-expression + (looking-at semantic-number-expression)) + (setq ts (cons (cons 'number + (cons (match-beginning 0) + (match-end 0))) + ts))) + ;; symbols + ((looking-at "\\(\\sw\\|\\s_\\)+") + (setq ts (cons (cons + ;; Get info on if this is a keyword or not + (or (semantic-lex-keyword-p (match-string 0)) + 'symbol) + (cons (match-beginning 0) (match-end 0))) + ts))) + ;; Character quoting characters (ie, \n as newline) + ((looking-at "\\s\\+") + (setq ts (cons (cons 'charquote + (cons (match-beginning 0) (match-end 0))) + ts))) + ;; Open parens, or semantic-lists. + ((looking-at "\\s(") + (if (or (not depth) (< curdepth depth)) + (progn + (setq curdepth (1+ curdepth)) + (setq ts (cons (cons 'open-paren + (cons (match-beginning 0) (match-end 0))) + ts))) + (setq ts (cons + (cons 'semantic-list + (cons (match-beginning 0) + (save-excursion + (condition-case nil + (forward-list 1) + ;; This case makes flex robust + ;; to broken lists. + (error + (goto-char + (funcall + semantic-flex-unterminated-syntax-end-function + 'semantic-list + start end)))) + (setq ep (point))))) + ts)))) + ;; Close parens + ((looking-at "\\s)") + (setq ts (cons (cons 'close-paren + (cons (match-beginning 0) (match-end 0))) + ts)) + (setq curdepth (1- curdepth))) + ;; String initiators + ((looking-at "\\s\"") + ;; Zing to the end of this string. + (setq ts (cons (cons 'string + (cons (match-beginning 0) + (save-excursion + (condition-case nil + (forward-sexp 1) + ;; This case makes flex + ;; robust to broken strings. + (error + (goto-char + (funcall + semantic-flex-unterminated-syntax-end-function + 'string + start end)))) + (setq ep (point))))) + ts))) + ;; comments + ((looking-at cs) + (if (and semantic-ignore-comments + (not semantic-flex-enable-whitespace)) + ;; If the language doesn't deal with comments nor + ;; whitespaces, ignore them here. + (let ((comment-start-point (point))) + (forward-comment 1) + (if (eq (point) comment-start-point) + ;; In this case our start-skip string failed + ;; to work properly. Lets try and move over + ;; whatever white space we matched to begin + ;; with. + (skip-syntax-forward "-.'" + (save-excursion + (end-of-line) + (point))) + ;;(forward-comment 1) + ;; Generate newline token if enabled + (if (and semantic-flex-enable-newlines + (bolp)) + (backward-char 1))) + (if (eq (point) comment-start-point) + (error "Strange comment syntax prevents lexical analysis")) + (setq ep (point))) + (let ((tk (if semantic-ignore-comments 'whitespace 'comment))) + (save-excursion + (forward-comment 1) + ;; Generate newline token if enabled + (if (and semantic-flex-enable-newlines + (bolp)) + (backward-char 1)) + (setq ep (point))) + ;; Language wants comments or want them as whitespaces, + ;; link them together. + (if (eq (car (car ts)) tk) + (setcdr (cdr (car ts)) ep) + (setq ts (cons (cons tk (cons (match-beginning 0) ep)) + ts)))))) + ;; punctuation + ((looking-at "\\(\\s.\\|\\s$\\|\\s'\\)") + (setq ts (cons (cons 'punctuation + (cons (match-beginning 0) (match-end 0))) + ts))) + ;; unknown token + (t + (error "What is that?"))) + (goto-char (or ep (match-end 0))) + (setq ep nil))) + ;; maybe catch the last beginning of line when needed + (and semantic-flex-enable-bol + (= (point) end) + (bolp) + (setq ts (cons (cons 'bol (cons (point) (point))) ts))) + (goto-char pos) + ;;(message "Flexing muscles...done") + (nreverse ts))) + +(provide 'semantic/lex) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/lex" +;; End: + +;;; semantic/lex.el ends here diff --git a/lisp/cedet/semantic/mru-bookmark.el b/lisp/cedet/semantic/mru-bookmark.el new file mode 100644 index 00000000000..66da681e3b4 --- /dev/null +++ b/lisp/cedet/semantic/mru-bookmark.el @@ -0,0 +1,435 @@ +;;; semantic/mru-bookmark.el --- Automatic bookmark tracking + +;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Using editing hooks, track the most recently visited or poked tags, +;; and keep a list of them, with the current point in from, and sorted +;; by most recently used. +;; +;; I envision this would be used in place of switch-buffers once +;; someone got the hang of it. +;; +;; I'd also like to see this used to provide some nice defaults for +;; other programs where logical destinations or targets are the tags +;; that have been recently edited. +;; +;; Quick Start: +;; +;; M-x global-semantic-mru-bookmark-mode RET +;; +;; < edit some code > +;; +;; C-x B <select a tag name> RET +;; +;; In the above, the history is pre-filled with the tags you recenetly +;; edited in the order you edited them. + +;;; Code: + +(require 'semantic) +(require 'eieio-base) +(require 'ring) + +(declare-function data-debug-new-buffer "data-debug") +(declare-function data-debug-insert-object-slots "eieio-datadebug") +(declare-function semantic-momentary-highlight-tag "semantic/decorate") + +;;; TRACKING CORE +;; +;; Data structure for tracking MRU tag locations + +(defclass semantic-bookmark (eieio-named) + ((tag :initarg :tag + :type semantic-tag + :documentation "The TAG this bookmark belongs to.") + (parent :type (or semantic-tag null) + :documentation "The tag that is the parent of :tag.") + (offset :type number + :documentation "The offset from `tag' start that is +somehow interesting.") + (filename :type string + :documentation "String the tag belongs to. +Set this when the tag gets unlinked from the buffer it belongs to.") + (frequency :type number + :initform 0 + :documentation "Track the frequency this tag is visited.") + (reason :type symbol + :initform t + :documentation + "The reason this tag is interesting. +Nice values are 'edit, 'read, 'jump, and 'mark. + edit - created because the tag text was edited. + read - created because point lingered in tag text. + jump - jumped to another tag from this tag. + mark - created a regular mark in this tag.") + ) + "A single bookmark.") + +(defmethod initialize-instance :AFTER ((sbm semantic-bookmark) &rest fields) + "Initialize the bookmark SBM with details about :tag." + (condition-case nil + (save-excursion + (oset sbm filename (semantic-tag-file-name (oref sbm tag))) + (semantic-go-to-tag (oref sbm tag)) + (oset sbm parent (semantic-current-tag-parent))) + (error (message "Error bookmarking tag."))) + ) + +(defmethod semantic-mrub-visit ((sbm semantic-bookmark)) + "Visit the semantic tag bookmark SBM. +Uses `semantic-go-to-tag' and highlighting." + (require 'semantic/decorate) + (with-slots (tag filename) sbm + ;; Go to the tag + (when (not (semantic-tag-in-buffer-p tag)) + (let ((fn (or (semantic-tag-file-name tag) + filename))) + (set-buffer (find-file-noselect fn)))) + (semantic-go-to-tag (oref sbm tag) (oref sbm parent)) + ;; Go back to the offset. + (condition-case nil + (let ((o (oref sbm offset))) + (forward-char o)) + (error nil)) + ;; make it visible + (switch-to-buffer (current-buffer)) + (semantic-momentary-highlight-tag tag) + )) + +(defmethod semantic-mrub-update ((sbm semantic-bookmark) point reason) + "Update the existing bookmark SBM. +POINT is some important location. +REASON is a symbol. See slot `reason' on `semantic-bookmark'." + (condition-case nil + (progn + (with-slots (tag offset frequency) sbm + (setq offset (- point (semantic-tag-start tag))) + (setq frequency (1+ frequency)) + ) + (oset sbm reason reason)) + ;; This can fail on XEmacs at miscelaneous times. + (error nil)) + ) + +(defmethod semantic-mrub-preflush ((sbm semantic-bookmark)) + "Method called on a tag before the current buffer list of tags is flushed. +If there is a buffer match, unlink the tag." + (let ((tag (oref sbm tag)) + (parent (when (slot-boundp sbm 'parent) + (oref sbm parent)))) + (let ((b (semantic-tag-in-buffer-p tag))) + (when (and b (eq b (current-buffer))) + (semantic--tag-unlink-from-buffer tag))) + + (when parent + (let ((b (semantic-tag-in-buffer-p parent))) + (when (and b (eq b (current-buffer))) + (semantic--tag-unlink-from-buffer parent)))))) + +(defclass semantic-bookmark-ring () + ((ring :initarg :ring + :type ring + :documentation + "List of `semantic-bookmark' objects. +This list is maintained as a list with the first item +being the current location, and the rest being a list of +items that were recently visited.") + (current-index :initform 0 + :type number + :documentation + "The current index into RING for some operation. +User commands use this to move through the ring, or reset.") + ) + "Track the current MRU stack of bookmarks. +We can't use the built-in ring data structure because we need +to delete some items from the ring when we don't have the data.") + +(defvar semantic-mru-bookmark-ring (semantic-bookmark-ring + "Ring" + :ring (make-ring 20)) + "The MRU bookmark ring. +This ring tracks the most recent active tags of interest.") + +(defun semantic-mrub-find-nearby-tag (point) + "Find a nearby tag to be pushed for this current location. +Argument POINT is where to find the tag near." + ;; I thought this was a good idea, but it is not! + ;;(semantic-fetch-tags) ;; Make sure everything is up-to-date. + (let ((tag (semantic-current-tag))) + (when (or (not tag) (semantic-tag-of-class-p tag 'type)) + (let ((nearby (or (semantic-find-tag-by-overlay-next point) + (semantic-find-tag-by-overlay-prev point)))) + (when nearby (setq tag nearby)))) + tag)) + +(defmethod semantic-mrub-push ((sbr semantic-bookmark-ring) point + &optional reason) + "Add a bookmark to the ring SBR from POINT. +REASON is why it is being pushed. See doc for `semantic-bookmark' +for possible reasons. +The resulting bookmark is then sorted within the ring." + (let* ((ring (oref sbr ring)) + (tag (semantic-mrub-find-nearby-tag (point))) + (idx 0)) + (when tag + (while (and (not (ring-empty-p ring)) (< idx (ring-size ring))) + (if (semantic-tag-similar-p (oref (ring-ref ring idx) tag) + tag) + (ring-remove ring idx)) + (setq idx (1+ idx))) + ;; Create a new mark + (let ((sbm (semantic-bookmark (semantic-tag-name tag) + :tag tag))) + ;; Take the mark, and update it for the current state. + (ring-insert ring sbm) + (semantic-mrub-update sbm point reason)) + ))) + +(defun semantic-mrub-cache-flush-fcn () + "Function called in the `semantic-before-toplevel-cache-flush-hook`. +Cause tags in the ring to become unlinked." + (let* ((ring (oref semantic-mru-bookmark-ring ring)) + (len (ring-length ring)) + (idx 0) + ) + (while (< idx len) + (semantic-mrub-preflush (ring-ref ring idx)) + (setq idx (1+ idx))))) + +(add-hook 'semantic-before-toplevel-cache-flush-hook + 'semantic-mrub-cache-flush-fcn) + +;;; EDIT tracker +;; +(defvar semantic-mrub-last-overlay nil + "The last overlay bumped by `semantic-mru-bookmark-change-hook-fcn'.") + +(defun semantic-mru-bookmark-change-hook-fcn (overlay) + "Function set into `semantic-edits-new/move-change-hook's. +Argument OVERLAY is the overlay created to mark the change. +This function pushes tags onto the tag ring." + ;; Dup? + (when (not (eq overlay semantic-mrub-last-overlay)) + (setq semantic-mrub-last-overlay overlay) + (semantic-mrub-push semantic-mru-bookmark-ring + (point) + 'edit))) + +;;; MINOR MODE +;; +;; Tracking minor mode. + +(defcustom global-semantic-mru-bookmark-mode nil + "*If non-nil enable global use of variable `semantic-mru-bookmark-mode'. +When this mode is enabled, changes made to a buffer are highlighted +until the buffer is reparsed." + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic-util-modes + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-mru-bookmark-mode (if val 1 -1)))) + +;;;###autoload +(defun global-semantic-mru-bookmark-mode (&optional arg) + "Toggle global use of option `semantic-mru-bookmark-mode'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-mru-bookmark-mode + (semantic-toggle-minor-mode-globally + 'semantic-mru-bookmark-mode arg))) + +(defcustom semantic-mru-bookmark-mode-hook nil + "*Hook run at the end of function `semantic-mru-bookmark-mode'." + :group 'semantic + :type 'hook) + +(defvar semantic-mru-bookmark-mode-map + (let ((km (make-sparse-keymap))) + (define-key km "\C-xB" 'semantic-mrub-switch-tags) + km) + "Keymap for mru-bookmark minor mode.") + +(defvar semantic-mru-bookmark-mode nil + "Non-nil if mru-bookmark minor mode is enabled. +Use the command `semantic-mru-bookmark-mode' to change this variable.") +(make-variable-buffer-local 'semantic-mru-bookmark-mode) + +(defun semantic-mru-bookmark-mode-setup () + "Setup option `semantic-mru-bookmark-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-mru-bookmark-mode + (if (not (and (featurep 'semantic) (semantic-active-p))) + (progn + ;; Disable minor mode if semantic stuff not available + (setq semantic-mru-bookmark-mode nil) + (error "Buffer %s was not set up for parsing" + (buffer-name))) + (semantic-make-local-hook 'semantic-edits-new-change-hooks) + (add-hook 'semantic-edits-new-change-hooks + 'semantic-mru-bookmark-change-hook-fcn nil t) + (add-hook 'semantic-edits-move-change-hooks + 'semantic-mru-bookmark-change-hook-fcn nil t) + ) + ;; Remove hooks + (remove-hook 'semantic-edits-new-change-hooks + 'semantic-mru-bookmark-change-hook-fcn t) + (remove-hook 'semantic-edits-move-change-hooks + 'semantic-mru-bookmark-change-hook-fcn t) + ) + semantic-mru-bookmark-mode) + +(defun semantic-mru-bookmark-mode (&optional arg) + "Minor mode for tracking tag-based bookmarks automatically. +Tag based bookmarks a tracked based on editing and viewing habits +and can then be navigated via the MRU bookmark keymap. + +\\{semantic-mru-bookmark-mode-map} + +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-mru-bookmark-mode 0 1)))) + (setq semantic-mru-bookmark-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-mru-bookmark-mode))) + (semantic-mru-bookmark-mode-setup) + (run-hooks 'semantic-mru-bookmark-mode-hook) + (if (interactive-p) + (message "mru-bookmark minor mode %sabled" + (if semantic-mru-bookmark-mode "en" "dis"))) + (semantic-mode-line-update) + semantic-mru-bookmark-mode) + +(semantic-add-minor-mode 'semantic-mru-bookmark-mode + "k" + semantic-mru-bookmark-mode-map) + +;;; COMPLETING READ +;; +;; Ask the user for a tag in MRU order. +(defun semantic-mrub-read-history nil + "History of `semantic-mrub-completing-read'.") + +(defun semantic-mrub-ring-to-assoc-list (ring) + "Convert RING into an association list for completion." + (let ((idx 0) + (len (ring-length ring)) + (al nil)) + (while (< idx len) + (let ((r (ring-ref ring idx))) + (setq al (cons (cons (oref r :object-name) r) + al))) + (setq idx (1+ idx))) + (nreverse al))) + +(defun semantic-mrub-completing-read (prompt) + "Do a `completing-read' on elements from the mru bookmark ring. +Argument PROMPT is the promot to use when reading." + (if (ring-empty-p (oref semantic-mru-bookmark-ring ring)) + (error "Semantic Bookmark ring is currently empty")) + (let* ((ring (oref semantic-mru-bookmark-ring ring)) + (ans nil) + (alist (semantic-mrub-ring-to-assoc-list ring)) + (first (cdr (car alist))) + (semantic-mrub-read-history nil) + ) + ;; Don't include the current tag.. only those that come after. + (if (semantic-equivalent-tag-p (oref first tag) + (semantic-current-tag)) + (setq first (cdr (car (cdr alist))))) + ;; Create a fake history list so we don't have to bind + ;; M-p and M-n to our special cause. + (let ((elts (reverse alist))) + (while elts + (setq semantic-mrub-read-history + (cons (car (car elts)) semantic-mrub-read-history)) + (setq elts (cdr elts)))) + (setq semantic-mrub-read-history (nreverse semantic-mrub-read-history)) + + ;; Do the read/prompt + (let ((prompt (if first (format "%s (%s): " prompt + (semantic-format-tag-name + (oref first tag) t) + ) + (concat prompt ": "))) + ) + (setq ans + (completing-read prompt alist nil nil nil 'semantic-mrub-read-history))) + ;; Calculate the return tag. + (if (string= ans "") + (setq ans first) + ;; Return the bookmark object. + (setq ans (assoc ans alist)) + (if ans + (cdr ans) + ;; no match. Custom word. Look it up somwhere? + nil) + ))) + +(defun semantic-mrub-switch-tags (tagmark) + "Switch tags to TAGMARK. +Selects a new tag via promt through the mru tag ring. +Jumps to the tag and highlights it briefly." + (interactive (list (semantic-mrub-completing-read "Switch to tag"))) + (if (not (semantic-bookmark-p tagmark)) + (signal 'wrong-type-argument tagmark)) + + (semantic-mrub-push semantic-mru-bookmark-ring + (point) + 'jump) + (semantic-mrub-visit tagmark) + ) + +;;; Debugging +;; +(defun semantic-adebug-mrub () + "Display a list of items in the MRU bookmarks list. +Useful for debugging mrub problems." + (interactive) + (require 'eieio-datadebug) + (let* ((out semantic-mru-bookmark-ring)) + (data-debug-new-buffer "*TAG RING ADEBUG*") + (data-debug-insert-object-slots out "]") + )) + + +(provide 'semantic/mru-bookmark) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/mru-bookmark" +;; End: + +;;; semantic/mru-bookmark.el ends here diff --git a/lisp/cedet/semantic/sb.el b/lisp/cedet/semantic/sb.el new file mode 100644 index 00000000000..3300d09b3b1 --- /dev/null +++ b/lisp/cedet/semantic/sb.el @@ -0,0 +1,420 @@ +;;; semantic/sb.el --- Semantic tag display for speedbar + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +;;; 2007, 2008 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: +;; +;; Convert a tag table into speedbar buttons. + +;;; TODO: + +;; Use semanticdb to find which semanticdb-table is being used for each +;; file/tag. Replace `semantic-sb-with-tag-buffer' to instead call +;; children with the new `with-mode-local' instead. + +(require 'semantic) +(require 'semantic/format) +(require 'semantic/sort) +(require 'semantic/util) +(require 'speedbar) +(declare-function semanticdb-file-stream "semantic/db") + +(defcustom semantic-sb-autoexpand-length 1 + "*Length of a semantic bucket to autoexpand in place. +This will replace the named bucket that would have usually occured here." + :group 'speedbar + :type 'integer) + +(defcustom semantic-sb-button-format-tag-function 'semantic-format-tag-abbreviate + "*Function called to create the text for a but from a token." + :group 'speedbar + :type semantic-format-tag-custom-list) + +(defcustom semantic-sb-info-format-tag-function 'semantic-format-tag-summarize + "*Function called to create the text for info display from a token." + :group 'speedbar + :type semantic-format-tag-custom-list) + +;;; Code: +;; + +;;; Buffer setting for correct mode manipulation. +(defun semantic-sb-tag-set-buffer (tag) + "Set the current buffer to something associated with TAG. +use the `speedbar-line-file' to get this info if needed." + (if (semantic-tag-buffer tag) + (set-buffer (semantic-tag-buffer tag)) + (let ((f (speedbar-line-file))) + (set-buffer (find-file-noselect f))))) + +(defmacro semantic-sb-with-tag-buffer (tag &rest forms) + "Set the current buffer to the origin of TAG and execute FORMS. +Restore the old current buffer when completed." + `(save-excursion + (semantic-sb-tag-set-buffer ,tag) + ,@forms)) +(put 'semantic-sb-with-tag-buffer 'lisp-indent-function 1) + +;;; Button Generation +;; +;; Here are some button groups: +;; +;; +> Function () +;; @ return_type +;; +( arg1 +;; +| arg2 +;; +) arg3 +;; +;; +> Variable[1] = +;; @ type +;; = default value +;; +;; +> keywrd Type +;; +> type part +;; +;; +> -> click to see additional information + +(define-overloadable-function semantic-sb-tag-children-to-expand (tag) + "For TAG, return a list of children that TAG expands to. +If this returns a value, then a +> icon is created. +If it returns nil, then a => icon is created.") + +(defun semantic-sb-tag-children-to-expand-default (tag) + "For TAG, the children for type, variable, and function classes." + (semantic-sb-with-tag-buffer tag + (semantic-tag-components tag))) + +(defun semantic-sb-one-button (tag depth &optional prefix) + "Insert TAG as a speedbar button at DEPTH. +Optional PREFIX is used to specify special marker characters." + (let* ((class (semantic-tag-class tag)) + (edata (semantic-sb-tag-children-to-expand tag)) + (type (semantic-tag-type tag)) + (abbrev (semantic-sb-with-tag-buffer tag + (funcall semantic-sb-button-format-tag-function tag))) + (start (point)) + (end (progn + (insert (int-to-string depth) ":") + (point)))) + (insert-char ? (1- depth) nil) + (put-text-property end (point) 'invisible nil) + ;; take care of edata = (nil) -- a yucky but hard to clean case + (if (and edata (listp edata) (and (<= (length edata) 1) (not (car edata)))) + (setq edata nil)) + (if (and (not edata) + (member class '(variable function)) + type) + (setq edata t)) + ;; types are a bit unique. Variable types can have special meaning. + (if edata + (speedbar-insert-button (if prefix (concat " +" prefix) " +>") + 'speedbar-button-face + 'speedbar-highlight-face + 'semantic-sb-show-extra + tag t) + (speedbar-insert-button (if prefix (concat " " prefix) " =>") + nil nil nil nil t)) + (speedbar-insert-button abbrev + 'speedbar-tag-face + 'speedbar-highlight-face + 'semantic-sb-token-jump + tag t) + ;; This is very bizarre. When this was just after the insertion + ;; of the depth: text, the : would get erased, but only for the + ;; auto-expanded short- buckets. Move back for a later version + ;; version of Emacs 21 CVS + (put-text-property start end 'invisible t) + )) + +(defun semantic-sb-speedbar-data-line (depth button text &optional + text-fun text-data) + "Insert a semantic token data element. +DEPTH is the current depth. BUTTON is the text for the button. +TEXT is the actual info with TEXT-FUN to occur when it happens. +Argument TEXT-DATA is the token data to pass to TEXT-FUN." + (let ((start (point)) + (end (progn + (insert (int-to-string depth) ":") + (point)))) + (put-text-property start end 'invisible t) + (insert-char ? depth nil) + (put-text-property end (point) 'invisible nil) + (speedbar-insert-button button nil nil nil nil t) + (speedbar-insert-button text + 'speedbar-tag-face + (if text-fun 'speedbar-highlight-face) + text-fun text-data t) + )) + +(defun semantic-sb-maybe-token-to-button (obj indent &optional + prefix modifiers) + "Convert OBJ, which was returned from the semantic parser, into a button. +This OBJ might be a plain string (simple type or untyped variable) +or a complete tag. +Argument INDENT is the indentation used when making the button. +Optional PREFIX is the character to use when marking the line. +Optional MODIFIERS is additional text needed for variables." + (let ((myprefix (or prefix ">"))) + (if (stringp obj) + (semantic-sb-speedbar-data-line indent myprefix obj) + (if (listp obj) + (progn + (if (and (stringp (car obj)) + (= (length obj) 1)) + (semantic-sb-speedbar-data-line indent myprefix + (concat + (car obj) + (or modifiers ""))) + (semantic-sb-one-button obj indent prefix))))))) + +(defun semantic-sb-insert-details (tag indent) + "Insert details about TAG at level INDENT." + (let ((tt (semantic-tag-class tag)) + (type (semantic-tag-type tag))) + (cond ((eq tt 'type) + (let ((parts (semantic-tag-type-members tag)) + (newparts nil)) + ;; Lets expect PARTS to be a list of either strings, + ;; or variable tokens. + (when (semantic-tag-p (car parts)) + ;; Bucketize into groups + (semantic-sb-with-tag-buffer (car parts) + (setq newparts (semantic-bucketize parts))) + (when (> (length newparts) semantic-sb-autoexpand-length) + ;; More than one bucket, insert inline + (semantic-sb-insert-tag-table (1- indent) newparts) + (setq parts nil)) + ;; Dump the strings in. + (while parts + (semantic-sb-maybe-token-to-button (car parts) indent) + (setq parts (cdr parts)))))) + ((eq tt 'variable) + (if type + (semantic-sb-maybe-token-to-button type indent "@")) + (let ((default (semantic-tag-variable-default tag))) + (if default + (semantic-sb-maybe-token-to-button default indent "="))) + ) + ((eq tt 'function) + (if type + (semantic-sb-speedbar-data-line + indent "@" + (if (stringp type) type + (semantic-tag-name type)))) + ;; Arguments to the function + (let ((args (semantic-tag-function-arguments tag))) + (if (and args (car args)) + (progn + (semantic-sb-maybe-token-to-button (car args) indent "(") + (setq args (cdr args)) + (while (> (length args) 1) + (semantic-sb-maybe-token-to-button (car args) + indent + "|") + (setq args (cdr args))) + (if args + (semantic-sb-maybe-token-to-button + (car args) indent ")")) + )))) + (t + (let ((components + (save-excursion + (when (and (semantic-tag-overlay tag) + (semantic-tag-buffer tag)) + (set-buffer (semantic-tag-buffer tag))) + (semantic-sb-tag-children-to-expand tag)))) + ;; Well, it wasn't one of the many things we expect. + ;; Lets just insert them in with no decoration. + (while components + (semantic-sb-one-button (car components) indent) + (setq components (cdr components))) + )) + ) + )) + +(defun semantic-sb-detail-parent () + "Return the first parent token of the current line that includes a location." + (save-excursion + (beginning-of-line) + (let ((dep (if (looking-at "[0-9]+:") + (1- (string-to-number (match-string 0))) + 0))) + (re-search-backward (concat "^" + (int-to-string dep) + ":") + nil t)) + (beginning-of-line) + (if (looking-at "[0-9]+: +[-+][>()@|] \\([^\n]+\\)$") + (let ((prop nil)) + (goto-char (match-beginning 1)) + (setq prop (get-text-property (point) 'speedbar-token)) + (if (semantic-tag-with-position-p prop) + prop + (semantic-sb-detail-parent))) + nil))) + +(defun semantic-sb-show-extra (text token indent) + "Display additional information about the token as an expansion. +TEXT TOKEN and INDENT are the details." + (cond ((string-match "+" text) ;we have to expand this file + (speedbar-change-expand-button-char ?-) + (speedbar-with-writable + (save-excursion + (end-of-line) (forward-char 1) + (save-restriction + (narrow-to-region (point) (point)) + ;; Add in stuff specific to this type of token. + (semantic-sb-insert-details token (1+ indent)))))) + ((string-match "-" text) ;we have to contract this node + (speedbar-change-expand-button-char ?+) + (speedbar-delete-subblock indent)) + (t (error "Ooops... not sure what to do"))) + (speedbar-center-buffer-smartly)) + +(defun semantic-sb-token-jump (text token indent) + "Jump to the location specified in token. +TEXT TOKEN and INDENT are the details." + (let ((file + (or + (cond ((fboundp 'speedbar-line-path) + (speedbar-line-directory indent)) + ((fboundp 'speedbar-line-directory) + (speedbar-line-directory indent))) + ;; If speedbar cannot figure this out, extract the filename from + ;; the token. True for Analysis mode. + (semantic-tag-file-name token))) + (parent (semantic-sb-detail-parent))) + (let ((f (selected-frame))) + (dframe-select-attached-frame speedbar-frame) + (run-hooks 'speedbar-before-visiting-tag-hook) + (select-frame f)) + ;; Sometimes FILE may be nil here. If you are debugging a problem + ;; when this happens, go back and figure out why FILE is nil and try + ;; and fix the source. + (speedbar-find-file-in-frame file) + (save-excursion (speedbar-stealthy-updates)) + (semantic-go-to-tag token parent) + (switch-to-buffer (current-buffer)) + ;; Reset the timer with a new timeout when cliking a file + ;; in case the user was navigating directories, we can cancel + ;; that other timer. + ;; (speedbar-set-timer dframe-update-speed) + ;;(recenter) + (speedbar-maybee-jump-to-attached-frame) + (run-hooks 'speedbar-visiting-tag-hook))) + +(defun semantic-sb-expand-group (text token indent) + "Expand a group which has semantic tokens. +TEXT TOKEN and INDENT are the details." + (cond ((string-match "+" text) ;we have to expand this file + (speedbar-change-expand-button-char ?-) + (speedbar-with-writable + (save-excursion + (end-of-line) (forward-char 1) + (save-restriction + (narrow-to-region (point-min) (point)) + (semantic-sb-buttons-plain (1+ indent) token))))) + ((string-match "-" text) ;we have to contract this node + (speedbar-change-expand-button-char ?+) + (speedbar-delete-subblock indent)) + (t (error "Ooops... not sure what to do"))) + (speedbar-center-buffer-smartly)) + +(defun semantic-sb-buttons-plain (level tokens) + "Create buttons at LEVEL using TOKENS." + (let ((sordid (speedbar-create-tag-hierarchy tokens))) + (while sordid + (cond ((null (car-safe sordid)) nil) + ((consp (car-safe (cdr-safe (car-safe sordid)))) + ;; A group! + (speedbar-make-tag-line 'curly ?+ 'semantic-sb-expand-group + (cdr (car sordid)) + (car (car sordid)) + nil nil 'speedbar-tag-face + level)) + (t ;; Assume that this is a token. + (semantic-sb-one-button (car sordid) level))) + (setq sordid (cdr sordid))))) + +(defun semantic-sb-insert-tag-table (level table) + "At LEVEL, insert the tag table TABLE. +Use arcane knowledge about the semantic tokens in the tagged elements +to create much wiser decisions about how to sort and group these items." + (semantic-sb-buttons level table)) + +(defun semantic-sb-buttons (level lst) + "Create buttons at LEVEL using LST sorting into type buckets." + (save-restriction + (narrow-to-region (point-min) (point)) + (let (tmp) + (while lst + (setq tmp (car lst)) + (if (cdr tmp) + (if (<= (length (cdr tmp)) semantic-sb-autoexpand-length) + (semantic-sb-buttons-plain (1+ level) (cdr tmp)) + (speedbar-make-tag-line 'curly ?+ 'semantic-sb-expand-group + (cdr tmp) + (car (car lst)) + nil nil 'speedbar-tag-face + (1+ level)))) + (setq lst (cdr lst)))))) + +(defun semantic-sb-fetch-tag-table (file) + "Load FILE into a buffer, and generate tags using the Semantic parser. +Returns the tag list, or t for an error." + (let ((out nil)) + (if (and (featurep 'semantic/db) + (semanticdb-minor-mode-p) + (not speedbar-power-click) + ;; If the database is loaded and running, try to get + ;; tokens from it. + (setq out (semanticdb-file-stream file))) + ;; Successful DB query. + nil + ;; No database, do it the old way. + (save-excursion + (set-buffer (find-file-noselect file)) + (if (or (not (featurep 'semantic)) + (not semantic--parse-table)) + (setq out t) + (if speedbar-power-click (semantic-clear-toplevel-cache)) + (setq out (semantic-fetch-tags))))) + (if (listp out) + (condition-case nil + (progn + ;; This brings externally defind methods into + ;; their classes, and creates meta classes for + ;; orphans. + (setq out (semantic-adopt-external-members out)) + ;; Dump all the tokens into buckets. + (semantic-sb-with-tag-buffer (car out) + (semantic-bucketize out))) + (error t)) + t))) + +;; Link ourselves into the tagging process. +(add-to-list 'speedbar-dynamic-tags-function-list + '(semantic-sb-fetch-tag-table . semantic-sb-insert-tag-table)) + +(provide 'semantic/sb) + +;;; semantic/sb.el ends here diff --git a/lisp/cedet/semantic/scope.el b/lisp/cedet/semantic/scope.el new file mode 100644 index 00000000000..30b394d4a07 --- /dev/null +++ b/lisp/cedet/semantic/scope.el @@ -0,0 +1,816 @@ +;;; semantic/scope.el --- Analyzer Scope Calculations + +;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Calculate information about the current scope. +;; +;; Manages the current scope as a structure that can be cached on a +;; per-file basis and recycled between different occurances of +;; analysis on different parts of a file. +;; +;; Pattern for Scope Calculation +;; +;; Step 1: Calculate DataTypes in Scope: +;; +;; a) What is in scope via using statements or local namespaces +;; b) Lineage of current context. Some names drawn from step 1. +;; +;; Step 2: Convert type names into lists of concrete tags +;; +;; a) Convert each datatype into the real datatype tag +;; b) Convert namespaces into the list of contents of the namespace. +;; c) Merge all existing scopes together into one search list. +;; +;; Step 3: Local variables +;; +;; a) Local variables are in the master search list. +;; + +(require 'semantic/db) +(require 'semantic/analyze/fcn) +(require 'semantic/ctxt) + +(eval-when-compile (require 'semantic/find)) + +(declare-function data-debug-show "eieio-datadebug") +(declare-function semantic-analyze-find-tag "semantic/analyze") +(declare-function semantic-analyze-princ-sequence "semantic/analyze") +(declare-function semanticdb-typecache-merge-streams "semantic/db-typecache") +(declare-function semanticdb-typecache-add-dependant "semantic/db-typecache") + +;;; Code: + +(defclass semantic-scope-cache (semanticdb-abstract-cache) + ((tag :initform nil + :documentation + "The tag this scope was calculated for.") + (scopetypes :initform nil + :documentation + "The list of types currently in scope. +For C++, this would contain anonymous namespaces known, and +anything labled by a `using' statement.") + (parents :initform nil + :documentation + "List of parents in scope w/in the body of this function. +Presumably, the members of these parent classes are available for access +based on private:, or public: style statements.") + (parentinheritance :initform nil + :documentation "Alist of parents by inheritance. +Each entry is ( PARENT . PROTECTION ), where PARENT is a type, and +PROTECTION is a symbol representing the level of inheritance, such as 'private, or 'protected.") + (scope :initform nil + :documentation + "Items in scope due to the scopetypes or parents.") + (fullscope :initform nil + :documentation + "All the other stuff on one master list you can search.") + (localargs :initform nil + :documentation + "The arguments to the function tag.") + (localvar :initform nil + :documentation + "The local variables.") + (typescope :initform nil + :documentation + "Slot to save intermediate scope while metatypes are dereferenced.") + ) + "Cache used for storage of the current scope by the Semantic Analyzer. +Saves scoping information between runs of the analyzer.") + +;;; METHODS +;; +;; Methods for basic management of the structure in semanticdb. +;; +(defmethod semantic-reset ((obj semantic-scope-cache)) + "Reset OBJ back to it's empty settings." + (oset obj tag nil) + (oset obj scopetypes nil) + (oset obj parents nil) + (oset obj parentinheritance nil) + (oset obj scope nil) + (oset obj fullscope nil) + (oset obj localargs nil) + (oset obj localvar nil) + (oset obj typescope nil) + ) + +(defmethod semanticdb-synchronize ((cache semantic-scope-cache) + new-tags) + "Synchronize a CACHE with some NEW-TAGS." + (semantic-reset cache)) + + +(defmethod semanticdb-partial-synchronize ((cache semantic-scope-cache) + new-tags) + "Synchronize a CACHE with some changed NEW-TAGS." + ;; If there are any includes or datatypes changed, then clear. + (if (or (semantic-find-tags-by-class 'include new-tags) + (semantic-find-tags-by-class 'type new-tags) + (semantic-find-tags-by-class 'using new-tags)) + (semantic-reset cache)) + ) + +(defun semantic-scope-reset-cache () + "Get the current cached scope, and reset it." + (when semanticdb-current-table + (let ((co (semanticdb-cache-get semanticdb-current-table + semantic-scope-cache))) + (semantic-reset co)))) + +(defmethod semantic-scope-set-typecache ((cache semantic-scope-cache) + types-in-scope) + "Set the :typescope property on CACHE to some types. +TYPES-IN-SCOPE is a list of type tags whos members are +currently in scope. For each type in TYPES-IN-SCOPE, +add those members to the types list. +If nil, then the typescope is reset." + (let ((newts nil)) ;; New Type Scope + (dolist (onetype types-in-scope) + (setq newts (append (semantic-tag-type-members onetype) + newts)) + ) + (oset cache typescope newts))) + +;;; TAG SCOPES +;; +;; These fcns should be used by search routines that return a single +;; tag which, in turn, may have come from a deep scope. The scope +;; will be attached to the tag. Thus, in future scope based calls, a +;; tag can be passed in and a scope derived from it. + +(defun semantic-scope-tag-clone-with-scope (tag scopetags) + "Close TAG, and return it. Add SCOPETAGS as a tag-local scope. +Stores the SCOPETAGS as a set of tag properties on the cloned tag." + (let ((clone (semantic-tag-clone tag)) + ) + (semantic--tag-put-property clone 'scope scopetags) + )) + +(defun semantic-scope-tag-get-scope (tag) + "Get from TAG the list of tags comprising the scope from TAG." + (semantic--tag-get-property tag 'scope)) + +;;; SCOPE UTILITIES +;; +;; Functions that do the main scope calculations + + +(define-overloadable-function semantic-analyze-scoped-types (position) + "Return a list of types currently in scope at POSITION. +This is based on what tags exist at POSITION, and any associated +types available.") + +(defun semantic-analyze-scoped-types-default (position) + "Return a list of types currently in scope at POSITION. +Use `semantic-ctxt-scoped-types' to find types." + (require 'semantic/db-typecache) + (save-excursion + (goto-char position) + (let ((code-scoped-types nil)) + ;; Lets ask if any types are currently scoped. Scoped + ;; classes and types provide their public methods and types + ;; in source code, but are unrelated hierarchically. + (let ((sp (semantic-ctxt-scoped-types))) + (while sp + ;; Get this thing as a tag + (let ((tmp (cond + ((stringp (car sp)) + (semanticdb-typecache-find (car sp))) + ;(semantic-analyze-find-tag (car sp) 'type)) + ((semantic-tag-p (car sp)) + (if (semantic-analyze-tag-prototype-p (car sp)) + (semanticdb-typecache-find (semantic-tag-name (car sp))) + ;;(semantic-analyze-find-tag (semantic-tag-name (car sp)) 'type) + (car sp))) + (t nil)))) + (when tmp + (setq code-scoped-types + (cons tmp code-scoped-types)))) + (setq sp (cdr sp)))) + (setq code-scoped-types (nreverse code-scoped-types)) + + (when code-scoped-types + (semanticdb-typecache-merge-streams code-scoped-types nil)) + + ))) + +;;------------------------------------------------------------ +(define-overloadable-function semantic-analyze-scope-nested-tags (position scopedtypes) + "Return a list of types in order of nesting for the context of POSITION. +If POSITION is in a method with a named parent, find that parent, and +identify it's scope via overlay instead. +Optional SCOPETYPES are additional scoped entities in which our parent might +be found.") + +(defun semantic-analyze-scope-nested-tags-default (position scopetypes) + "Return a list of types in order of nesting for the context of POSITION. +If POSITION is in a method with a named parent, find that parent, and +identify it's scope via overlay instead. +Optional SCOPETYPES are additional scoped entities in which our parent might +be found. +This only finds ONE immediate parent by name. All other parents returned +are from nesting data types." + (require 'semantic/analyze) + (save-excursion + (if position (goto-char position)) + (let* ((stack (reverse (semantic-find-tag-by-overlay (point)))) + (tag (car stack)) + (pparent (car (cdr stack))) + (returnlist nil) + ) + ;; In case of arg lists or some-such, throw out non-types. + (while (and stack (not (semantic-tag-of-class-p pparent 'type))) + (setq stack (cdr stack) + pparent (car (cdr stack)))) + + ;; Step 1: + ;; Analyze the stack of tags we are nested in as parents. + ;; + + ;; If we have a pparent tag, lets go there + ;; an analyze that stack of tags. + (when (and pparent (semantic-tag-with-position-p pparent)) + (semantic-go-to-tag pparent) + (setq stack (semantic-find-tag-by-overlay (point))) + ;; Step one, find the merged version of stack in the typecache. + (let* ((stacknames (reverse (mapcar 'semantic-tag-name stack))) + (tc nil) + ) + ;; @todo - can we use the typecache ability to + ;; put a scope into a tag to do this? + (while (and stacknames + (setq tc (semanticdb-typecache-find + (reverse stacknames)))) + (setq returnlist (cons tc returnlist) + stacknames (cdr stacknames))) + (when (not returnlist) + ;; When there was nothing from the typecache, then just + ;; use what's right here. + (setq stack (reverse stack)) + ;; Add things to STACK until we cease finding tags of class type. + (while (and stack (eq (semantic-tag-class (car stack)) 'type)) + ;; Otherwise, just add this to the returnlist. + (setq returnlist (cons (car stack) returnlist)) + (setq stack (cdr stack))) + + (setq returnlist (nreverse returnlist)) + )) + ) + + ;; Only do this level of analysis for functions. + (when (eq (semantic-tag-class tag) 'function) + ;; Step 2: + ;; If the function tag itself has a "parent" by name, then that + ;; parent will exist in the scope we just calculated, so look it + ;; up now. + ;; + (let ((p (semantic-tag-function-parent tag))) + (when p + ;; We have a parent, search for it. + (let* ((searchnameraw (cond ((stringp p) p) + ((semantic-tag-p p) + (semantic-tag-name p)) + ((and (listp p) (stringp (car p))) + (car p)))) + (searchname (semantic-analyze-split-name searchnameraw)) + (snlist (if (consp searchname) + searchname + (list searchname))) + (fullsearchname nil) + + (miniscope (semantic-scope-cache "mini")) + ptag) + + ;; Find the next entry in the refereneced type for + ;; our function, and append to return list till our + ;; returnlist is empty. + (while snlist + (setq fullsearchname + (append (mapcar 'semantic-tag-name returnlist) + (list (car snlist)))) ;; Next one + (setq ptag + (semanticdb-typecache-find fullsearchname)) + + (when (or (not ptag) + (not (semantic-tag-of-class-p ptag 'type))) + (let ((rawscope + (apply 'append + (mapcar 'semantic-tag-type-members + (cons (car returnlist) scopetypes) + ))) + ) + (oset miniscope parents returnlist) ;; Not really accurate, but close + (oset miniscope scope rawscope) + (oset miniscope fullscope rawscope) + (setq ptag + (semantic-analyze-find-tag searchnameraw + 'type + miniscope + )) + )) + + (when ptag + (when (and (not (semantic-tag-p ptag)) + (semantic-tag-p (car ptag))) + (setq ptag (car ptag))) + (setq returnlist (append returnlist (list ptag))) + ) + + (setq snlist (cdr snlist))) + (setq returnlist returnlist) + ))) + ) + returnlist + ))) + +(define-overloadable-function semantic-analyze-scope-lineage-tags (parents scopedtypes) + "Return the full lineage of tags from PARENTS. +The return list is of the form ( TAG . PROTECTION ), where TAG is a tag, +and PROTECTION is the level of protection offered by the relationship. +Optional SCOPETYPES are additional scoped entities in which our parent might +be found.") + +(defun semantic-analyze-scope-lineage-tags-default (parents scopetypes) + "Return the full lineage of tags from PARENTS. +The return list is of the form ( TAG . PROTECTION ), where TAG is a tag, +and PROTECTION is the level of protection offered by the relationship. +Optional SCOPETYPES are additional scoped entities in which our parent might +be found." + (let ((lineage nil) + (miniscope (semantic-scope-cache "mini")) + ) + (oset miniscope parents parents) + (oset miniscope scope scopetypes) + (oset miniscope fullscope scopetypes) + + (dolist (slp parents) + (semantic-analyze-scoped-inherited-tag-map + slp (lambda (newparent) + (let* ((pname (semantic-tag-name newparent)) + (prot (semantic-tag-type-superclass-protection slp pname)) + (effectiveprot (cond ((eq prot 'public) + ;; doesn't provide access to private slots? + 'protected) + (t prot)))) + (push (cons newparent effectiveprot) lineage) + )) + miniscope)) + + lineage)) + + +;;------------------------------------------------------------ + +(define-overloadable-function semantic-analyze-scoped-tags (typelist parentlist) + "Return accessable tags when TYPELIST and PARENTLIST is in scope. +Tags returned are not in the global name space, but are instead +scoped inside a class or namespace. Such items can be referenced +without use of \"object.function()\" style syntax due to an +implicit \"object\".") + +(defun semantic-analyze-scoped-tags-default (typelist halfscope) + "Return accessable tags when TYPELIST and HALFSCOPE is in scope. +HALFSCOPE is the current scope partially initialized. +Tags returned are not in the global name space, but are instead +scoped inside a class or namespace. Such items can be referenced +without use of \"object.function()\" style syntax due to an +implicit \"object\"." + (let ((typelist2 nil) + (currentscope nil) + (parentlist (oref halfscope parents)) + (miniscope halfscope) + ) + ;; Loop over typelist, and find and merge all namespaces matching + ;; the names in typelist. + (while typelist + (let ((tt (semantic-tag-type (car typelist)))) + (when (and (stringp tt) (string= tt "namespace")) + ;; By using the typecache, our namespaces are pre-merged. + (setq typelist2 (cons (car typelist) typelist2)) + )) + (setq typelist (cdr typelist))) + + ;; Loop over the types (which should be sorted by postion + ;; adding to the scopelist as we go, and using the scopelist + ;; for additional searching! + (while typelist2 + (oset miniscope scope currentscope) + (oset miniscope fullscope currentscope) + (setq currentscope (append + (semantic-analyze-scoped-type-parts (car typelist2) + miniscope) + currentscope)) + (setq typelist2 (cdr typelist2))) + + ;; Collect all the types (class, etc) that are in our heratage. + ;; These are types that we can extract members from, not those + ;; delclared in using statements, or the like. + ;; Get the PARENTS including nesting scope for this location. + (while parentlist + (oset miniscope scope currentscope) + (oset miniscope fullscope currentscope) + (setq currentscope (append + (semantic-analyze-scoped-type-parts (car parentlist) + miniscope) + currentscope)) + (setq parentlist (cdr parentlist))) + + ;; Loop over all the items, and collect any type constants. + (let ((constants nil)) + (dolist (T currentscope) + (setq constants (append constants + (semantic-analyze-type-constants T))) + ) + + (setq currentscope (append currentscope constants))) + + currentscope)) + +;;------------------------------------------------------------ +(define-overloadable-function semantic-analyze-scope-calculate-access (type scope) + "Calculate the access class for TYPE as defined by the current SCOPE. +Access is related to the :parents in SCOPE. If type is a member of SCOPE +then access would be 'private. If TYPE is inherited by a member of SCOPE, +the access would be 'protected. Otherwise, access is 'public") + +(defun semantic-analyze-scope-calculate-access-default (type scope) + "Calculate the access class for TYPE as defined by the current SCOPE." + (cond ((semantic-scope-cache-p scope) + (let ((parents (oref scope parents)) + (parentsi (oref scope parentinheritance)) + ) + (catch 'moose + ;; Investigate the parent, and see how it relates to type. + ;; If these tags are basically the same, then we have full access. + (dolist (p parents) + (when (semantic-tag-similar-p type p) + (throw 'moose 'private)) + ) + ;; Look to see if type is in our list of inherited parents. + (dolist (pi parentsi) + ;; pi is a cons cell ( PARENT . protection) + (let ((pip (car pi)) + (piprot (cdr pi))) + (when (semantic-tag-similar-p type pip) + (throw 'moose + ;; protection via inheritance means to pull out different + ;; bits based on protection labels in an opposite way. + (cdr (assoc piprot + '((public . private) + (protected . protected) + (private . public)))) + ))) + ) + ;; Not in our parentage. Is type a FRIEND? + (let ((friends (semantic-find-tags-by-class 'friend (semantic-tag-type-members type)))) + (dolist (F friends) + (dolist (pi parents) + (if (string= (semantic-tag-name F) (semantic-tag-name pi)) + (throw 'moose 'private)) + ))) + ;; Found nothing, return public + 'public) + )) + (t 'public))) + +(defun semantic-completable-tags-from-type (type) + "Return a list of slots that are valid completions from the list of SLOTS. +If a tag in SLOTS has a named parent, then that implies that the +tag is not something you can complete from within TYPE." + (let ((allslots (semantic-tag-components type)) + (leftover nil) + ) + (dolist (S allslots) + (when (or (not (semantic-tag-of-class-p S 'function)) + (not (semantic-tag-function-parent S))) + (setq leftover (cons S leftover))) + ) + (nreverse leftover))) + +(defun semantic-analyze-scoped-type-parts (type &optional scope noinherit protection) + "Return all parts of TYPE, a tag representing a TYPE declaration. +SCOPE is the scope object. +NOINHERIT turns off searching of inherited tags. +PROTECTION specifies the type of access requested, such as 'public or 'private." + (if (not type) + nil + (let* ((access (semantic-analyze-scope-calculate-access type scope)) + ;; SLOTS are the slots directly a part of TYPE. + (allslots (semantic-completable-tags-from-type type)) + (slots (semantic-find-tags-by-scope-protection + access + type allslots)) + (fname (semantic-tag-file-name type)) + ;; EXTMETH are externally defined methods that are still + ;; a part of this class. + + ;; @TODO - is this line needed?? Try w/out for a while + ;; @note - I think C++ says no. elisp might, but methods + ;; look like defuns, so it makes no difference. + (extmeth nil) ; (semantic-tag-external-member-children type t)) + + ;; INHERITED are tags found in classes that our TYPE tag + ;; inherits from. Do not do this if it was not requested. + (inherited (when (not noinherit) + (semantic-analyze-scoped-inherited-tags type scope + access))) + ) + (when (not (semantic-tag-in-buffer-p type)) + (let ((copyslots nil)) + (dolist (TAG slots) + ;;(semantic--tag-put-property TAG :filename fname) + (if (semantic-tag-file-name TAG) + ;; If it has a filename, just go with it... + (setq copyslots (cons TAG copyslots)) + ;; Otherwise, copy the tag w/ the guessed filename. + (setq copyslots (cons (semantic-tag-copy TAG nil fname) + copyslots))) + ) + (setq slots (nreverse copyslots)) + )) + ;; Flatten the database output. + (append slots extmeth inherited) + ))) + +(defun semantic-analyze-scoped-inherited-tags (type scope access) + "Return all tags that TYPE inherits from. +Argument SCOPE specify additional tags that are in scope +whose tags can be searched when needed, OR it may be a scope object. +ACCESS is the level of access we filter on child supplied tags. +For langauges with protection on specific methods or slots, +it should strip out those not accessable by methods of TYPE. +An ACCESS of 'public means not in a method of a subclass of type. +A value of 'private means we can access private parts of the originating +type." + (let ((ret nil)) + (semantic-analyze-scoped-inherited-tag-map + type (lambda (p) + (let* ((pname (semantic-tag-name p)) + (protection (semantic-tag-type-superclass-protection + type pname)) + ) + (if (and (eq access 'public) (not (eq protection 'public))) + nil ;; Don't do it. + + ;; We can get some parts of this type. + (setq ret (nconc ret + ;; Do not pull in inherited parts here. Those + ;; will come via the inherited-tag-map fcn + (semantic-analyze-scoped-type-parts + p scope t protection)) + )))) + scope) + ret)) + +(defun semantic-analyze-scoped-inherited-tag-map (type fcn scope) + "Map all parents of TYPE to FCN. Return tags of all the types. +Argument SCOPE specify additional tags that are in scope +whose tags can be searched when needed, OR it may be a scope object." + (require 'semantic/analyze) + (let* (;; PARENTS specifies only the superclasses and not + ;; interfaces. Inheriting from an interfaces implies + ;; you have a copy of all methods locally. I think. + (parents (semantic-tag-type-superclasses type)) + ps pt + (tmpscope scope) + ) + (save-excursion + + ;; Create a SCOPE just for looking up the parent based on where + ;; the parent came from. + ;; + ;; @TODO - Should we cache these mini-scopes around in Emacs + ;; for recycling later? Should this become a helpful + ;; extra routine? + (when (and parents (semantic-tag-with-position-p type)) + ;; If TYPE has a position, go there and get the scope. + (semantic-go-to-tag type) + + ;; We need to make a mini scope, and only include the misc bits + ;; that will help in finding the parent. We don't really need + ;; to do any of the stuff related to variables and what-not. + (setq tmpscope (semantic-scope-cache "mini")) + (let* (;; Step 1: + (scopetypes (semantic-analyze-scoped-types (point))) + (parents (semantic-analyze-scope-nested-tags (point) scopetypes)) + ;;(parentinherited (semantic-analyze-scope-lineage-tags parents scopetypes)) + (lscope nil) + ) + (oset tmpscope scopetypes scopetypes) + (oset tmpscope parents parents) + ;;(oset tmpscope parentinheritance parentinherited) + + (when (or scopetypes parents) + (setq lscope (semantic-analyze-scoped-tags scopetypes tmpscope)) + (oset tmpscope scope lscope)) + (oset tmpscope fullscope (append scopetypes lscope parents)) + )) + ;; END creating tmpscope + + ;; Look up each parent one at a time. + (dolist (p parents) + (setq ps (cond ((stringp p) p) + ((and (semantic-tag-p p) (semantic-tag-prototype-p p)) + (semantic-tag-name p)) + ((and (listp p) (stringp (car p))) + p)) + pt (condition-case nil + (or (semantic-analyze-find-tag ps 'type tmpscope) + ;; A backup hack. + (semantic-analyze-find-tag ps 'type scope)) + (error nil))) + + (when pt + (funcall fcn pt) + ;; Note that we pass the original SCOPE in while recursing. + ;; so that the correct inheritance model is passed along. + (semantic-analyze-scoped-inherited-tag-map pt fcn scope) + ))) + nil)) + +;;; ANALYZER +;; +;; Create the scope structure for use in the Analyzer. +;; +;;;###autoload +(defun semantic-calculate-scope (&optional point) + "Calculate the scope at POINT. +If POINT is not provided, then use the current location of point. +The class returned from the scope calculation is variable +`semantic-scope-cache'." + (interactive) + (if (not (and (featurep 'semantic/db) semanticdb-current-database)) + nil ;; Don't do anything... + (require 'semantic/db-typecache) + (if (not point) (setq point (point))) + (when (interactive-p) + (semantic-fetch-tags) + (semantic-scope-reset-cache) + ) + (save-excursion + (goto-char point) + (let* ((TAG (semantic-current-tag)) + (scopecache + (semanticdb-cache-get semanticdb-current-table + semantic-scope-cache)) + ) + (when (not (semantic-equivalent-tag-p TAG (oref scopecache tag))) + (semantic-reset scopecache)) + (if (oref scopecache tag) + ;; Even though we can recycle most of the scope, we + ;; need to redo the local variables since those change + ;; as you move about the tag. + (condition-case nil + (oset scopecache localvar (semantic-get-all-local-variables)) + (error nil)) + + (let* (;; Step 1: + (scopetypes (semantic-analyze-scoped-types point)) + (parents (semantic-analyze-scope-nested-tags point scopetypes)) + (parentinherited (semantic-analyze-scope-lineage-tags + parents scopetypes)) + ) + (oset scopecache tag TAG) + (oset scopecache scopetypes scopetypes) + (oset scopecache parents parents) + (oset scopecache parentinheritance parentinherited) + + (let* (;; Step 2: + (scope (when (or scopetypes parents) + (semantic-analyze-scoped-tags scopetypes scopecache)) + ) + ;; Step 3: + (localargs (semantic-get-local-arguments)) + (localvar (condition-case nil + (semantic-get-all-local-variables) + (error nil))) + ) + + ;; Try looking for parents again. + (when (not parentinherited) + (setq parentinherited (semantic-analyze-scope-lineage-tags + parents (append scopetypes scope))) + (when parentinherited + (oset scopecache parentinheritance parentinherited) + ;; Try calculating the scope again with the new inherited parent list. + (setq scope (when (or scopetypes parents) + (semantic-analyze-scoped-tags scopetypes scopecache)) + ))) + + ;; Fill out the scope. + (oset scopecache scope scope) + (oset scopecache fullscope (append scopetypes scope parents)) + (oset scopecache localargs localargs) + (oset scopecache localvar localvar) + ))) + ;; Make sure we become dependant on the typecache. + (semanticdb-typecache-add-dependant scopecache) + ;; Handy debug output. + (when (interactive-p) + (require 'eieio-datadebug) + (data-debug-show scopecache) + ) + ;; Return ourselves + scopecache)))) + +(defun semantic-scope-find (name &optional class scope-in) + "Find the tag with NAME, and optinal CLASS in the current SCOPE-IN. +Searches various elements of the scope for NAME. Return ALL the +hits in order, with the first tag being in the closest scope." + (let ((scope (or scope-in (semantic-calculate-scope))) + (ans nil)) + ;; Is the passed in scope really a scope? if so, look through + ;; the options in that scope. + (if (semantic-scope-cache-p scope) + (let* ((la + ;; This should be first, but bugs in the + ;; C parser will turn function calls into + ;; assumed int return function prototypes. Yuck! + (semantic-find-tags-by-name name (oref scope localargs))) + (lv + (semantic-find-tags-by-name name (oref scope localvar))) + (fullscoperaw (oref scope fullscope)) + (sc (semantic-find-tags-by-name name fullscoperaw)) + (typescoperaw (oref scope typescope)) + (tsc (semantic-find-tags-by-name name typescoperaw)) + ) + (setq ans + (if class + ;; Scan out things not of the right class. + (semantic-find-tags-by-class class (append la lv sc tsc)) + (append la lv sc tsc)) + ) + + (when (and (not ans) (or typescoperaw fullscoperaw)) + (let ((namesplit (semantic-analyze-split-name name))) + (when (consp namesplit) + ;; It may be we need to hack our way through type typescope. + (while namesplit + (setq ans (append + (semantic-find-tags-by-name (car namesplit) + typescoperaw) + (semantic-find-tags-by-name (car namesplit) + fullscoperaw) + )) + (if (not ans) + (setq typescoperaw nil) + (when (cdr namesplit) + (setq typescoperaw (semantic-tag-type-members + (car ans))))) + + (setq namesplit (cdr namesplit))) + ;; Once done, store the current typecache lookup + (oset scope typescope + (append typescoperaw (oref scope typescope))) + ))) + ;; Return it. + ans) + ;; Not a real scope. Our scope calculation analyze parts of + ;; what it finds, and needs to pass lists through to do it's work. + ;; Tread that list as a singly entry. + (if class + (semantic-find-tags-by-class class scope) + scope) + ))) + +;;; DUMP +;; +(defmethod semantic-analyze-show ((context semantic-scope-cache)) + "Insert CONTEXT into the current buffer in a nice way." + (require 'semantic/analyze) + (semantic-analyze-princ-sequence (oref context scopetypes) "-> ScopeTypes: " ) + (semantic-analyze-princ-sequence (oref context parents) "-> Parents: " ) + (semantic-analyze-princ-sequence (oref context scope) "-> Scope: " ) + ;;(semantic-analyze-princ-sequence (oref context fullscope) "Fullscope: " ) + (semantic-analyze-princ-sequence (oref context localargs) "-> Local Args: " ) + (semantic-analyze-princ-sequence (oref context localvar) "-> Local Vars: " ) + ) + +(provide 'semantic/scope) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/scope" +;; End: + +;;; semantic/scope.el ends here diff --git a/lisp/cedet/semantic/senator.el b/lisp/cedet/semantic/senator.el new file mode 100644 index 00000000000..41735f9c6c7 --- /dev/null +++ b/lisp/cedet/semantic/senator.el @@ -0,0 +1,888 @@ +;;; semantic/senator.el --- SEmantic NAvigaTOR + +;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +;; 2009 Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> +;; Maintainer: FSF +;; Created: 10 Nov 2000 +;; 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: +;; +;; This file defines some user commands for navigating between +;; Semantic tags. This is a subset of the version of senator.el in +;; the upstream CEDET package; the rest is incorporated into other +;; parts of Semantic or Emacs. + +;;; Code: + +(require 'ring) +(require 'semantic) +(require 'semantic/ctxt) +(require 'semantic/decorate) +(require 'semantic/format) + +(eval-when-compile (require 'semantic/find)) + +;; (eval-when-compile (require 'hippie-exp)) + +(declare-function semanticdb-fast-strip-find-results "semantic/db-find") +(declare-function semanticdb-deep-find-tags-for-completion "semantic/db-find") +(declare-function semantic-analyze-tag-references "semantic/analyze/refs") +(declare-function semantic-analyze-refs-impl "semantic/analyze/refs") +(declare-function semantic-analyze-find-tag "semantic/analyze") +(declare-function semantic-analyze-tag-type "semantic/analyze/fcn") +(declare-function semantic-tag-external-class "semantic/sort") +(declare-function imenu--mouse-menu "imenu") + +;;; Customization +(defgroup senator nil + "Semantic Navigator." + :group 'semantic) + +;;;###autoload +(defcustom senator-step-at-tag-classes nil + "List of tag classes recognized by Senator's navigation commands. +A tag class is a symbol, such as `variable', `function', or `type'. + +As a special exception, if the value is nil, Senator's navigation +commands recognize all tag classes." + :group 'senator + :type '(repeat (symbol))) +;;;###autoload +(make-variable-buffer-local 'senator-step-at-tag-classes) + +;;;###autoload +(defcustom senator-step-at-start-end-tag-classes nil + "List of tag classes at which Senator's navigation commands should stop. +A tag class is a symbol, such as `variable', `function', or `type'. +The navigation commands stop at the start and end of each tag +class in this list, provided the tag class is recognized (see +`senator-step-at-tag-classes'). + +As a special exception, if the value is nil, the navigation +commands stop at the beginning of every tag. + +If t, the navigation commands stop at the start and end of any +tag, where possible." + :group 'senator + :type '(choice :tag "Identifiers" + (repeat :menu-tag "Symbols" (symbol)) + (const :tag "All" t))) +;;;###autoload +(make-variable-buffer-local 'senator-step-at-start-end-tag-classes) + +(defcustom senator-highlight-found nil + "If non-nil, Senator commands momentarily highlight found tags." + :group 'senator + :type 'boolean) +(make-variable-buffer-local 'senator-highlight-found) + +;;; Faces +(defface senator-momentary-highlight-face + '((((class color) (background dark)) + (:background "gray30")) + (((class color) (background light)) + (:background "gray70"))) + "Face used to momentarily highlight tags." + :group 'semantic-faces) + +;;; Common functions + +(defun senator-momentary-highlight-tag (tag) + "Momentarily highlight TAG. +Does nothing if `senator-highlight-found' is nil." + (and senator-highlight-found + (semantic-momentary-highlight-tag + tag 'senator-momentary-highlight-face))) + +(defun senator-step-at-start-end-p (tag) + "Return non-nil if must step at start and end of TAG." + (and tag + (or (eq senator-step-at-start-end-tag-classes t) + (memq (semantic-tag-class tag) + senator-step-at-start-end-tag-classes)))) + +(defun senator-skip-p (tag) + "Return non-nil if must skip TAG." + (and tag + senator-step-at-tag-classes + (not (memq (semantic-tag-class tag) + senator-step-at-tag-classes)))) + +(defun senator-middle-of-tag-p (pos tag) + "Return non-nil if POS is between start and end of TAG." + (and (> pos (semantic-tag-start tag)) + (< pos (semantic-tag-end tag)))) + +(defun senator-step-at-parent (tag) + "Return TAG's outermost parent if must step at start/end of it. +Return nil otherwise." + (if tag + (let (parent parents) + (setq parents (semantic-find-tag-by-overlay + (semantic-tag-start tag))) + (while (and parents (not parent)) + (setq parent (car parents) + parents (cdr parents)) + (if (or (eq tag parent) + (senator-skip-p parent) + (not (senator-step-at-start-end-p parent))) + (setq parent nil))) + parent))) + +(defun senator-previous-tag-or-parent (pos) + "Return the tag before POS or one of its parent where to step." + (let (ol tag) + (while (and pos (> pos (point-min)) (not tag)) + (setq pos (semantic-overlay-previous-change pos)) + (when pos + ;; Get overlays at position + (setq ol (semantic-overlays-at pos)) + ;; find the overlay that belongs to semantic + ;; and STARTS or ENDS at the found position. + (while (and ol (not tag)) + (setq tag (semantic-overlay-get (car ol) 'semantic)) + (unless (and tag (semantic-tag-p tag) + (or (= (semantic-tag-start tag) pos) + (= (semantic-tag-end tag) pos))) + (setq tag nil + ol (cdr ol)))))) + (or (senator-step-at-parent tag) tag))) + +;;; Search functions + +(defun senator-search-tag-name (tag) + "Search for TAG name in current buffer. +Limit the search to TAG bounds. +If found, set point to the end of the name, and return point. The +beginning of the name is at (match-beginning 0). +Return nil if not found, that is if TAG name doesn't come from the +source." + (let ((name (semantic-tag-name tag))) + (setq name (if (string-match "\\`\\([^[]+\\)[[]" name) + (match-string 1 name) + name)) + (goto-char (semantic-tag-start tag)) + (when (re-search-forward (concat + ;; The tag name is expected to be + ;; between word delimiters, whitespaces, + ;; or punctuations. + "\\(\\<\\|\\s-+\\|\\s.\\)" + (regexp-quote name) + "\\(\\>\\|\\s-+\\|\\s.\\)") + (semantic-tag-end tag) + t) + (goto-char (match-beginning 0)) + (search-forward name)))) + +(defcustom senator-search-ignore-tag-classes + '(code block) + "List of ignored tag classes. +Tags of those classes are excluded from search." + :group 'senator + :type '(repeat (symbol :tag "class"))) + +(defun senator-search-default-tag-filter (tag) + "Default function that filters searched tags. +Ignore tags of classes in `senator-search-ignore-tag-classes'" + (not (memq (semantic-tag-class tag) + senator-search-ignore-tag-classes))) + +(defvar senator-search-tag-filter-functions + '(senator-search-default-tag-filter) + "List of functions to be called to filter searched tags. +Each function is passed a tag. If one of them returns nil, the tag is +excluded from the search.") + +(defun senator-search (searcher text &optional bound noerror count) + "Use the SEARCHER function to search from point for TEXT in a tag name. +SEARCHER is typically the function `search-forward', `search-backward', +`word-search-forward', `word-search-backward', `re-search-forward', or +`re-search-backward'. See one of the above function to see how the +TEXT, BOUND, NOERROR, and COUNT arguments are interpreted." + (let* ((origin (point)) + (count (or count 1)) + (step (cond ((> count 0) 1) + ((< count 0) (setq count (- count)) -1) + (0))) + found next sstart send tag tstart tend) + (or (zerop step) + (while (and (not found) + (setq next (funcall searcher text bound t step))) + (setq sstart (match-beginning 0) + send (match-end 0)) + (if (= sstart send) + (setq found t) + (and (setq tag (semantic-current-tag)) + (run-hook-with-args-until-failure + 'senator-search-tag-filter-functions tag) + (setq tend (senator-search-tag-name tag)) + (setq tstart (match-beginning 0) + found (and (>= sstart tstart) + (<= send tend) + (zerop (setq count (1- count)))))) + (goto-char next)))) + (cond ((null found) + (setq next origin + send origin)) + ((= next sstart) + (setq next send + send sstart)) + (t + (setq next sstart))) + (goto-char next) + ;; Setup the returned value and the `match-data' or maybe fail! + (funcall searcher text send noerror step))) + +;;; Navigation commands + +;;;###autoload +(defun senator-next-tag () + "Navigate to the next Semantic tag. +Return the tag or nil if at end of buffer." + (interactive) + (let ((pos (point)) + (tag (semantic-current-tag)) + where) + (if (and tag + (not (senator-skip-p tag)) + (senator-step-at-start-end-p tag) + (or (= pos (semantic-tag-start tag)) + (senator-middle-of-tag-p pos tag))) + nil + (if (setq tag (senator-step-at-parent tag)) + nil + (setq tag (semantic-find-tag-by-overlay-next pos)) + (while (and tag (senator-skip-p tag)) + (setq tag (semantic-find-tag-by-overlay-next + (semantic-tag-start tag)))))) + (if (not tag) + (progn + (goto-char (point-max)) + (message "End of buffer")) + (cond ((and (senator-step-at-start-end-p tag) + (or (= pos (semantic-tag-start tag)) + (senator-middle-of-tag-p pos tag))) + (setq where "end") + (goto-char (semantic-tag-end tag))) + (t + (setq where "start") + (goto-char (semantic-tag-start tag)))) + (senator-momentary-highlight-tag tag) + (message "%S: %s (%s)" + (semantic-tag-class tag) + (semantic-tag-name tag) + where)) + tag)) + +;;;###autoload +(defun senator-previous-tag () + "Navigate to the previous Semantic tag. +Return the tag or nil if at beginning of buffer." + (interactive) + (let ((pos (point)) + (tag (semantic-current-tag)) + where) + (if (and tag + (not (senator-skip-p tag)) + (senator-step-at-start-end-p tag) + (or (= pos (semantic-tag-end tag)) + (senator-middle-of-tag-p pos tag))) + nil + (if (setq tag (senator-step-at-parent tag)) + nil + (setq tag (senator-previous-tag-or-parent pos)) + (while (and tag (senator-skip-p tag)) + (setq tag (senator-previous-tag-or-parent + (semantic-tag-start tag)))))) + (if (not tag) + (progn + (goto-char (point-min)) + (message "Beginning of buffer")) + (cond ((or (not (senator-step-at-start-end-p tag)) + (= pos (semantic-tag-end tag)) + (senator-middle-of-tag-p pos tag)) + (setq where "start") + (goto-char (semantic-tag-start tag))) + (t + (setq where "end") + (goto-char (semantic-tag-end tag)))) + (senator-momentary-highlight-tag tag) + (message "%S: %s (%s)" + (semantic-tag-class tag) + (semantic-tag-name tag) + where)) + tag)) + +;;; Search commands + +(defun senator-search-forward (string &optional bound noerror count) + "Search in tag names forward from point for STRING. +Set point to the end of the occurrence found, and return point. +See also the function `search-forward' for details on the BOUND, +NOERROR and COUNT arguments." + (interactive "sSemantic search: ") + (senator-search 'search-forward string bound noerror count)) + +(defun senator-re-search-forward (regexp &optional bound noerror count) + "Search in tag names forward from point for regular expression REGEXP. +Set point to the end of the occurrence found, and return point. +See also the function `re-search-forward' for details on the BOUND, +NOERROR and COUNT arguments." + (interactive "sSemantic regexp search: ") + (senator-search 're-search-forward regexp bound noerror count)) + +(defun senator-word-search-forward (word &optional bound noerror count) + "Search in tag names forward from point for WORD. +Set point to the end of the occurrence found, and return point. +See also the function `word-search-forward' for details on the BOUND, +NOERROR and COUNT arguments." + (interactive "sSemantic word search: ") + (senator-search 'word-search-forward word bound noerror count)) + +(defun senator-search-backward (string &optional bound noerror count) + "Search in tag names backward from point for STRING. +Set point to the beginning of the occurrence found, and return point. +See also the function `search-backward' for details on the BOUND, +NOERROR and COUNT arguments." + (interactive "sSemantic backward search: ") + (senator-search 'search-backward string bound noerror count)) + +(defun senator-re-search-backward (regexp &optional bound noerror count) + "Search in tag names backward from point for regular expression REGEXP. +Set point to the beginning of the occurrence found, and return point. +See also the function `re-search-backward' for details on the BOUND, +NOERROR and COUNT arguments." + (interactive "sSemantic backward regexp search: ") + (senator-search 're-search-backward regexp bound noerror count)) + +(defun senator-word-search-backward (word &optional bound noerror count) + "Search in tag names backward from point for WORD. +Set point to the beginning of the occurrence found, and return point. +See also the function `word-search-backward' for details on the BOUND, +NOERROR and COUNT arguments." + (interactive "sSemantic backward word search: ") + (senator-search 'word-search-backward word bound noerror count)) + +;;; Other useful search commands (minor mode menu) + +(defvar senator-last-search-type nil + "Type of last non-incremental search command called.") + +(defun senator-nonincremental-repeat-search-forward () + "Search forward for the previous search string or regexp." + (interactive) + (cond + ((and (eq senator-last-search-type 'string) + search-ring) + (senator-search-forward (car search-ring))) + ((and (eq senator-last-search-type 'regexp) + regexp-search-ring) + (senator-re-search-forward (car regexp-search-ring))) + (t + (error "No previous search")))) + +(defun senator-nonincremental-repeat-search-backward () + "Search backward for the previous search string or regexp." + (interactive) + (cond + ((and (eq senator-last-search-type 'string) + search-ring) + (senator-search-backward (car search-ring))) + ((and (eq senator-last-search-type 'regexp) + regexp-search-ring) + (senator-re-search-backward (car regexp-search-ring))) + (t + (error "No previous search")))) + +(defun senator-nonincremental-search-forward (string) + "Search for STRING nonincrementally." + (interactive "sSemantic search for string: ") + (setq senator-last-search-type 'string) + (if (equal string "") + (senator-search-forward (car search-ring)) + (isearch-update-ring string nil) + (senator-search-forward string))) + +(defun senator-nonincremental-search-backward (string) + "Search backward for STRING nonincrementally." + (interactive "sSemantic search for string: ") + (setq senator-last-search-type 'string) + (if (equal string "") + (senator-search-backward (car search-ring)) + (isearch-update-ring string nil) + (senator-search-backward string))) + +(defun senator-nonincremental-re-search-forward (string) + "Search for the regular expression STRING nonincrementally." + (interactive "sSemantic search for regexp: ") + (setq senator-last-search-type 'regexp) + (if (equal string "") + (senator-re-search-forward (car regexp-search-ring)) + (isearch-update-ring string t) + (senator-re-search-forward string))) + +(defun senator-nonincremental-re-search-backward (string) + "Search backward for the regular expression STRING nonincrementally." + (interactive "sSemantic search for regexp: ") + (setq senator-last-search-type 'regexp) + (if (equal string "") + (senator-re-search-backward (car regexp-search-ring)) + (isearch-update-ring string t) + (senator-re-search-backward string))) + +(defvar senator--search-filter nil) + +(defun senator-search-set-tag-class-filter (&optional classes) + "In current buffer, limit search scope to tag CLASSES. +CLASSES is a list of tag class symbols or nil. If nil only global +filters in `senator-search-tag-filter-functions' remain active." + (interactive "sClasses: ") + (setq classes + (cond + ((null classes) + nil) + ((symbolp classes) + (list classes)) + ((stringp classes) + (mapcar 'read (split-string classes))) + (t + (signal 'wrong-type-argument (list classes))) + )) + ;; Clear previous filter. + (remove-hook 'senator-search-tag-filter-functions + senator--search-filter t) + (kill-local-variable 'senator--search-filter) + (if classes + (let ((tag (make-symbol "tag")) + (names (mapconcat 'symbol-name classes "', `"))) + (set (make-local-variable 'senator--search-filter) + `(lambda (,tag) + (memq (semantic-tag-class ,tag) ',classes))) + (add-hook 'senator-search-tag-filter-functions + senator--search-filter nil t) + (message "Limit search to `%s' tags" names)) + (message "Default search filter restored"))) + +;;; Folding +;; +;; Use new folding state. It might be wise to extend the idea +;; of folding for hiding all but this, or show all children, etc. + +(defun senator-fold-tag (&optional tag) + "Fold the current TAG." + (interactive) + (semantic-set-tag-folded (or tag (semantic-current-tag)) t)) + +(defun senator-unfold-tag (&optional tag) + "Fold the current TAG." + (interactive) + (semantic-set-tag-folded (or tag (semantic-current-tag)) nil)) + +(defun senator-fold-tag-toggle (&optional tag) + "Fold the current TAG." + (interactive) + (let ((tag (or tag (semantic-current-tag)))) + (if (semantic-tag-folded-p tag) + (senator-unfold-tag tag) + (senator-fold-tag tag)))) + +;; @TODO - move this to some analyzer / refs tool +(define-overloadable-function semantic-up-reference (tag) + "Return a tag that is referred to by TAG. +A \"reference\" could be any interesting feature of TAG. +In C++, a function may have a 'parent' which is non-local. +If that parent which is only a reference in the function tag +is found, we can jump to it. +Some tags such as includes have other reference features.") + +;;;###autoload +(defun senator-go-to-up-reference (&optional tag) + "Move up one reference from the current TAG. +A \"reference\" could be any interesting feature of TAG. +In C++, a function may have a 'parent' which is non-local. +If that parent which is only a reference in the function tag +is found, we can jump to it. +Some tags such as includes have other reference features." + (interactive) + (let ((result (semantic-up-reference (or tag (semantic-current-tag))))) + (if (not result) + (error "No up reference found") + (push-mark) + (cond + ;; A tag + ((semantic-tag-p result) + (semantic-go-to-tag result) + (switch-to-buffer (current-buffer)) + (semantic-momentary-highlight-tag result)) + ;; Buffers + ((bufferp result) + (switch-to-buffer result) + (pulse-momentary-highlight-one-line (point))) + ;; Files + ((and (stringp result) (file-exists-p result)) + (find-file result) + (pulse-momentary-highlight-one-line (point))) + (t + (error "Unknown result type from `semantic-up-reference'")))))) + +(defun semantic-up-reference-default (tag) + "Return a tag that is referredto by TAG. +Makes C/C++ language like assumptions." + (cond ((semantic-tag-faux-p tag) + ;; Faux tags should have a real tag in some other location. + (require 'semantic/sort) + (let ((options (semantic-tag-external-class tag))) + ;; I should do something a little better than + ;; this. Oy! + (car options) + )) + + ;; Include always point to another file. + ((eq (semantic-tag-class tag) 'include) + (let ((file (semantic-dependency-tag-file tag))) + (cond + ((or (not file) (not (file-exists-p file))) + (error "Could not location include %s" + (semantic-tag-name tag))) + ((get-file-buffer file) + (get-file-buffer file)) + ((stringp file) + file) + ))) + + ;; Is there a parent of the function to jump to? + ((and (semantic-tag-of-class-p tag 'function) + (semantic-tag-function-parent tag)) + (let* ((scope (semantic-calculate-scope (point)))) + ;; @todo - it would be cool to ask the user which one if + ;; more than one. + (car (oref scope parents)) + )) + + ;; Is there a non-prototype version of the tag to jump to? + ((semantic-tag-get-attribute tag :prototype-flag) + (require 'semantic/analyze/refs) + (let* ((sar (semantic-analyze-tag-references tag))) + (car (semantic-analyze-refs-impl sar t))) + ) + + ;; If this is a datatype, and we have superclasses + ((and (semantic-tag-of-class-p tag 'type) + (semantic-tag-type-superclasses tag)) + (require 'semantic/analyze) + (let ((scope (semantic-calculate-scope (point))) + (parents (semantic-tag-type-superclasses tag))) + (semantic-analyze-find-tag (car parents) 'type scope))) + + ;; Get the data type, and try to find that. + ((semantic-tag-type tag) + (require 'semantic/analyze) + (let ((scope (semantic-calculate-scope (point)))) + (semantic-analyze-tag-type tag scope)) + ) + (t nil))) + +(defvar senator-isearch-semantic-mode nil + "Non-nil if isearch does semantic search. +This is a buffer local variable.") +(make-variable-buffer-local 'senator-isearch-semantic-mode) + +(defun senator-beginning-of-defun (&optional arg) + "Move backward to the beginning of a defun. +Use semantic tags to navigate. +ARG is the number of tags to navigate (not yet implemented)." + (semantic-fetch-tags) + (let* ((senator-highlight-found nil) + ;; Step at beginning of next tag with class specified in + ;; `senator-step-at-tag-classes'. + (senator-step-at-start-end-tag-classes t) + (tag (senator-previous-tag))) + (when tag + (if (= (point) (semantic-tag-end tag)) + (goto-char (semantic-tag-start tag))) + (beginning-of-line)))) + +(defun senator-end-of-defun (&optional arg) + "Move forward to next end of defun. +Use semantic tags to navigate. +ARG is the number of tags to navigate (not yet implemented)." + (semantic-fetch-tags) + (let* ((senator-highlight-found nil) + ;; Step at end of next tag with class specified in + ;; `senator-step-at-tag-classes'. + (senator-step-at-start-end-tag-classes t) + (tag (senator-next-tag))) + (when tag + (if (= (point) (semantic-tag-start tag)) + (goto-char (semantic-tag-end tag))) + (skip-chars-forward " \t") + (if (looking-at "\\s<\\|\n") + (forward-line 1))))) + +(defun senator-narrow-to-defun () + "Make text outside current defun invisible. +The defun visible is the one that contains point or follows point. +Use semantic tags to navigate." + (interactive) + (semantic-fetch-tags) + (save-excursion + (widen) + (senator-end-of-defun) + (let ((end (point))) + (senator-beginning-of-defun) + (narrow-to-region (point) end)))) + +(defun senator-mark-defun () + "Put mark at end of this defun, point at beginning. +The defun marked is the one that contains point or follows point. +Use semantic tags to navigate." + (interactive) + (let ((origin (point)) + (end (progn (senator-end-of-defun) (point))) + (start (progn (senator-beginning-of-defun) (point)))) + (goto-char origin) + (push-mark (point)) + (goto-char end) ;; end-of-defun + (push-mark (point) nil t) + (goto-char start) ;; beginning-of-defun + (re-search-backward "^\n" (- (point) 1) t))) + +;;; Tag Cut & Paste + +;; To copy a tag, means to put a tag definition into the tag +;; ring. To kill a tag, put the tag into the tag ring AND put +;; the body of the tag into the kill-ring. +;; +;; To retrieve a killed tag's text, use C-y (yank), but to retrieve +;; the tag as a reference of some sort, use senator-yank-tag. + +(defvar senator-tag-ring (make-ring 20) + "Ring of tags for use with cut and paste.") + +;;;###autoload +(defun senator-copy-tag () + "Take the current tag, and place it in the tag ring." + (interactive) + (semantic-fetch-tags) + (let ((ft (semantic-obtain-foreign-tag))) + (when ft + (ring-insert senator-tag-ring ft) + (kill-ring-save (semantic-tag-start ft) (semantic-tag-end ft)) + (when (interactive-p) + (message "Use C-y to yank text. Use `senator-yank-tag' for prototype insert.")) + ) + ft)) + +;;;###autoload +(defun senator-kill-tag () + "Take the current tag, place it in the tag ring, and kill it. +Killing the tag removes the text for that tag, and places it into +the kill ring. Retrieve that text with \\[yank]." + (interactive) + (let ((ct (senator-copy-tag))) ;; this handles the reparse for us. + (kill-region (semantic-tag-start ct) + (semantic-tag-end ct)) + (when (interactive-p) + (message "Use C-y to yank text. Use `senator-yank-tag' for prototype insert.")) + )) + +;;;###autoload +(defun senator-yank-tag () + "Yank a tag from the tag ring. +The form the tag takes is differnet depending on where it is being +yanked to." + (interactive) + (or (ring-empty-p senator-tag-ring) + (let ((ft (ring-ref senator-tag-ring 0))) + (semantic-foreign-tag-check ft) + (semantic-insert-foreign-tag ft) + (when (interactive-p) + (message "Use C-y to recover the yank the text of %s." + (semantic-tag-name ft))) + ))) + +;;;###autoload +(defun senator-copy-tag-to-register (register &optional kill-flag) + "Copy the current tag into REGISTER. +Optional argument KILL-FLAG will delete the text of the tag to the +kill ring." + (interactive "cTag to register: \nP") + (semantic-fetch-tags) + (let ((ft (semantic-obtain-foreign-tag))) + (when ft + (set-register register ft) + (if kill-flag + (kill-region (semantic-tag-start ft) + (semantic-tag-end ft)))))) + +;;;###autoload +(defun senator-transpose-tags-up () + "Transpose the current tag, and the preceeding tag." + (interactive) + (semantic-fetch-tags) + (let* ((current-tag (semantic-current-tag)) + (prev-tag (save-excursion + (goto-char (semantic-tag-start current-tag)) + (semantic-find-tag-by-overlay-prev))) + (ct-parent (semantic-find-tag-parent-by-overlay current-tag)) + (pt-parent (semantic-find-tag-parent-by-overlay prev-tag))) + (if (not (eq ct-parent pt-parent)) + (error "Cannot transpose tags")) + (let ((txt (buffer-substring (semantic-tag-start current-tag) + (semantic-tag-end current-tag))) + (line (count-lines (semantic-tag-start current-tag) + (point))) + (insert-point nil) + ) + (delete-region (semantic-tag-start current-tag) + (semantic-tag-end current-tag)) + (delete-blank-lines) + (goto-char (semantic-tag-start prev-tag)) + (setq insert-point (point)) + (insert txt) + (if (/= (current-column) 0) + (insert "\n")) + (insert "\n") + (goto-char insert-point) + (forward-line line) + ))) + +;;;###autoload +(defun senator-transpose-tags-down () + "Transpose the current tag, and the following tag." + (interactive) + (semantic-fetch-tags) + (let* ((current-tag (semantic-current-tag)) + (next-tag (save-excursion + (goto-char (semantic-tag-end current-tag)) + (semantic-find-tag-by-overlay-next))) + (end-pt (point-marker)) + ) + (goto-char (semantic-tag-start next-tag)) + (forward-char 1) + (senator-transpose-tags-up) + ;; I know that the above fcn deletes the next tag, so our pt marker + ;; will be stable. + (goto-char end-pt))) + +;;; Using semantic search in isearch mode + +(defun senator-lazy-highlight-update () + "Force lazy highlight update." + (lazy-highlight-cleanup t) + (set 'isearch-lazy-highlight-last-string nil) + (setq isearch-adjusted t) + (isearch-update)) + +;; Recent versions of GNU Emacs allow to override the isearch search +;; function for special needs, and avoid to advice the built-in search +;; function :-) +(defun senator-isearch-search-fun () + "Return the function to use for the search. +Use a senator search function when semantic isearch mode is enabled." + (intern + (concat (if senator-isearch-semantic-mode + "senator-" + "") + (cond (isearch-word "word-") + (isearch-regexp "re-") + (t "")) + "search-" + (if isearch-forward + "forward" + "backward")))) + +(defun senator-isearch-toggle-semantic-mode () + "Toggle semantic searching on or off in isearch mode." + (interactive) + (setq senator-isearch-semantic-mode + (not senator-isearch-semantic-mode)) + (if isearch-mode + ;; force lazy highlight update + (senator-lazy-highlight-update) + (message "Isearch semantic mode %s" + (if senator-isearch-semantic-mode + "enabled" + "disabled")))) + +(defvar senator-old-isearch-search-fun nil + "Hold previous value of `isearch-search-fun-function'.") + +(defun senator-isearch-mode-hook () + "Isearch mode hook to setup semantic searching." + (if (and isearch-mode senator-isearch-semantic-mode) + (progn + ;; When `senator-isearch-semantic-mode' is on save the + ;; previous `isearch-search-fun-function' and install the + ;; senator one. + (when (and (local-variable-p 'isearch-search-fun-function) + (not (local-variable-p 'senator-old-isearch-search-fun))) + (set (make-local-variable 'senator-old-isearch-search-fun) + isearch-search-fun-function)) + (set (make-local-variable 'isearch-search-fun-function) + 'senator-isearch-search-fun)) + ;; When `senator-isearch-semantic-mode' is off restore the + ;; previous `isearch-search-fun-function'. + (when (eq isearch-search-fun-function 'senator-isearch-search-fun) + (if (local-variable-p 'senator-old-isearch-search-fun) + (progn + (set (make-local-variable 'isearch-search-fun-function) + senator-old-isearch-search-fun) + (kill-local-variable 'senator-old-isearch-search-fun)) + (kill-local-variable 'isearch-search-fun-function))))) + +;; (add-hook 'isearch-mode-hook 'senator-isearch-mode-hook) +;; (add-hook 'isearch-mode-end-hook 'senator-isearch-mode-hook) + +;; ;; Keyboard shortcut to toggle semantic search in isearch mode. +;; (define-key isearch-mode-map +;; [(control ?,)] +;; 'senator-isearch-toggle-semantic-mode) + +;; (defadvice insert-register (around senator activate) +;; "Insert contents of register REGISTER as a tag. +;; If senator is not active, use the original mechanism." +;; (let ((val (get-register (ad-get-arg 0)))) +;; (if (and senator-minor-mode (interactive-p) +;; (semantic-foreign-tag-p val)) +;; (semantic-insert-foreign-tag val) +;; ad-do-it))) + +;; (defadvice jump-to-register (around senator activate) +;; "Insert contents of register REGISTER as a tag. +;; If senator is not active, use the original mechanism." +;; (let ((val (get-register (ad-get-arg 0)))) +;; (if (and senator-minor-mode (interactive-p) +;; (semantic-foreign-tag-p val)) +;; (progn +;; (switch-to-buffer (semantic-tag-buffer val)) +;; (goto-char (semantic-tag-start val))) +;; ad-do-it))) + +(provide 'semantic/senator) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/senator" +;; End: + +;;; semantic/senator.el ends here diff --git a/lisp/cedet/semantic/sort.el b/lisp/cedet/semantic/sort.el new file mode 100644 index 00000000000..36d4d808ca2 --- /dev/null +++ b/lisp/cedet/semantic/sort.el @@ -0,0 +1,570 @@ +;;; sort.el --- Utilities for sorting and re-arranging tag tables. + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, +;;; 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: +;; +;; Tag tables originate in the order they appear in a buffer, or source file. +;; It is often useful to re-arrange them is some predictable way for browsing +;; purposes. Re-organization may be alphabetical, or even a complete +;; reorganization of parents and children. +;; +;; Originally written in semantic-util.el +;; + +(require 'semantic) +(eval-when-compile + (require 'semantic/find)) + +(declare-function semanticdb-find-tags-external-children-of-type + "semantic/db-find") + +;;; Alphanumeric sorting +;; +;; Takes a list of tags, and sorts them in a case-insensitive way +;; at a single level. + +;;; Code: +(defun semantic-string-lessp-ci (s1 s2) + "Case insensitive version of `string-lessp'. +Argument S1 and S2 are the strings to compare." + ;; Use downcase instead of upcase because an average name + ;; has more lower case characters. + (if (fboundp 'compare-strings) + (eq (compare-strings s1 0 nil s2 0 nil t) -1) + (string-lessp (downcase s1) (downcase s2)))) + +(defun semantic-sort-tag-type (tag) + "Return a type string for TAG guaranteed to be a string." + (let ((ty (semantic-tag-type tag))) + (cond ((stringp ty) + ty) + ((listp ty) + (or (car ty) "")) + (t "")))) + +(defun semantic-tag-lessp-name-then-type (A B) + "Return t if tag A is < tag B. +First sorts on name, then sorts on the name of the :type of +each tag." + (let ((na (semantic-tag-name A)) + (nb (semantic-tag-name B)) + ) + (if (string-lessp na nb) + t ; a sure thing. + (if (string= na nb) + ;; If equal, test the :type which might be different. + (let* ((ta (semantic-tag-type A)) + (tb (semantic-tag-type B)) + (tas (cond ((stringp ta) + ta) + ((semantic-tag-p ta) + (semantic-tag-name ta)) + (t nil))) + (tbs (cond ((stringp tb) + tb) + ((semantic-tag-p tb) + (semantic-tag-name tb)) + (t nil)))) + (if (and (stringp tas) (stringp tbs)) + (string< tas tbs) + ;; This is if A == B, and no types in A or B + nil)) + ;; This nil is if A > B, but not = + nil)))) + +(defun semantic-sort-tags-by-name-increasing (tags) + "Sort TAGS by name in increasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (string-lessp (semantic-tag-name a) + (semantic-tag-name b))))) + +(defun semantic-sort-tags-by-name-decreasing (tags) + "Sort TAGS by name in decreasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (string-lessp (semantic-tag-name b) + (semantic-tag-name a))))) + +(defun semantic-sort-tags-by-type-increasing (tags) + "Sort TAGS by type in increasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (string-lessp (semantic-sort-tag-type a) + (semantic-sort-tag-type b))))) + +(defun semantic-sort-tags-by-type-decreasing (tags) + "Sort TAGS by type in decreasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (string-lessp (semantic-sort-tag-type b) + (semantic-sort-tag-type a))))) + +(defun semantic-sort-tags-by-name-increasing-ci (tags) + "Sort TAGS by name in increasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (semantic-string-lessp-ci (semantic-tag-name a) + (semantic-tag-name b))))) + +(defun semantic-sort-tags-by-name-decreasing-ci (tags) + "Sort TAGS by name in decreasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (semantic-string-lessp-ci (semantic-tag-name b) + (semantic-tag-name a))))) + +(defun semantic-sort-tags-by-type-increasing-ci (tags) + "Sort TAGS by type in increasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (semantic-string-lessp-ci (semantic-sort-tag-type a) + (semantic-sort-tag-type b))))) + +(defun semantic-sort-tags-by-type-decreasing-ci (tags) + "Sort TAGS by type in decreasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) + (semantic-string-lessp-ci (semantic-sort-tag-type b) + (semantic-sort-tag-type a))))) + +(defun semantic-sort-tags-by-name-then-type-increasing (tags) + "Sort TAGS by name, then type in increasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) (semantic-tag-lessp-name-then-type a b)))) + +(defun semantic-sort-tags-by-name-then-type-decreasing (tags) + "Sort TAGS by name, then type in increasing order with side effects. +Return the sorted list." + (sort tags (lambda (a b) (semantic-tag-lessp-name-then-type b a)))) + +;;; Unique +;; +;; Scan a list of tags, removing duplicates. +;; This must first sort the tags by name alphabetically ascending. +;; +;; Useful for completion lists, or other situations where the +;; other data isn't as useful. + +(defun semantic-unique-tag-table-by-name (tags) + "Scan a list of TAGS, removing duplicate names. +This must first sort the tags by name alphabetically ascending. +For more complex uniqueness testing used by the semanticdb +typecaching system, see `semanticdb-typecache-merge-streams'." + (let ((sorted (semantic-sort-tags-by-name-increasing + (copy-sequence tags))) + (uniq nil)) + (while sorted + (if (or (not uniq) + (not (string= (semantic-tag-name (car sorted)) + (semantic-tag-name (car uniq))))) + (setq uniq (cons (car sorted) uniq))) + (setq sorted (cdr sorted)) + ) + (nreverse uniq))) + +(defun semantic-unique-tag-table (tags) + "Scan a list of TAGS, removing duplicates. +This must first sort the tags by position ascending. +TAGS are removed only if they are equivalent, as can happen when +multiple tag sources are scanned. +For more complex uniqueness testing used by the semanticdb +typecaching system, see `semanticdb-typecache-merge-streams'." + (let ((sorted (sort (copy-sequence tags) + (lambda (a b) + (cond ((not (semantic-tag-with-position-p a)) + t) + ((not (semantic-tag-with-position-p b)) + nil) + (t + (< (semantic-tag-start a) + (semantic-tag-start b))))))) + (uniq nil)) + (while sorted + (if (or (not uniq) + (not (semantic-equivalent-tag-p (car sorted) (car uniq)))) + (setq uniq (cons (car sorted) uniq))) + (setq sorted (cdr sorted)) + ) + (nreverse uniq))) + + +;;; Tag Table Flattening +;; +;; In the 1.4 search API, there was a parameter "search-parts" which +;; was used to find tags inside other tags. This was used +;; infrequently, mostly for completion/jump routines. These types +;; of commands would be better off with a flattened list, where all +;; tags appear at the top level. + +;;;###autoload +(defun semantic-flatten-tags-table (&optional table) + "Flatten the tags table TABLE. +All tags in TABLE, and all components of top level tags +in TABLE will appear at the top level of list. +Tags promoted to the top of the list will still appear +unmodified as components of their parent tags." + (let* ((table (semantic-something-to-tag-table table)) + ;; Initialize the starting list with our table. + (lists (list table))) + (mapc (lambda (tag) + (let ((components (semantic-tag-components tag))) + (if (and components + ;; unpositined tags can be hazardous to + ;; completion. Do we need any type of tag + ;; here? - EL + (semantic-tag-with-position-p (car components))) + (setq lists (cons + (semantic-flatten-tags-table components) + lists))))) + table) + (apply 'append (nreverse lists)) + )) + + +;;; Buckets: +;; +;; A list of tags can be grouped into buckets based on the tag class. +;; Bucketize means to take a list of tags at a given level in a tag +;; table, and reorganize them into buckets based on class. +;; +(defvar semantic-bucketize-tag-class + ;; Must use lambda because `semantic-tag-class' is a macro. + (lambda (tok) (semantic-tag-class tok)) + "Function used to get a symbol describing the class of a tag. +This function must take one argument of a semantic tag. +It should return a symbol found in `semantic-symbol->name-assoc-list' +which `semantic-bucketize' uses to bin up tokens. +To create new bins for an application augment +`semantic-symbol->name-assoc-list', and +`semantic-symbol->name-assoc-list-for-type-parts' in addition +to setting this variable (locally in your function).") + +(defun semantic-bucketize (tags &optional parent filter) + "Sort TAGS into a group of buckets based on tag class. +Unknown classes are placed in a Misc bucket. +Type bucket names are defined by either `semantic-symbol->name-assoc-list'. +If PARENT is specified, then TAGS belong to this PARENT in some way. +This will use `semantic-symbol->name-assoc-list-for-type-parts' to +generate bucket names. +Optional argument FILTER is a filter function to be applied to each bucket. +The filter function will take one argument, which is a list of tokens, and +may re-organize the list with side-effects." + (let* ((name-list (if parent + semantic-symbol->name-assoc-list-for-type-parts + semantic-symbol->name-assoc-list)) + (sn name-list) + (bins (make-vector (1+ (length sn)) nil)) + ask tagtype + (nsn nil) + (num 1) + (out nil)) + ;; Build up the bucket vector + (while sn + (setq nsn (cons (cons (car (car sn)) num) nsn) + sn (cdr sn) + num (1+ num))) + ;; Place into buckets + (while tags + (setq tagtype (funcall semantic-bucketize-tag-class (car tags)) + ask (assq tagtype nsn) + num (or (cdr ask) 0)) + (aset bins num (cons (car tags) (aref bins num))) + (setq tags (cdr tags))) + ;; Remove from buckets into a list. + (setq num 1) + (while (< num (length bins)) + (when (aref bins num) + (setq out + (cons (cons + (cdr (nth (1- num) name-list)) + ;; Filtering, First hacked by David Ponce david@dponce.com + (funcall (or filter 'nreverse) (aref bins num))) + out))) + (setq num (1+ num))) + (if (aref bins 0) + (setq out (cons (cons "Misc" + (funcall (or filter 'nreverse) (aref bins 0))) + out))) + (nreverse out))) + +;;; Adoption +;; +;; Some languages allow children of a type to be defined outside +;; the syntactic scope of that class. These routines will find those +;; external members, and bring them together in a cloned copy of the +;; class tag. +;; +(defvar semantic-orphaned-member-metaparent-type "class" + "In `semantic-adopt-external-members', the type of 'type for metaparents. +A metaparent is a made-up type semantic token used to hold the child list +of orphaned members of a named type.") +(make-variable-buffer-local 'semantic-orphaned-member-metaparent-type) + +(defvar semantic-mark-external-member-function nil + "Function called when an externally defined orphan is found. +By default, the token is always marked with the `adopted' property. +This function should be locally bound by a program that needs +to add additional behaviors into the token list. +This function is called with two arguments. The first is TOKEN which is +a shallow copy of the token to be modified. The second is the PARENT +which is adopting TOKEN. This function should return TOKEN (or a copy of it) +which is then integrated into the revised token list.") + +(defun semantic-adopt-external-members (tags) + "Rebuild TAGS so that externally defined members are regrouped. +Some languages such as C++ and CLOS permit the declaration of member +functions outside the definition of the class. It is easier to study +the structure of a program when such methods are grouped together +more logically. + +This function uses `semantic-tag-external-member-p' to +determine when a potential child is an externally defined member. + +Note: Applications which use this function must account for token +types which do not have a position, but have children which *do* +have positions. + +Applications should use `semantic-mark-external-member-function' +to modify all tags which are found as externally defined to some +type. For example, changing the token type for generating extra +buckets with the bucket function." + (let ((parent-buckets nil) + (decent-list nil) + (out nil) + (tmp nil) + ) + ;; Rebuild the output list, stripping out all parented + ;; external entries + (while tags + (cond + ((setq tmp (semantic-tag-external-member-parent (car tags))) + (let ((tagcopy (semantic-tag-clone (car tags))) + (a (assoc tmp parent-buckets))) + (semantic--tag-put-property-no-side-effect tagcopy 'adopted t) + (if a + ;; If this parent is already in the list, append. + (setcdr (nthcdr (1- (length a)) a) (list tagcopy)) + ;; If not, prepend this new parent bucket into our list + (setq parent-buckets + (cons (cons tmp (list tagcopy)) parent-buckets))) + )) + ((eq (semantic-tag-class (car tags)) 'type) + ;; Types need to be rebuilt from scratch so we can add in new + ;; children to the child list. Only the top-level cons + ;; cells need to be duplicated so we can hack out the + ;; child list later. + (setq out (cons (semantic-tag-clone (car tags)) out)) + (setq decent-list (cons (car out) decent-list)) + ) + (t + ;; Otherwise, append this tag to our new output list. + (setq out (cons (car tags) out))) + ) + (setq tags (cdr tags))) + ;; Rescan out, by descending into all types and finding parents + ;; for all entries moved into the parent-buckets. + (while decent-list + (let* ((bucket (assoc (semantic-tag-name (car decent-list)) + parent-buckets)) + (bucketkids (cdr bucket))) + (when bucket + ;; Run our secondary marking function on the children + (if semantic-mark-external-member-function + (setq bucketkids + (mapcar (lambda (tok) + (funcall semantic-mark-external-member-function + tok (car decent-list))) + bucketkids))) + ;; We have some extra kids. Merge. + (semantic-tag-put-attribute + (car decent-list) :members + (append (semantic-tag-type-members (car decent-list)) + bucketkids)) + ;; Nuke the bucket label so it is not found again. + (setcar bucket nil)) + (setq decent-list + (append (cdr decent-list) + ;; get embedded types to scan and make copies + ;; of them. + (mapcar + (lambda (tok) (semantic-tag-clone tok)) + (semantic-find-tags-by-class 'type + (semantic-tag-type-members (car decent-list))))) + ))) + ;; Scan over all remaining lost external methods, and tack them + ;; onto the end. + (while parent-buckets + (if (car (car parent-buckets)) + (let* ((tmp (car parent-buckets)) + (fauxtag (semantic-tag-new-type + (car tmp) + semantic-orphaned-member-metaparent-type + nil ;; Part list + nil ;; parents (unknown) + )) + (bucketkids (cdr tmp))) + (semantic-tag-set-faux fauxtag) ;; properties + (if semantic-mark-external-member-function + (setq bucketkids + (mapcar (lambda (tok) + (funcall semantic-mark-external-member-function + tok fauxtag)) + bucketkids))) + (semantic-tag-put-attribute fauxtag :members bucketkids) + ;; We have a bunch of methods with no parent in this file. + ;; Create a meta-type to hold it. + (setq out (cons fauxtag out)) + )) + (setq parent-buckets (cdr parent-buckets))) + ;; Return the new list. + (nreverse out))) + + +;;; External children +;; +;; In order to adopt external children, we need a few overload methods +;; to enable the feature. + +;;;###autoload +(define-overloadable-function semantic-tag-external-member-parent (tag) + "Return a parent for TAG when TAG is an external member. +TAG is an external member if it is defined at a toplevel and +has some sort of label defining a parent. The parent return will +be a string. + +The default behavior, if not overridden with +`tag-member-parent' gets the 'parent extra +specifier of TAG. + +If this function is overridden, use +`semantic-tag-external-member-parent-default' to also +include the default behavior, and merely extend your own." + ) + +(defun semantic-tag-external-member-parent-default (tag) + "Return the name of TAGs parent only if TAG is not defined in it's parent." + ;; Use only the extra spec because a type has a parent which + ;; means something completely different. + (let ((tp (semantic-tag-get-attribute tag :parent))) + (when (stringp tp) + tp))) + +(define-overloadable-function semantic-tag-external-member-p (parent tag) + "Return non-nil if PARENT is the parent of TAG. +TAG is an external member of PARENT when it is somehow tagged +as having PARENT as it's parent. +PARENT and TAG must both be semantic tags. + +The default behavior, if not overridden with +`tag-external-member-p' is to match :parent attribute in +the name of TAG. + +If this function is overridden, use +`semantic-tag-external-member-children-p-default' to also +include the default behavior, and merely extend your own." + ) + +(defun semantic-tag-external-member-p-default (parent tag) + "Return non-nil if PARENT is the parent of TAG." + ;; Use only the extra spec because a type has a parent which + ;; means something completely different. + (let ((tp (semantic-tag-external-member-parent tag))) + (and (stringp tp) + (string= (semantic-tag-name parent) tp)))) + +(define-overloadable-function semantic-tag-external-member-children (tag &optional usedb) + "Return the list of children which are not *in* TAG. +If optional argument USEDB is non-nil, then also search files in +the Semantic Database. If USEDB is a list of databases, search those +databases. + +Children in this case are functions or types which are members of +TAG, such as the parts of a type, but which are not defined inside +the class. C++ and CLOS both permit methods of a class to be defined +outside the bounds of the class' definition. + +The default behavior, if not overridden with +`tag-external-member-children' is to search using +`semantic-tag-external-member-p' in all top level definitions +with a parent of TAG. + +If this function is overridden, use +`semantic-tag-external-member-children-default' to also +include the default behavior, and merely extend your own." + ) + +(defun semantic-tag-external-member-children-default (tag &optional usedb) + "Return list of external children for TAG. +Optional argument USEDB specifies if the semantic database is used. +See `semantic-tag-external-member-children' for details." + (if (and usedb + (require 'semantic/db-mode) + (semanticdb-minor-mode-p) + (require 'semantic/db-find)) + (let ((m (semanticdb-find-tags-external-children-of-type + (semantic-tag-name tag)))) + (if m (apply #'append (mapcar #'cdr m)))) + (semantic--find-tags-by-function + `(lambda (tok) + ;; This bit of annoying backquote forces the contents of + ;; tag into the generated lambda. + (semantic-tag-external-member-p ',tag tok)) + (current-buffer)) + )) + +(define-overloadable-function semantic-tag-external-class (tag) + "Return a list of real tags that faux TAG might represent. + +In some languages, a method can be defined on an object which is +not in the same file. In this case, +`semantic-adopt-external-members' will create a faux-tag. If it +is necessary to get the tag from which for faux TAG was most +likely derived, then this function is needed." + (unless (semantic-tag-faux-p tag) + (signal 'wrong-type-argument (list tag 'semantic-tag-faux-p))) + (:override) + ) + +(defun semantic-tag-external-class-default (tag) + "Return a list of real tags that faux TAG might represent. +See `semantic-tag-external-class' for details." + (if (and (require 'semantic/db-mode) + (semanticdb-minor-mode-p)) + (let* ((semanticdb-search-system-databases nil) + (m (semanticdb-find-tags-by-class + (semantic-tag-class tag) + (semanticdb-find-tags-by-name (semantic-tag-name tag))))) + (semanticdb-strip-find-results m 'name)) + ;; Presumably, if the tag is faux, it is not local. + nil)) + +(provide 'semantic/sort) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/sort" +;; End: + +;;; semantic-sort.el ends here diff --git a/lisp/cedet/semantic/symref.el b/lisp/cedet/semantic/symref.el new file mode 100644 index 00000000000..bea148b1c21 --- /dev/null +++ b/lisp/cedet/semantic/symref.el @@ -0,0 +1,501 @@ +;;; semantic/symref.el --- Symbol Reference API + +;;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Semantic Symbol Reference API. +;; +;; Semantic's native parsing tools do not handle symbol references. +;; Tracking such information is a task that requires a huge amount of +;; space and processing not apropriate for an Emacs Lisp program. +;; +;; Many desired tools used in refactoring, however, need to have +;; such references available to them. This API aims to provide a +;; range of functions that can be used to identify references. The +;; API is backed by an OO system that is used to allow multiple +;; external tools to provide the information. +;; +;; The default implementation uses a find/grep combination to do a +;; search. This works ok in small projects. For larger projects, it +;; is important to find an alternate tool to use as a back-end to +;; symref. +;; +;; See the command: `semantic-symref' for an example app using this api. +;; +;; TO USE THIS TOOL +;; +;; The following functions can be used to find different kinds of +;; references. +;; +;; `semantic-symref-find-references-by-name' +;; `semantic-symref-find-file-references-by-name' +;; `semantic-symref-find-text' +;; +;; All the search routines return a class of type +;; `semantic-symref-result'. You can reference the various slots, but +;; you will need the following methods to get extended information. +;; +;; `semantic-symref-result-get-files' +;; `semantic-symref-result-get-tags' +;; +;; ADD A NEW EXTERNAL TOOL +;; +;; To support a new external tool, sublcass `semantic-symref-tool-baseclass' +;; and implement the methods. The baseclass provides support for +;; managing external processes that produce parsable output. +;; +;; Your tool should then create an instance of `semantic-symref-result'. + +(require 'semantic) + +(defvar ede-minor-mode) +(declare-function data-debug-new-buffer "data-debug") +(declare-function data-debug-insert-object-slots "eieio-datadebug") +(declare-function ede-toplevel "ede/files") +(declare-function ede-project-root-directory "ede/files") + +;;; Code: +(defvar semantic-symref-tool 'detect + "*The active symbol reference tool name. +The tool symbol can be 'detect, or a symbol that is the name of +a tool that can be used for symbol referencing.") +(make-variable-buffer-local 'semantic-symref-tool) + +;;; TOOL SETUP +;; +(defvar semantic-symref-tool-alist + '( ( (lambda (rootdir) (file-exists-p (expand-file-name "GPATH" rootdir))) . + global) + ( (lambda (rootdir) (file-exists-p (expand-file-name "ID" rootdir))) . + idutils) + ( (lambda (rootdir) (file-exists-p (expand-file-name "cscope.out" rootdir))) . + cscope ) + ) + "Alist of tools usable by `semantic-symref'. +Each entry is of the form: + ( PREDICATE . KEY ) +Where PREDICATE is a function that takes a directory name for the +root of a project, and returns non-nil if the tool represented by KEY +is supported. + +If no tools are supported, then 'grep is assumed.") + +(defun semantic-symref-detect-symref-tool () + "Detect the symref tool to use for the current buffer." + (if (not (eq semantic-symref-tool 'detect)) + semantic-symref-tool + ;; We are to perform a detection for the right tool to use. + (let* ((rootproj (when (and (featurep 'ede) ede-minor-mode) + (ede-toplevel))) + (rootdir (if rootproj + (ede-project-root-directory rootproj) + default-directory)) + (tools semantic-symref-tool-alist)) + (while (and tools (eq semantic-symref-tool 'detect)) + (when (funcall (car (car tools)) rootdir) + (setq semantic-symref-tool (cdr (car tools)))) + (setq tools (cdr tools))) + + (when (eq semantic-symref-tool 'detect) + (setq semantic-symref-tool 'grep)) + + semantic-symref-tool))) + +(defun semantic-symref-instantiate (&rest args) + "Instantiate a new symref search object. +ARGS are the initialization arguments to pass to the created class." + (let* ((srt (symbol-name (semantic-symref-detect-symref-tool))) + (class (intern-soft (concat "semantic-symref-tool-" srt))) + (inst nil) + ) + (when (not (class-p class)) + (error "Unknown symref tool %s" semantic-symref-tool)) + (setq inst (apply 'make-instance class args)) + inst)) + +(defvar semantic-symref-last-result nil + "The last calculated symref result.") + +(defun semantic-symref-data-debug-last-result () + "Run the last symref data result in Data Debug." + (interactive) + (require 'eieio-datadebug) + (if semantic-symref-last-result + (progn + (data-debug-new-buffer "*Symbol Reference ADEBUG*") + (data-debug-insert-object-slots semantic-symref-last-result "]")) + (message "Empty results."))) + +;;; EXTERNAL API +;; + +;;;###autoload +(defun semantic-symref-find-references-by-name (name &optional scope tool-return) + "Find a list of references to NAME in the current project. +Optional SCOPE specifies which file set to search. Defaults to 'project. +Refers to `semantic-symref-tool', to determine the reference tool to use +for the current buffer. +Returns an object of class `semantic-symref-result'. +TOOL-RETURN is an optional symbol, which will be assigned the tool used +to perform the search. This was added for use by a test harness." + (interactive "sName: ") + (let* ((inst (semantic-symref-instantiate + :searchfor name + :searchtype 'symbol + :searchscope (or scope 'project) + :resulttype 'line)) + (result (semantic-symref-get-result inst))) + (when tool-return + (set tool-return inst)) + (prog1 + (setq semantic-symref-last-result result) + (when (interactive-p) + (semantic-symref-data-debug-last-result)))) + ) + +;;;###autoload +(defun semantic-symref-find-tags-by-name (name &optional scope) + "Find a list of references to NAME in the current project. +Optional SCOPE specifies which file set to search. Defaults to 'project. +Refers to `semantic-symref-tool', to determine the reference tool to use +for the current buffer. +Returns an object of class `semantic-symref-result'." + (interactive "sName: ") + (let* ((inst (semantic-symref-instantiate + :searchfor name + :searchtype 'tagname + :searchscope (or scope 'project) + :resulttype 'line)) + (result (semantic-symref-get-result inst))) + (prog1 + (setq semantic-symref-last-result result) + (when (interactive-p) + (semantic-symref-data-debug-last-result)))) + ) + +;;;###autoload +(defun semantic-symref-find-tags-by-regexp (name &optional scope) + "Find a list of references to NAME in the current project. +Optional SCOPE specifies which file set to search. Defaults to 'project. +Refers to `semantic-symref-tool', to determine the reference tool to use +for the current buffer. +Returns an object of class `semantic-symref-result'." + (interactive "sName: ") + (let* ((inst (semantic-symref-instantiate + :searchfor name + :searchtype 'tagregexp + :searchscope (or scope 'project) + :resulttype 'line)) + (result (semantic-symref-get-result inst))) + (prog1 + (setq semantic-symref-last-result result) + (when (interactive-p) + (semantic-symref-data-debug-last-result)))) + ) + +;;;###autoload +(defun semantic-symref-find-tags-by-completion (name &optional scope) + "Find a list of references to NAME in the current project. +Optional SCOPE specifies which file set to search. Defaults to 'project. +Refers to `semantic-symref-tool', to determine the reference tool to use +for the current buffer. +Returns an object of class `semantic-symref-result'." + (interactive "sName: ") + (let* ((inst (semantic-symref-instantiate + :searchfor name + :searchtype 'tagcompletions + :searchscope (or scope 'project) + :resulttype 'line)) + (result (semantic-symref-get-result inst))) + (prog1 + (setq semantic-symref-last-result result) + (when (interactive-p) + (semantic-symref-data-debug-last-result)))) + ) + +;;;###autoload +(defun semantic-symref-find-file-references-by-name (name &optional scope) + "Find a list of references to NAME in the current project. +Optional SCOPE specifies which file set to search. Defaults to 'project. +Refers to `semantic-symref-tool', to determine the reference tool to use +for the current buffer. +Returns an object of class `semantic-symref-result'." + (interactive "sName: ") + (let* ((inst (semantic-symref-instantiate + :searchfor name + :searchtype 'regexp + :searchscope (or scope 'project) + :resulttype 'file)) + (result (semantic-symref-get-result inst))) + (prog1 + (setq semantic-symref-last-result result) + (when (interactive-p) + (semantic-symref-data-debug-last-result)))) + ) + +;;;###autoload +(defun semantic-symref-find-text (text &optional scope) + "Find a list of occurances of TEXT in the current project. +TEXT is a regexp formatted for use with egrep. +Optional SCOPE specifies which file set to search. Defaults to 'project. +Refers to `semantic-symref-tool', to determine the reference tool to use +for the current buffer. +Returns an object of class `semantic-symref-result'." + (interactive "sEgrep style Regexp: ") + (let* ((inst (semantic-symref-instantiate + :searchfor text + :searchtype 'regexp + :searchscope (or scope 'project) + :resulttype 'line)) + (result (semantic-symref-get-result inst))) + (prog1 + (setq semantic-symref-last-result result) + (when (interactive-p) + (semantic-symref-data-debug-last-result)))) + ) + +;;; RESULTS +;; +;; The results class and methods provide features for accessing hits. +(defclass semantic-symref-result () + ((created-by :initarg :created-by + :type semantic-symref-tool-baseclass + :documentation + "Back-pointer to the symref tool creating these results.") + (hit-files :initarg :hit-files + :type list + :documentation + "The list of files hit.") + (hit-text :initarg :hit-text + :type list + :documentation + "If the result doesn't provide full lines, then fill in hit-text. +GNU Global does completion search this way.") + (hit-lines :initarg :hit-lines + :type list + :documentation + "The list of line hits. +Each element is a cons cell of the form (LINE . FILENAME).") + (hit-tags :initarg :hit-tags + :type list + :documentation + "The list of tags with hits in them. +Use the `semantic-symref-hit-tags' method to get this list.") + ) + "The results from a symbol reference search.") + +(defmethod semantic-symref-result-get-files ((result semantic-symref-result)) + "Get the list of files from the symref result RESULT." + (if (slot-boundp result :hit-files) + (oref result hit-files) + (let* ((lines (oref result :hit-lines)) + (files (mapcar (lambda (a) (cdr a)) lines)) + (ans nil)) + (setq ans (list (car files)) + files (cdr files)) + (dolist (F files) + ;; This algorithm for uniqing the file list depends on the + ;; tool in question providing all the hits in the same file + ;; grouped together. + (when (not (string= F (car ans))) + (setq ans (cons F ans)))) + (oset result hit-files (nreverse ans)) + ) + )) + +(defmethod semantic-symref-result-get-tags ((result semantic-symref-result) + &optional open-buffers) + "Get the list of tags from the symref result RESULT. +Optional OPEN-BUFFERS indicates that the buffers that the hits are +in should remain open after scanning. +Note: This can be quite slow if most of the hits are not in buffers +already." + (if (and (slot-boundp result :hit-tags) (oref result hit-tags)) + (oref result hit-tags) + ;; Calculate the tags. + (let ((lines (oref result :hit-lines)) + (txt (oref (oref result :created-by) :searchfor)) + (searchtype (oref (oref result :created-by) :searchtype)) + (ans nil) + (out nil) + (buffs-to-kill nil)) + (save-excursion + (setq + ans + (mapcar + (lambda (hit) + (let* ((line (car hit)) + (file (cdr hit)) + (buff (get-file-buffer file)) + (tag nil) + ) + (cond + ;; We have a buffer already. Check it out. + (buff + (set-buffer buff)) + + ;; We have a table, but it needs a refresh. + ;; This means we should load in that buffer. + (t + (let ((kbuff + (if open-buffers + ;; Even if we keep the buffers open, don't + ;; let EDE ask lots of questions. + (let ((ede-auto-add-method 'never)) + (find-file-noselect file t)) + ;; When not keeping the buffers open, then + ;; don't setup all the fancy froo-froo features + ;; either. + (semantic-find-file-noselect file t)))) + (set-buffer kbuff) + (setq buffs-to-kill (cons kbuff buffs-to-kill)) + (semantic-fetch-tags) + )) + ) + + ;; Too much baggage in goto-line + ;; (goto-line line) + (goto-char (point-min)) + (forward-line (1- line)) + + ;; Search forward for the matching text + (re-search-forward (regexp-quote txt) + (point-at-eol) + t) + + (setq tag (semantic-current-tag)) + + ;; If we are searching for a tag, but bound the tag we are looking + ;; for, see if it resides in some other parent tag. + ;; + ;; If there is no parent tag, then we still need to hang the originator + ;; in our list. + (when (and (eq searchtype 'symbol) + (string= (semantic-tag-name tag) txt)) + (setq tag (or (semantic-current-tag-parent) tag))) + + ;; Copy the tag, which adds a :filename property. + (when tag + (setq tag (semantic-tag-copy tag nil t)) + ;; Ad this hit to the tag. + (semantic--tag-put-property tag :hit (list line))) + tag)) + lines))) + ;; Kill off dead buffers, unless we were requested to leave them open. + (when (not open-buffers) + (mapc 'kill-buffer buffs-to-kill)) + ;; Strip out duplicates. + (dolist (T ans) + (if (and T (not (semantic-equivalent-tag-p (car out) T))) + (setq out (cons T out)) + (when T + ;; Else, add this line into the existing list of lines. + (let ((lines (append (semantic--tag-get-property (car out) :hit) + (semantic--tag-get-property T :hit)))) + (semantic--tag-put-property (car out) :hit lines))) + )) + ;; Out is reversed... twice + (oset result :hit-tags (nreverse out))))) + +;;; SYMREF TOOLS +;; +;; The base symref tool provides something to hang new tools off of +;; for finding symbol references. +(defclass semantic-symref-tool-baseclass () + ((searchfor :initarg :searchfor + :type string + :documentation "The thing to search for.") + (searchtype :initarg :searchtype + :type symbol + :documentation "The type of search to do. +Values could be `symbol, `regexp, 'tagname, or 'completion.") + (searchscope :initarg :searchscope + :type symbol + :documentation + "The scope to search for. +Can be 'project, 'target, or 'file.") + (resulttype :initarg :resulttype + :type symbol + :documentation + "The kind of search results desired. +Can be 'line, 'file, or 'tag. +The type of result can be converted from 'line to 'file, or 'line to 'tag, +but not from 'file to 'line or 'tag.") + ) + "Baseclass for all symbol references tools. +A symbol reference tool supplies functionality to identify the locations of +where different symbols are used. + +Subclasses should be named `semantic-symref-tool-NAME', where +NAME is the name of the tool used in the configuration variable +`semantic-symref-tool'" + :abstract t) + +(defmethod semantic-symref-get-result ((tool semantic-symref-tool-baseclass)) + "Calculate the results of a search based on TOOL. +The symref TOOL should already contain the search criteria." + (let ((answer (semantic-symref-perform-search tool)) + ) + (when answer + (let ((answersym (if (eq (oref tool :resulttype) 'file) + :hit-files + (if (stringp (car answer)) + :hit-text + :hit-lines)))) + (semantic-symref-result (oref tool searchfor) + answersym + answer + :created-by tool)) + ) + )) + +(defmethod semantic-symref-perform-search ((tool semantic-symref-tool-baseclass)) + "Base search for symref tools should throw an error." + (error "Symref tool objects must implement `semantic-symref-perform-search'")) + +(defmethod semantic-symref-parse-tool-output ((tool semantic-symref-tool-baseclass) + outputbuffer) + "Parse the entire OUTPUTBUFFER of a symref tool. +Calls the method `semantic-symref-parse-tool-output-one-line' over and +over until it returns nil." + (save-excursion + (set-buffer outputbuffer) + (goto-char (point-min)) + (let ((result nil) + (hit nil)) + (while (setq hit (semantic-symref-parse-tool-output-one-line tool)) + (setq result (cons hit result))) + (nreverse result))) + ) + +(defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-baseclass)) + "Base tool output parser is not implemented." + (error "Symref tool objects must implement `semantic-symref-parse-tool-output-one-line'")) + +(provide 'semantic/symref) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/symref" +;; End: + +;;; semantic/symref.el ends here diff --git a/lisp/cedet/semantic/symref/cscope.el b/lisp/cedet/semantic/symref/cscope.el new file mode 100644 index 00000000000..9c9af7c67dd --- /dev/null +++ b/lisp/cedet/semantic/symref/cscope.el @@ -0,0 +1,95 @@ +;;; semantic/symref/cscope.el --- Semantic-symref support via cscope. + +;;; Copyright (C) 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Semantic symref support via cscope. + +(require 'cedet-cscope) +(require 'semantic/symref) + +(defvar ede-minor-mode) +(declare-function ede-toplevel "ede/files") +(declare-function ede-project-root-directory "ede/files") + +;;; Code: +;;;###autoload +(defclass semantic-symref-tool-cscope (semantic-symref-tool-baseclass) + ( + ) + "A symref tool implementation using CScope. +The CScope command can be used to generate lists of tags in a way +similar to that of `grep'. This tool will parse the output to generate +the hit list. + +See the function `cedet-cscope-search' for more details.") + +(defmethod semantic-symref-perform-search ((tool semantic-symref-tool-cscope)) + "Perform a search with GNU Global." + (let* ((rootproj (when (and (featurep 'ede) ede-minor-mode) + (ede-toplevel))) + (default-directory (if rootproj + (ede-project-root-directory rootproj) + default-directory)) + ;; CScope has to be run from the project root where + ;; cscope.out is. + (b (cedet-cscope-search (oref tool :searchfor) + (oref tool :searchtype) + (oref tool :resulttype) + (oref tool :searchscope) + )) + ) + (semantic-symref-parse-tool-output tool b) + )) + +(defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-cscope)) + "Parse one line of grep output, and return it as a match list. +Moves cursor to end of the match." + (cond ((eq (oref tool :resulttype) 'file) + ;; Search for files + (when (re-search-forward "^\\([^\n]+\\)$" nil t) + (match-string 1))) + ((eq (oref tool :searchtype) 'tagcompletions) + ;; Search for files + (when (re-search-forward "^[^ ]+ [^ ]+ [^ ]+ \\(.*\\)$" nil t) + (let ((subtxt (match-string 1)) + (searchtxt (oref tool :searchfor))) + (if (string-match (concat "\\<" searchtxt "\\(\\w\\|\\s_\\)*\\>") + subtxt) + (match-string 0 subtxt) + ;; We have to return something at this point. + subtxt))) + ) + (t + (when (re-search-forward "^\\([^ ]+\\) [^ ]+ \\([0-9]+\\) " nil t) + (cons (string-to-number (match-string 2)) + (expand-file-name (match-string 1))) + )))) + +(provide 'semantic/symref/cscope) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/symref/cscope" +;; End: + +;;; semantic/symref/cscope.el ends here diff --git a/lisp/cedet/semantic/symref/filter.el b/lisp/cedet/semantic/symref/filter.el new file mode 100644 index 00000000000..97e5c92a6ab --- /dev/null +++ b/lisp/cedet/semantic/symref/filter.el @@ -0,0 +1,140 @@ +;;; semantic/symref/filter.el --- Filter symbol reference hits for accuracy. + +;;; Copyright (C) 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Filter symbol reference hits for accuracy. +;; +;; Most symbol referencing tools, such as find/grep only find matching +;; strings, but cannot determine the difference between an actual use, +;; and something else with a similar name, or even a string in a comment. +;; +;; This file provides utilities for filtering down to accurate matches +;; starting at a basic filter level that doesn't use symref, up to filters +;; across symref results. + +;;; Code: + +(require 'semantic) +(require 'semantic/analyze) +(declare-function srecode-active-template-region "srecode/fields") +(declare-function srecode-delete "srecode/fields") +(declare-function srecode-field "srecode/fields") +(declare-function srecode-template-inserted-region "srecode/fields") +(declare-function srecode-overlaid-activate "srecode/fields") +(declare-function semantic-idle-summary-useful-context-p "semantic/idle") + +;;; FILTERS +;; +(defun semantic-symref-filter-hit (target &optional position) + "Determine if the tag TARGET is used at POSITION in the current buffer. +Return non-nil for a match." + (semantic-analyze-current-symbol + (lambda (start end prefix) + (let ((tag (car (nreverse prefix)))) + (and (semantic-tag-p tag) + (semantic-equivalent-tag-p target tag)))) + position)) + +;;; IN-BUFFER FILTERING + +;; The following does filtering in-buffer only, and not against +;; a symref results object. + +(defun semantic-symref-hits-in-region (target hookfcn start end) + "Find all occurances of the symbol TARGET that match TARGET the tag. +For each match, call HOOKFCN. +HOOKFCN takes three arguments that match +`semantic-analyze-current-symbol's use of HOOKfCN. + ( START END PREFIX ) + +Search occurs in the current buffer between START and END." + (require 'semantic/idle) + (save-excursion + (goto-char start) + (let* ((str (semantic-tag-name target)) + (case-fold-search semantic-case-fold) + (regexp (concat "\\<" (regexp-quote str) "\\>"))) + (while (re-search-forward regexp end t) + (when (semantic-idle-summary-useful-context-p) + (semantic-analyze-current-symbol + (lambda (start end prefix) + (let ((tag (car (nreverse prefix)))) + ;; check for semantic match on the text match. + (when (and (semantic-tag-p tag) + (semantic-equivalent-tag-p target tag)) + (save-excursion + (funcall hookfcn start end prefix))))) + (point))))))) + +(defun semantic-symref-rename-local-variable () + "Fancy way to rename the local variable under point. +Depends on the SRecode Field editing API." + (interactive) + ;; Do the replacement as needed. + (let* ((ctxt (semantic-analyze-current-context)) + (target (car (reverse (oref ctxt prefix)))) + (tag (semantic-current-tag)) + ) + + (when (or (not target) + (not (semantic-tag-with-position-p target))) + (error "Cannot identify symbol under point")) + + (when (not (semantic-tag-of-class-p target 'variable)) + (error "Can only rename variables")) + + (when (or (< (semantic-tag-start target) (semantic-tag-start tag)) + (> (semantic-tag-end target) (semantic-tag-end tag))) + (error "Can only rename variables declared in %s" + (semantic-tag-name tag))) + + ;; I think we're good for this example. Give it a go through + ;; our fancy interface from SRecode. + (require 'srecode/fields) + + ;; Make sure there is nothing active. + (let ((ar (srecode-active-template-region))) + (when ar (srecode-delete ar))) + + (let ((srecode-field-archive nil) + (region nil) + ) + (semantic-symref-hits-in-region + target (lambda (start end prefix) + ;; For every valid hit, create one field. + (srecode-field "LOCAL" :name "LOCAL" :start start :end end)) + (semantic-tag-start tag) (semantic-tag-end tag)) + + ;; Now that the fields are setup, create the region. + (setq region (srecode-template-inserted-region + "REGION" :start (semantic-tag-start tag) + :end (semantic-tag-end tag))) + + ;; Activate the region. + (srecode-overlaid-activate region) + + ) + )) + +(provide 'semantic/symref/filter) + +;;; semantic/symref/filter.el ends here diff --git a/lisp/cedet/semantic/symref/global.el b/lisp/cedet/semantic/symref/global.el new file mode 100644 index 00000000000..de3f7a552de --- /dev/null +++ b/lisp/cedet/semantic/symref/global.el @@ -0,0 +1,76 @@ +;;; semantic/symref/global.el --- Use GNU Global for symbol references + +;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric Ludlam <eludlam@mathworks.com> + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; GNU Global use with the semantic-symref system. + +(require 'cedet-global) +(require 'semantic/symref) + +;;; Code: +;;;###autoload +(defclass semantic-symref-tool-global (semantic-symref-tool-baseclass) + ( + ) + "A symref tool implementation using GNU Global. +The GNU Global command can be used to generate lists of tags in a way +similar to that of `grep'. This tool will parse the output to generate +the hit list. + +See the function `cedet-gnu-global-search' for more details.") + +(defmethod semantic-symref-perform-search ((tool semantic-symref-tool-global)) + "Perform a search with GNU Global." + (let ((b (cedet-gnu-global-search (oref tool :searchfor) + (oref tool :searchtype) + (oref tool :resulttype) + (oref tool :searchscope) + )) + ) + (semantic-symref-parse-tool-output tool b) + )) + +(defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-global)) + "Parse one line of grep output, and return it as a match list. +Moves cursor to end of the match." + (cond ((or (eq (oref tool :resulttype) 'file) + (eq (oref tool :searchtype) 'tagcompletions)) + ;; Search for files + (when (re-search-forward "^\\([^\n]+\\)$" nil t) + (match-string 1))) + (t + (when (re-search-forward "^\\([^ ]+\\) +\\([0-9]+\\) \\([^ ]+\\) " nil t) + (cons (string-to-number (match-string 2)) + (match-string 3)) + )))) + +(provide 'semantic/symref/global) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/symref/global" +;; End: + +;;; semantic/symref/global.el ends here diff --git a/lisp/cedet/semantic/symref/grep.el b/lisp/cedet/semantic/symref/grep.el new file mode 100644 index 00000000000..3033a41faaf --- /dev/null +++ b/lisp/cedet/semantic/symref/grep.el @@ -0,0 +1,202 @@ +;;; semantic/symref/grep.el --- Symref implementation using find/grep + +;;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Implement the symref tool API using the external tools find/grep. +;; +;; The symref GREP tool uses grep in a project to find symbol references. +;; This is a lowest-common-denominator tool with sucky performance that +;; can be used in small projects to find symbol references. + +(require 'semantic/symref) +(require 'grep) + +(defvar ede-minor-mode) +(declare-function ede-toplevel "ede/files") +(declare-function ede-project-root-directory "ede/files") + +;;; Code: + +;;; GREP +;;;###autoload +(defclass semantic-symref-tool-grep (semantic-symref-tool-baseclass) + ( + ) + "A symref tool implementation using grep. +This tool uses EDE to find he root of the project, then executes +find-grep in the project. The output is parsed for hits +and those hits returned.") + +(defvar semantic-symref-filepattern-alist + '((c-mode "*.[ch]") + (c++-mode "*.[chCH]" "*.[ch]pp" "*.cc" "*.hh") + (html-mode "*.s?html" "*.php") + ) + "List of major modes and file extension pattern regexp. +See find -regex man page for format.") + +(defun semantic-symref-derive-find-filepatterns (&optional mode) + "Derive a list of file patterns for the current buffer. +Looks first in `semantic-symref-filepattern-alist'. If it is not +there, it then looks in `auto-mode-alist', and attempts to derive something +from that. +Optional argument MODE specifies the `major-mode' to test." + ;; First, try the filepattern alist. + (let* ((mode (or mode major-mode)) + (pat (cdr (assoc mode semantic-symref-filepattern-alist)))) + (when (not pat) + ;; No hit, try auto-mode-alist. + (dolist (X auto-mode-alist) + (when (eq (cdr X) mode) + ;; Only take in simple patterns, so try to convert this one. + (let ((Xp + (cond ((string-match "\\\\\\.\\([^\\'>]+\\)\\\\'" (car X)) + (concat "*." (match-string 1 (car X)))) + (t nil)))) + (when Xp + (setq pat (cons Xp pat)))) + ))) + ;; Convert the list into some find-flags. + (cond ((= (length pat) 1) + (concat "-name \"" (car pat) "\"")) + ((consp pat) + (concat "\\( " + (mapconcat (lambda (s) + (concat "-name \"" s "\"")) + pat + " -o ") + " \\)")) + (t + (error "Configuration for `semantic-symref-tool-grep' needed for %s" major-mode)) + ))) + +(defvar semantic-symref-grep-expand-keywords + (condition-case nil + (let* ((kw (copy-alist grep-expand-keywords)) + (C (assoc "<C>" kw)) + (R (assoc "<R>" kw))) + (setcdr C 'grepflags) + (setcdr R 'greppattern) + kw) + (error nil)) + "Grep expand keywords used when expanding templates for symref.") + +(defun semantic-symref-grep-use-template (rootdir filepattern grepflags greppattern) + "Use the grep template expand feature to create a grep command. +ROOTDIR is the root location to run the `find' from. +FILEPATTERN is a string represeting find flags for searching file patterns. +GREPFLAGS are flags passed to grep, such as -n or -l. +GREPPATTERN is the pattren used by grep." + ;; We have grep-compute-defaults. Lets use it. + (grep-compute-defaults) + (let* ((grep-expand-keywords semantic-symref-grep-expand-keywords) + (cmd (grep-expand-template grep-find-template + greppattern + filepattern + rootdir))) + ;; For some reason, my default has no <D> in it. + (when (string-match "find \\(\\.\\)" cmd) + (setq cmd (replace-match rootdir t t cmd 1))) + ;;(message "New command: %s" cmd) + cmd)) + +(defmethod semantic-symref-perform-search ((tool semantic-symref-tool-grep)) + "Perform a search with Grep." + ;; Grep doesn't support some types of searches. + (let ((st (oref tool :searchtype))) + (when (not (eq st 'symbol)) + (error "Symref impl GREP does not support searchtype of %s" st)) + ) + ;; Find the root of the project, and do a find-grep... + (let* (;; Find the file patterns to use. + (pat (cdr (assoc major-mode semantic-symref-filepattern-alist))) + (rootdir (cond + ;; Project root via EDE. + ((eq (oref tool :searchscope) 'project) + (let ((rootproj (when (and (featurep 'ede) ede-minor-mode) + (ede-toplevel)))) + (if rootproj + (ede-project-root-directory rootproj) + default-directory))) + ;; Calculate the target files as just in + ;; this directory... cause I'm lazy. + ((eq (oref tool :searchscope) 'target) + default-directory) + )) + (filepattern (semantic-symref-derive-find-filepatterns)) + ;; Grep based flags. + (grepflags (cond ((eq (oref tool :resulttype) 'file) + "-l ") + (t "-n "))) + (greppat (cond ((eq (oref tool :searchtype) 'regexp) + (oref tool searchfor)) + (t + (concat "'\\<" (oref tool searchfor) "\\>'")))) + ;; Misc + (b (get-buffer-create "*Semantic SymRef*")) + (ans nil) + ) + + (save-excursion + (set-buffer b) + (erase-buffer) + (setq default-directory rootdir) + + (if (not (fboundp 'grep-compute-defaults)) + + ;; find . -type f -print0 | xargs -0 -e grep -nH -e + ;; Note : I removed -e as it is not posix, nor necessary it seems. + + (let ((cmd (concat "find " default-directory " -type f " filepattern " -print0 " + "| xargs -0 grep -H " grepflags "-e " greppat))) + ;;(message "Old command: %s" cmd) + (call-process "sh" nil b nil "-c" cmd) + ) + (let ((cmd (semantic-symref-grep-use-template rootdir filepattern grepflags greppat))) + (call-process "sh" nil b nil "-c" cmd)) + )) + (setq ans (semantic-symref-parse-tool-output tool b)) + ;; Return the answer + ans)) + +(defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-grep)) + "Parse one line of grep output, and return it as a match list. +Moves cursor to end of the match." + (cond ((eq (oref tool :resulttype) 'file) + ;; Search for files + (when (re-search-forward "^\\([^\n]+\\)$" nil t) + (match-string 1))) + (t + (when (re-search-forward "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):" nil t) + (cons (string-to-number (match-string 2)) + (match-string 1)) + )))) + +(provide 'semantic/symref/grep) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/symref/grep" +;; End: + +;;; semantic/symref/grep.el ends here diff --git a/lisp/cedet/semantic/symref/idutils.el b/lisp/cedet/semantic/symref/idutils.el new file mode 100644 index 00000000000..e94084e6f2d --- /dev/null +++ b/lisp/cedet/semantic/symref/idutils.el @@ -0,0 +1,78 @@ +;;; semantic/symref/idutils.el --- Symref implementation for idutils + +;;; Copyright (C) 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; This file is part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2, or (at +;; your option) any later version. + +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; Support IDUtils use in the Semantic Symref tool. + +(require 'cedet-idutils) +(require 'semantic/symref) + +;;; Code: +;;;###autoload +(defclass semantic-symref-tool-idutils (semantic-symref-tool-baseclass) + ( + ) + "A symref tool implementation using ID Utils. +The udutils command set can be used to generate lists of tags in a way +similar to that of `grep'. This tool will parse the output to generate +the hit list. + +See the function `cedet-idutils-search' for more details.") + +(defmethod semantic-symref-perform-search ((tool semantic-symref-tool-idutils)) + "Perform a search with IDUtils." + (let ((b (cedet-idutils-search (oref tool :searchfor) + (oref tool :searchtype) + (oref tool :resulttype) + (oref tool :searchscope) + )) + ) + (semantic-symref-parse-tool-output tool b) + )) + +(defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-idutils)) + "Parse one line of grep output, and return it as a match list. +Moves cursor to end of the match." + (cond ((eq (oref tool :resulttype) 'file) + ;; Search for files + (when (re-search-forward "^\\([^\n]+\\)$" nil t) + (match-string 1))) + ((eq (oref tool :searchtype) 'tagcompletions) + (when (re-search-forward "^\\([^ ]+\\) " nil t) + (match-string 1))) + (t + (when (re-search-forward "^\\([^ :]+\\):+\\([0-9]+\\):" nil t) + (cons (string-to-number (match-string 2)) + (expand-file-name (match-string 1) default-directory)) + )))) + +(provide 'semantic/symref/idutils) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/symref/idutils" +;; End: + +;;; semantic/symref/idutils.el ends here diff --git a/lisp/cedet/semantic/symref/list.el b/lisp/cedet/semantic/symref/list.el new file mode 100644 index 00000000000..08d2478283b --- /dev/null +++ b/lisp/cedet/semantic/symref/list.el @@ -0,0 +1,337 @@ +;;; semantic/symref/list.el --- Symref Output List UI. + +;;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; This file is part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2, or (at +;; your option) any later version. + +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; Provide a simple user facing API to finding symbol references. +;; +;; This UI will is the base of some refactoring tools. For any +;; refactor, the user will execture `semantic-symref' in a tag. Once +;; that data is collected, the output will be listed in a buffer. In +;; the output buffer, the user can then initiate different refactoring +;; operations. +;; +;; NOTE: Need to add some refactoring tools. + +(require 'semantic/symref) +(require 'semantic/complete) +(require 'pulse) + +;;; Code: + +;;;###autoload +(defun semantic-symref () + "Find references to the current tag. +This command uses the currently configured references tool within the +current project to find references to the current tag. The +references are the organized by file and the name of the function +they are used in. +Display the references in`semantic-symref-results-mode'" + (interactive) + (semantic-fetch-tags) + (let ((ct (semantic-current-tag)) + (res nil) + ) + ;; Must have a tag... + (when (not ct) (error "Place cursor inside tag to be searched for")) + ;; Check w/ user. + (when (not (y-or-n-p (format "Find references for %s? " (semantic-tag-name ct)))) + (error "Quit")) + ;; Gather results and tags + (message "Gathering References...") + (setq res (semantic-symref-find-references-by-name (semantic-tag-name ct))) + (semantic-symref-produce-list-on-results res (semantic-tag-name ct)))) + +;;;###autoload +(defun semantic-symref-symbol (sym) + "Find references to the symbol SYM. +This command uses the currently configured references tool within the +current project to find references to the input SYM. The +references are the organized by file and the name of the function +they are used in. +Display the references in`semantic-symref-results-mode'" + (interactive (list (semantic-tag-name (semantic-complete-read-tag-buffer-deep + "Symrefs for: ")))) + (semantic-fetch-tags) + (let ((res nil) + ) + ;; Gather results and tags + (message "Gathering References...") + (setq res (semantic-symref-find-references-by-name sym)) + (semantic-symref-produce-list-on-results res sym))) + + +(defun semantic-symref-produce-list-on-results (res str) + "Produce a symref list mode buffer on the results RES." + (when (not res) (error "No references found")) + (semantic-symref-result-get-tags res t) + (message "Gathering References...done") + ;; Build a refrences buffer. + (let ((buff (get-buffer-create + (format "*Symref %s" str))) + ) + (switch-to-buffer-other-window buff) + (set-buffer buff) + (semantic-symref-results-mode res)) + ) + +;;; RESULTS MODE +;; +(defgroup semantic-symref-results-mode nil + "Symref Results group." + :group 'semantic) + +(defvar semantic-symref-results-mode-map + (let ((km (make-sparse-keymap))) + (define-key km "\C-i" 'forward-button) + (define-key km "\M-C-i" 'backward-button) + (define-key km " " 'push-button) + (define-key km "-" 'semantic-symref-list-toggle-showing) + (define-key km "=" 'semantic-symref-list-toggle-showing) + (define-key km "+" 'semantic-symref-list-toggle-showing) + (define-key km "n" 'semantic-symref-list-next-line) + (define-key km "p" 'semantic-symref-list-prev-line) + (define-key km "q" 'semantic-symref-hide-buffer) + km) + "Keymap used in `semantic-symref-results-mode'.") + +(defcustom semantic-symref-results-mode-hook nil + "*Hook run when `semantic-symref-results-mode' starts." + :group 'semantic-symref + :type 'hook) + +(defvar semantic-symref-current-results nil + "The current results in a results mode buffer.") + +(defun semantic-symref-results-mode (results) + "Major-mode for displaying Semantic Symbol Reference RESULTS. +RESULTS is an object of class `semantic-symref-results'." + (interactive) + (kill-all-local-variables) + (setq major-mode 'semantic-symref-results-mode + mode-name "Symref" + ) + (use-local-map semantic-symref-results-mode-map) + (set (make-local-variable 'semantic-symref-current-results) + results) + (semantic-symref-results-dump results) + (goto-char (point-min)) + (buffer-disable-undo) + (set (make-local-variable 'font-lock-global-modes) nil) + (font-lock-mode -1) + (run-hooks 'semantic-symref-results-mode-hook) + ) + +(defun semantic-symref-hide-buffer () + "Hide buffer with sematinc-symref results" + (interactive) + (bury-buffer)) + +(defcustom semantic-symref-results-summary-function 'semantic-format-tag-prototype + "*Function to use when creating items in Imenu. +Some useful functions are found in `semantic-format-tag-functions'." + :group 'semantic-symref + :type semantic-format-tag-custom-list) + +(defun semantic-symref-results-dump (results) + "Dump the RESULTS into the current buffer." + ;; Get ready for the insert. + (toggle-read-only -1) + (erase-buffer) + + ;; Insert the contents. + (let ((lastfile nil) + ) + (dolist (T (oref results :hit-tags)) + + (when (not (equal lastfile (semantic-tag-file-name T))) + (setq lastfile (semantic-tag-file-name T)) + (insert-button lastfile + 'mouse-face 'custom-button-pressed-face + 'action 'semantic-symref-rb-goto-file + 'tag T + ) + (insert "\n")) + + (insert " ") + (insert-button "[+]" + 'mouse-face 'highlight + 'face nil + 'action 'semantic-symref-rb-toggle-expand-tag + 'tag T + 'state 'closed) + (insert " ") + (insert-button (funcall semantic-symref-results-summary-function + T nil t) + 'mouse-face 'custom-button-pressed-face + 'face nil + 'action 'semantic-symref-rb-goto-tag + 'tag T) + (insert "\n") + + )) + + ;; Clean up the mess + (toggle-read-only 1) + (set-buffer-modified-p nil) + ) + +;;; Commands for semantic-symref-results +;; +(defun semantic-symref-list-toggle-showing () + "Toggle showing the contents below the current line." + (interactive) + (beginning-of-line) + (when (re-search-forward "\\[[-+]\\]" (point-at-eol) t) + (forward-char -1) + (push-button))) + +(defun semantic-symref-rb-toggle-expand-tag (&optional button) + "Go to the file specified in the symref results buffer. +BUTTON is the button that was clicked." + (interactive) + (let* ((tag (button-get button 'tag)) + (buff (semantic-tag-buffer tag)) + (hits (semantic--tag-get-property tag :hit)) + (state (button-get button 'state)) + (text nil) + ) + (cond + ((eq state 'closed) + (toggle-read-only -1) + (save-excursion + (set-buffer buff) + (dolist (H hits) + (goto-char (point-min)) + (forward-line (1- H)) + (beginning-of-line) + (back-to-indentation) + (setq text (cons (buffer-substring (point) (point-at-eol)) text))) + (setq text (nreverse text)) + ) + (goto-char (button-start button)) + (forward-char 1) + (delete-char 1) + (insert "-") + (button-put button 'state 'open) + (save-excursion + (end-of-line) + (while text + (insert "\n") + (insert " ") + (insert-button (car text) + 'mouse-face 'highlight + 'face nil + 'action 'semantic-symref-rb-goto-match + 'tag tag + 'line (car hits)) + (setq text (cdr text) + hits (cdr hits)))) + (toggle-read-only 1) + ) + ((eq state 'open) + (toggle-read-only -1) + (button-put button 'state 'closed) + ;; Delete the various bits. + (goto-char (button-start button)) + (forward-char 1) + (delete-char 1) + (insert "+") + (save-excursion + (end-of-line) + (forward-char 1) + (delete-region (point) + (save-excursion + (forward-char 1) + (forward-line (length hits)) + (point)))) + (toggle-read-only 1) + ) + )) + ) + +(defun semantic-symref-rb-goto-file (&optional button) + "Go to the file specified in the symref results buffer. +BUTTON is the button that was clicked." + (let* ((tag (button-get button 'tag)) + (buff (semantic-tag-buffer tag)) + (win (selected-window)) + ) + (switch-to-buffer-other-window buff) + (pulse-momentary-highlight-one-line (point)) + (when (eq last-command-event ?\s) (select-window win)) + )) + + +(defun semantic-symref-rb-goto-tag (&optional button) + "Go to the file specified in the symref results buffer. +BUTTON is the button that was clicked." + (interactive) + (let* ((tag (button-get button 'tag)) + (buff (semantic-tag-buffer tag)) + (win (selected-window)) + ) + (switch-to-buffer-other-window buff) + (semantic-go-to-tag tag) + (pulse-momentary-highlight-one-line (point)) + (when (eq last-command-event ?\s) (select-window win)) + ) + ) + +(defun semantic-symref-rb-goto-match (&optional button) + "Go to the file specified in the symref results buffer. +BUTTON is the button that was clicked." + (interactive) + (let* ((tag (button-get button 'tag)) + (line (button-get button 'line)) + (buff (semantic-tag-buffer tag)) + (win (selected-window)) + ) + (switch-to-buffer-other-window buff) + (with-no-warnings (goto-line line)) + (pulse-momentary-highlight-one-line (point)) + (when (eq last-command-event ?\s) (select-window win)) + ) + ) + +(defun semantic-symref-list-next-line () + "Next line in `semantic-symref-results-mode'." + (interactive) + (forward-line 1) + (back-to-indentation)) + +(defun semantic-symref-list-prev-line () + "Next line in `semantic-symref-results-mode'." + (interactive) + (forward-line -1) + (back-to-indentation)) + +(provide 'semantic/symref/list) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/symref/list" +;; End: + +;;; semantic/symref/list.el ends here diff --git a/lisp/cedet/semantic/tag-file.el b/lisp/cedet/semantic/tag-file.el new file mode 100644 index 00000000000..f558db99877 --- /dev/null +++ b/lisp/cedet/semantic/tag-file.el @@ -0,0 +1,220 @@ +;;; semantic/tag-file.el --- Routines that find files based on tags. + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, +;;; 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: +;; +;; A tag, by itself, can have representations in several files. +;; These routines will find those files. + +(require 'semantic/tag) + +(defvar ede-minor-mode) +(declare-function semanticdb-table-child-p "semantic/db") +(declare-function semanticdb-get-buffer "semantic/db") +(declare-function semantic-dependency-find-file-on-path "semantic/dep") +(declare-function ede-toplevel "ede/files") + +;;; Code: + +;;; Location a TAG came from. +;; +;;;###autoload +(define-overloadable-function semantic-go-to-tag (tag &optional parent) + "Go to the location of TAG. +TAG may be a stripped element, in which case PARENT specifies a +parent tag that has position information. +PARENT can also be a `semanticdb-table' object." + (:override + (save-match-data + (cond ((semantic-tag-in-buffer-p tag) + ;; We have a linked tag, go to that buffer. + (set-buffer (semantic-tag-buffer tag))) + ((semantic-tag-file-name tag) + ;; If it didn't have a buffer, but does have a file + ;; name, then we need to get to that file so the tag + ;; location is made accurate. + (set-buffer (find-file-noselect (semantic-tag-file-name tag)))) + ((and parent (semantic-tag-p parent) (semantic-tag-in-buffer-p parent)) + ;; The tag had nothing useful, but we have a parent with + ;; a buffer, then go there. + (set-buffer (semantic-tag-buffer parent))) + ((and parent (semantic-tag-p parent) (semantic-tag-file-name parent)) + ;; Tag had nothing, and the parent only has a file-name, then + ;; find that file, and switch to that buffer. + (set-buffer (find-file-noselect (semantic-tag-file-name parent)))) + ((and parent (featurep 'semantic/db) + (semanticdb-table-child-p parent)) + (set-buffer (semanticdb-get-buffer parent))) + (t + ;; Well, just assume things are in the current buffer. + nil + ))) + ;; We should be in the correct buffer now, try and figure out + ;; where the tag is. + (cond ((semantic-tag-with-position-p tag) + ;; If it's a number, go there + (goto-char (semantic-tag-start tag))) + ((semantic-tag-with-position-p parent) + ;; Otherwise, it's a trimmed vector, such as a parameter, + ;; or a structure part. If there is a parent, we can use it + ;; as a bounds for searching. + (goto-char (semantic-tag-start parent)) + ;; Here we make an assumption that the text returned by + ;; the parser and concocted by us actually exists + ;; in the buffer. + (re-search-forward (semantic-tag-name tag) + (semantic-tag-end parent) + t)) + ((semantic-tag-get-attribute tag :line) + ;; The tag has a line number in it. Go there. + (goto-char (point-min)) + (forward-line (1- (semantic-tag-get-attribute tag :line)))) + ((and (semantic-tag-p parent) (semantic-tag-get-attribute parent :line)) + ;; The tag has a line number in it. Go there. + (goto-char (point-min)) + (forward-line (1- (semantic-tag-get-attribute parent :line))) + (re-search-forward (semantic-tag-name tag) nil t)) + (t + ;; Take a guess that the tag has a unique name, and just + ;; search for it from the beginning of the buffer. + (goto-char (point-min)) + (re-search-forward (semantic-tag-name tag) nil t))) + ) + ) + +(make-obsolete-overload 'semantic-find-nonterminal + 'semantic-go-to-tag) + +;;; Dependencies +;; +;; A tag which is of type 'include specifies a dependency. +;; Dependencies usually represent a file of some sort. +;; Find the file described by a dependency. + +;;;###autoload +(define-overloadable-function semantic-dependency-tag-file (&optional tag) + "Find the filename represented from TAG. +Depends on `semantic-dependency-include-path' for searching. Always searches +`.' first, then searches additional paths." + (or tag (setq tag (car (semantic-find-tag-by-overlay nil)))) + (unless (semantic-tag-of-class-p tag 'include) + (signal 'wrong-type-argument (list tag 'include))) + (save-excursion + (let ((result nil) + (default-directory default-directory) + (edefind nil) + (tag-fname nil)) + (cond ((semantic-tag-in-buffer-p tag) + ;; If the tag has an overlay and buffer associated with it, + ;; switch to that buffer so that we get the right override metohds. + (set-buffer (semantic-tag-buffer tag))) + ((semantic-tag-file-name tag) + ;; If it didn't have a buffer, but does have a file + ;; name, then we need to get to that file so the tag + ;; location is made accurate. + ;;(set-buffer (find-file-noselect (semantic-tag-file-name tag))) + ;; + ;; 2/3/08 + ;; The above causes unnecessary buffer loads all over the place. Ick! + ;; All we really need is for 'default-directory' to be set correctly. + (setq default-directory (file-name-directory (semantic-tag-file-name tag))) + )) + ;; Setup the filename represented by this include + (setq tag-fname (semantic-tag-include-filename tag)) + + ;; First, see if this file exists in the current EDE project + (if (and (fboundp 'ede-expand-filename) ede-minor-mode + (setq edefind + (condition-case nil + (let ((proj (ede-toplevel))) + (when proj + (ede-expand-filename proj tag-fname))) + (error nil)))) + (setq result edefind)) + (if (not result) + (setq result + ;; I don't have a plan for refreshing tags with a dependency + ;; stuck on them somehow. I'm thinking that putting a cache + ;; onto the dependancy finding with a hash table might be best. + ;;(if (semantic--tag-get-property tag 'dependency-file) + ;; (semantic--tag-get-property tag 'dependency-file) + (:override + (save-excursion + (require 'semantic/dep) + (semantic-dependency-find-file-on-path + tag-fname (semantic-tag-include-system-p tag)))) + ;; ) + )) + (if (stringp result) + (progn + (semantic--tag-put-property tag 'dependency-file result) + result) + ;; @todo: Do something to make this get flushed w/ + ;; when the path is changed. + ;; @undo: Just eliminate + ;; (semantic--tag-put-property tag 'dependency-file 'none) + nil) + ))) + +(make-obsolete-overload 'semantic-find-dependency + 'semantic-dependency-tag-file) + +;;; PROTOTYPE FILE +;; +;; In C, a function in the .c file often has a representation in a +;; corresponding .h file. This routine attempts to find the +;; prototype file a given source file would be associated with. +;; This can be used by prototype manager programs. +(define-overloadable-function semantic-prototype-file (buffer) + "Return a file in which prototypes belonging to BUFFER should be placed. +Default behavior (if not overridden) looks for a token specifying the +prototype file, or the existence of an EDE variable indicating which +file prototypes belong in." + (:override + ;; Perform some default behaviors + (if (and (fboundp 'ede-header-file) ede-minor-mode) + (save-excursion + (set-buffer buffer) + (ede-header-file)) + ;; No EDE options for a quick answer. Search. + (save-excursion + (set-buffer buffer) + (if (re-search-forward "::Header:: \\([a-zA-Z0-9.]+\\)" nil t) + (match-string 1)))))) + +(semantic-alias-obsolete 'semantic-find-nonterminal + 'semantic-go-to-tag) + +(semantic-alias-obsolete 'semantic-find-dependency + 'semantic-dependency-tag-file) + + +(provide 'semantic/tag-file) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/tag-file" +;; End: + +;;; semantic/tag-file.el ends here diff --git a/lisp/cedet/semantic/tag-ls.el b/lisp/cedet/semantic/tag-ls.el new file mode 100644 index 00000000000..82d628cbf38 --- /dev/null +++ b/lisp/cedet/semantic/tag-ls.el @@ -0,0 +1,256 @@ +;;; semantic/tag-ls.el --- Language Specific override functions for tags + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007, 2008 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; There are some features of tags that are too langauge dependent to +;; put in the core `semantic-tag' functionality. For instance, the +;; protection of a tag (as specified by UML) could be almost anything. +;; In Java, it is a type specifier. In C, there is a label. This +;; informatin can be derived, and thus should not be stored in the tag +;; itself. These are the functions that languages can use to derive +;; the information. + +(require 'semantic) + +;;; Code: + +;;; UML features: +;; +;; UML can represent several types of features of a tag +;; such as the `protection' of a symbol, or if it is abstract, +;; leaf, etc. Learn about UML to catch onto the lingo. + +(define-overloadable-function semantic-tag-calculate-parent (tag) + "Attempt to calculate the parent of TAG. +The default behavior (if not overriden with `tag-calculate-parent') +is to search a buffer found with TAG, and if externally defined, +search locally, then semanticdb for that tag (when enabled.)") + +(defun semantic-tag-calculate-parent-default (tag) + "Attempt to calculate the parent of TAG." + (when (semantic-tag-in-buffer-p tag) + (save-excursion + (set-buffer (semantic-tag-buffer tag)) + (save-excursion + (goto-char (semantic-tag-start tag)) + (semantic-current-tag-parent)) + ))) + +(define-overloadable-function semantic-tag-protection (tag &optional parent) + "Return protection information about TAG with optional PARENT. +This function returns on of the following symbols: + nil - No special protection. Language dependent. + 'public - Anyone can access this TAG. + 'private - Only methods in the local scope can access TAG. + 'protected - Like private for outside scopes, like public for child + classes. +Some languages may choose to provide additional return symbols specific +to themselves. Use of this function should allow for this. + +The default behavior (if not overridden with `tag-protection' +is to return a symbol based on type modifiers." + (and (not parent) + (semantic-tag-overlay tag) + (semantic-tag-in-buffer-p tag) + (setq parent (semantic-tag-calculate-parent tag))) + (:override)) + +(make-obsolete-overload 'semantic-nonterminal-protection + 'semantic-tag-protection) + +(defun semantic-tag-protection-default (tag &optional parent) + "Return the protection of TAG as a child of PARENT default action. +See `semantic-tag-protection'." + (let ((mods (semantic-tag-modifiers tag)) + (prot nil)) + (while (and (not prot) mods) + (if (stringp (car mods)) + (let ((s (car mods))) + (setq prot + ;; A few silly defaults to get things started. + (cond ((or (string= s "public") + (string= s "extern") + (string= s "export")) + 'public) + ((string= s "private") + 'private) + ((string= s "protected") + 'protected))))) + (setq mods (cdr mods))) + prot)) + +(defun semantic-tag-protected-p (tag protection &optional parent) + "Non-nil if TAG is is protected. +PROTECTION is a symbol which can be returned by the method +`semantic-tag-protection'. +PARENT is the parent data type which contains TAG. + +For these PROTECTIONs, true is returned if TAG is: +@table @asis +@item nil + Always true +@item private + True if nil. +@item protected + True if private or nil. +@item public + True if private, protected, or nil. +@end table" + (if (null protection) + t + (let ((tagpro (semantic-tag-protection tag parent))) + (or (and (eq protection 'private) + (null tagpro)) + (and (eq protection 'protected) + (or (null tagpro) + (eq tagpro 'private))) + (and (eq protection 'public) + (not (eq tagpro 'public))))) + )) + +(define-overloadable-function semantic-tag-abstract-p (tag &optional parent) + "Return non nil if TAG is abstract. +Optional PARENT is the parent tag of TAG. +In UML, abstract methods and classes have special meaning and behavior +in how methods are overridden. In UML, abstract methods are italicized. + +The default behavior (if not overridden with `tag-abstract-p' +is to return true if `abstract' is in the type modifiers.") + +(make-obsolete-overload 'semantic-nonterminal-abstract + 'semantic-tag-abstract-p) + +(defun semantic-tag-abstract-p-default (tag &optional parent) + "Return non-nil if TAG is abstract as a child of PARENT default action. +See `semantic-tag-abstract-p'." + (let ((mods (semantic-tag-modifiers tag)) + (abs nil)) + (while (and (not abs) mods) + (if (stringp (car mods)) + (setq abs (or (string= (car mods) "abstract") + (string= (car mods) "virtual")))) + (setq mods (cdr mods))) + abs)) + +(define-overloadable-function semantic-tag-leaf-p (tag &optional parent) + "Return non nil if TAG is leaf. +Optional PARENT is the parent tag of TAG. +In UML, leaf methods and classes have special meaning and behavior. + +The default behavior (if not overridden with `tag-leaf-p' +is to return true if `leaf' is in the type modifiers.") + +(make-obsolete-overload 'semantic-nonterminal-leaf + 'semantic-tag-leaf-p) + +(defun semantic-tag-leaf-p-default (tag &optional parent) + "Return non-nil if TAG is leaf as a child of PARENT default action. +See `semantic-tag-leaf-p'." + (let ((mods (semantic-tag-modifiers tag)) + (leaf nil)) + (while (and (not leaf) mods) + (if (stringp (car mods)) + ;; Use java FINAL as example default. There is none + ;; for C/C++ + (setq leaf (string= (car mods) "final"))) + (setq mods (cdr mods))) + leaf)) + +(define-overloadable-function semantic-tag-static-p (tag &optional parent) + "Return non nil if TAG is static. +Optional PARENT is the parent tag of TAG. +In UML, static methods and attributes mean that they are allocated +in the parent class, and are not instance specific. +UML notation specifies that STATIC entries are underlined.") + +(defun semantic-tag-static-p-default (tag &optional parent) + "Return non-nil if TAG is static as a child of PARENT default action. +See `semantic-tag-static-p'." + (let ((mods (semantic-tag-modifiers tag)) + (static nil)) + (while (and (not static) mods) + (if (stringp (car mods)) + (setq static (string= (car mods) "static"))) + (setq mods (cdr mods))) + static)) + +;;;###autoload +(define-overloadable-function semantic-tag-prototype-p (tag) + "Return non nil if TAG is a prototype. +For some laguages, such as C, a prototype is a declaration of +something without an implementation." + ) + +(defun semantic-tag-prototype-p-default (tag) + "Non-nil if TAG is a prototype." + (let ((p (semantic-tag-get-attribute tag :prototype-flag))) + (cond + ;; Trust the parser author. + (p p) + ;; Empty types might be a prototype. + ;; @todo - make this better. + ((eq (semantic-tag-class tag) 'type) + (not (semantic-tag-type-members tag))) + ;; No other heuristics. + (t nil)) + )) + +;;; FULL NAMES +;; +;; For programmer convenience, a full name is not specified in source +;; code. Instead some abbreviation is made, and the local environment +;; will contain the info needed to determine the full name. + +(define-overloadable-function semantic-tag-full-name (tag &optional stream-or-buffer) + "Return the fully qualified name of TAG in the package hierarchy. +STREAM-OR-BUFFER can be anything convertable by `semantic-something-to-stream', +but must be a toplevel semantic tag stream that contains TAG. +A Package Hierarchy is defined in UML by the way classes and methods +are organized on disk. Some language use this concept such that a +class can be accessed via it's fully qualified name, (such as Java.) +Other languages qualify names within a Namespace (such as C++) which +result in a different package like structure. Languages which do not +override this function with `tag-full-name' will use +`semantic-tag-name'. Override functions only need to handle +STREAM-OR-BUFFER with a tag stream value, or nil." + (let ((stream (semantic-something-to-tag-table + (or stream-or-buffer tag)))) + (:override-with-args (tag stream)))) + +(make-obsolete-overload 'semantic-nonterminal-full-name + 'semantic-tag-full-name) + +(defun semantic-tag-full-name-default (tag stream) + "Default method for `semantic-tag-full-name'. +Return the name of TAG found in the toplevel STREAM." + (semantic-tag-name tag)) + +(provide 'semantic/tag-ls) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/tag-ls" +;; End: + +;;; semantic/tag-ls.el ends here diff --git a/lisp/cedet/semantic/tag-write.el b/lisp/cedet/semantic/tag-write.el new file mode 100644 index 00000000000..88d0ecb2f24 --- /dev/null +++ b/lisp/cedet/semantic/tag-write.el @@ -0,0 +1,179 @@ +;;; semantic/tag-write.el --- Write tags to a text stream + +;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <eric@siege-engine.com> + +;; 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: +;; +;; Routine for writing out a list of tags to a text stream. +;; +;; These routines will be used by semanticdb to output a tag list into +;; a text stream to be saved to a file. Ideally, you could use tag streams +;; to share tags between processes as well. +;; +;; As a bonus, these routines will also validate the tag structure, and make sure +;; that they conform to good semantic tag hygene. +;; + +(require 'semantic) + +;;; Code: +(defun semantic-tag-write-one-tag (tag &optional indent) + "Write a single tag TAG to standard out. +INDENT is the amount of indentation to use for this tag." + (when (not (semantic-tag-p tag)) + (signal 'wrong-type-argument (list tag 'semantic-tag-p))) + (when (not indent) (setq indent 0)) + ;(princ (make-string indent ? )) + (princ "(\"") + ;; Base parts + (let ((name (semantic-tag-name tag)) + (class (semantic-tag-class tag))) + (princ name) + (princ "\" ") + (princ (symbol-name class)) + ) + (let ((attr (semantic-tag-attributes tag)) + ) + ;; Attributes + (cond ((not attr) + (princ " nil")) + + ((= (length attr) 2) ;; One item + (princ " (") + (semantic-tag-write-one-attribute attr indent) + (princ ")") + ) + (t + ;; More than one tag. + (princ "\n") + (princ (make-string (+ indent 3) ? )) + (princ "(") + (while attr + (semantic-tag-write-one-attribute attr (+ indent 4)) + (setq attr (cdr (cdr attr))) + (when attr + (princ "\n") + (princ (make-string (+ indent 4) ? ))) + ) + (princ ")\n") + (princ (make-string (+ indent 3) ? )) + )) + ;; Properties - for now, always nil. + (let ((rs (semantic--tag-get-property tag 'reparse-symbol))) + (if (not rs) + (princ " nil") + ;; Else, put in the property list. + (princ " (reparse-symbol ") + (princ (symbol-name rs)) + (princ ")")) + )) + ;; Overlay + (if (semantic-tag-with-position-p tag) + (let ((bounds (semantic-tag-bounds tag))) + (princ " ") + (prin1 (apply 'vector bounds)) + ) + (princ " nil")) + ;; End it. + (princ ")") + ) + +(defun semantic-tag-write-tag-list (tlist &optional indent dontaddnewline) + "Write the tag list TLIST to the current stream. +INDENT indicates the current indentation level. +If optional DONTADDNEWLINE is non-nil, then don't add a newline." + (if (not indent) + (setq indent 0) + (unless dontaddnewline + ;; Assume cursor at end of current line. Add a CR, and make the list. + (princ "\n") + (princ (make-string indent ? )))) + (princ "( ") + (while tlist + (if (semantic-tag-p (car tlist)) + (semantic-tag-write-one-tag (car tlist) (+ indent 2)) + ;; If we don't have a tag in the tag list, use the below hack, and hope + ;; it doesn't contain anything bad. If we find something bad, go back here + ;; and start extending what's expected here. + (princ (format "%S" (car tlist)))) + (setq tlist (cdr tlist)) + (when tlist + (princ "\n") + (princ (make-string (+ indent 2) ? ))) + ) + (princ ")") + (princ (make-string indent ? )) + ) + + +;; Writing out random stuff. +(defun semantic-tag-write-one-attribute (attrs indent) + "Write out one attribute from the head of the list of attributes ATTRS. +INDENT is the current amount of indentation." + (when (not attrs) (signal 'wrong-type-argument (list 'listp attrs))) + (when (not (symbolp (car attrs))) (error "Bad Attribute List in tag")) + + (princ (symbol-name (car attrs))) + (princ " ") + (semantic-tag-write-one-value (car (cdr attrs)) indent) + ) + +(defun semantic-tag-write-one-value (value indent) + "Write out a VALUE for something in a tag. +INDENT is the current tag indentation. +Items that are long lists of tags may need their own line." + (cond + ;; Another tag. + ((semantic-tag-p value) + (semantic-tag-write-one-tag value (+ indent 2))) + ;; A list of more tags + ((and (listp value) (semantic-tag-p (car value))) + (semantic-tag-write-tag-list value (+ indent 2)) + ) + ;; Some arbitrary data. + (t + (let ((str (format "%S" value))) + ;; Protect against odd data types in tags. + (if (= (aref str 0) ?#) + (progn + (princ "nil") + (message "Warning: Value %s not writable in tag." str)) + (princ str))))) + ) +;;; EIEIO USAGE +;;;###autoload +(defun semantic-tag-write-list-slot-value (value) + "Write out the VALUE of a slot for EIEIO. +The VALUE is a list of tags." + (if (not value) + (princ "nil") + (princ "\n '") + (semantic-tag-write-tag-list value 10 t) + )) + +(provide 'semantic/tag-write) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/tag-write" +;; End: + +;;; semantic/tag-write.el ends here diff --git a/lisp/cedet/semantic/tag.el b/lisp/cedet/semantic/tag.el new file mode 100644 index 00000000000..608f4f403ee --- /dev/null +++ b/lisp/cedet/semantic/tag.el @@ -0,0 +1,1365 @@ +;;; semantic/tag.el --- tag creation and access + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, +;;; 2008, 2009 Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; I. The core production of semantic is the list of tags produced by the +;; different parsers. This file provides 3 APIs related to tag access: +;; +;; 1) Primitive Tag Access +;; There is a set of common features to all tags. These access +;; functions can get these values. +;; 2) Standard Tag Access +;; A Standard Tag should be produced by most traditional languages +;; with standard styles common to typed object oriented languages. +;; These functions can access these data elements from a tag. +;; 3) Generic Tag Access +;; Access to tag structure in a more direct way. +;; ** May not be forward compatible. +;; +;; II. There is also an API for tag creation. Use `semantic-tag' to create +;; a new tag. +;; +;; III. Tag Comparison. Allows explicit or comparitive tests to see +;; if two tags are the same. + +;;; Code: +;; + +;; Keep this only so long as we have obsolete fcns. +(require 'semantic/fw) +(require 'semantic/lex) + +(declare-function semantic-analyze-split-name "semantic/analyze/fcn") +(declare-function semantic-fetch-tags "semantic") +(declare-function semantic-clear-toplevel-cache "semantic") + +(defconst semantic-tag-version "2.0pre7" + "Version string of semantic tags made with this code.") + +(defconst semantic-tag-incompatible-version "1.0" + "Version string of semantic tags which are not currently compatible. +These old style tags may be loaded from a file with semantic db. +In this case, we must flush the old tags and start over.") + +;;; Primitive Tag access system: +;; +;; Raw tags in semantic are lists of 5 elements: +;; +;; (NAME CLASS ATTRIBUTES PROPERTIES OVERLAY) +;; +;; Where: +;; +;; - NAME is a string that represents the tag name. +;; +;; - CLASS is a symbol that represent the class of the tag (for +;; example, usual classes are `type', `function', `variable', +;; `include', `package', `code'). +;; +;; - ATTRIBUTES is a public list of attributes that describes +;; language data represented by the tag (for example, a variable +;; can have a `:constant-flag' attribute, a function an `:arguments' +;; attribute, etc.). +;; +;; - PROPERTIES is a private list of properties used internally. +;; +;; - OVERLAY represent the location of data described by the tag. +;; + +(defsubst semantic-tag-name (tag) + "Return the name of TAG. +For functions, variables, classes, typedefs, etc., this is the identifier +that is being defined. For tags without an obvious associated name, this +may be the statement type, e.g., this may return @code{print} for python's +print statement." + (car tag)) + +(defsubst semantic-tag-class (tag) + "Return the class of TAG. +That is, the symbol 'variable, 'function, 'type, or other. +There is no limit to the symbols that may represent the class of a tag. +Each parser generates tags with classes defined by it. + +For functional languages, typical tag classes are: + +@table @code +@item type +Data types, named map for a memory block. +@item function +A function or method, or named execution location. +@item variable +A variable, or named storage for data. +@item include +Statement that represents a file from which more tags can be found. +@item package +Statement that declairs this file's package name. +@item code +Code that has not name or binding to any other symbol, such as in a script. +@end table +" + (nth 1 tag)) + +(defsubst semantic-tag-attributes (tag) + "Return the list of public attributes of TAG. +That is a property list: (ATTRIBUTE-1 VALUE-1 ATTRIBUTE-2 VALUE-2...)." + (nth 2 tag)) + +(defsubst semantic-tag-properties (tag) + "Return the list of private properties of TAG. +That is a property list: (PROPERTY-1 VALUE-1 PROPERTY-2 VALUE-2...)." + (nth 3 tag)) + +(defsubst semantic-tag-overlay (tag) + "Return the OVERLAY part of TAG. +That is, an overlay or an unloaded buffer representation. +This function can also return an array of the form [ START END ]. +This occurs for tags that are not currently linked into a buffer." + (nth 4 tag)) + +(defsubst semantic--tag-overlay-cdr (tag) + "Return the cons cell whose car is the OVERLAY part of TAG. +That function is for internal use only." + (nthcdr 4 tag)) + +(defsubst semantic--tag-set-overlay (tag overlay) + "Set the overlay part of TAG with OVERLAY. +That function is for internal use only." + (setcar (semantic--tag-overlay-cdr tag) overlay)) + +(defsubst semantic-tag-start (tag) + "Return the start location of TAG." + (let ((o (semantic-tag-overlay tag))) + (if (semantic-overlay-p o) + (semantic-overlay-start o) + (aref o 0)))) + +(defsubst semantic-tag-end (tag) + "Return the end location of TAG." + (let ((o (semantic-tag-overlay tag))) + (if (semantic-overlay-p o) + (semantic-overlay-end o) + (aref o 1)))) + +(defsubst semantic-tag-bounds (tag) + "Return the location (START END) of data TAG describes." + (list (semantic-tag-start tag) + (semantic-tag-end tag))) + +(defun semantic-tag-set-bounds (tag start end) + "In TAG, set the START and END location of data it describes." + (let ((o (semantic-tag-overlay tag))) + (if (semantic-overlay-p o) + (semantic-overlay-move o start end) + (semantic--tag-set-overlay tag (vector start end))))) + +(defun semantic-tag-in-buffer-p (tag) + "Return the buffer TAG resides in IFF tag is already in a buffer. +If a tag is not in a buffer, return nil." + (let ((o (semantic-tag-overlay tag))) + ;; TAG is currently linked to a buffer, return it. + (when (and (semantic-overlay-p o) + (semantic-overlay-live-p o)) + (semantic-overlay-buffer o)))) + +(defsubst semantic--tag-get-property (tag property) + "From TAG, extract the value of PROPERTY. +Return the value found, or nil if PROPERTY is not one of the +properties of TAG. +That function is for internal use only." + (plist-get (semantic-tag-properties tag) property)) + +(defun semantic-tag-buffer (tag) + "Return the buffer TAG resides in. +If TAG has an originating file, read that file into a (maybe new) +buffer, and return it. +Return nil if there is no buffer for this tag." + (let ((buff (semantic-tag-in-buffer-p tag))) + (if buff + buff + ;; TAG has an originating file, read that file into a buffer, and + ;; return it. + (if (semantic--tag-get-property tag :filename) + (save-match-data + (find-file-noselect (semantic--tag-get-property tag :filename))) + ;; TAG is not in Emacs right now, no buffer is available. + )))) + +(defun semantic-tag-mode (&optional tag) + "Return the major mode active for TAG. +TAG defaults to the tag at point in current buffer. +If TAG has a :mode property return it. +If point is inside TAG bounds, return the major mode active at point. +Return the major mode active at beginning of TAG otherwise. +See also the function `semantic-ctxt-current-mode'." + (or tag (setq tag (semantic-current-tag))) + (or (semantic--tag-get-property tag :mode) + (let ((buffer (semantic-tag-buffer tag)) + (start (semantic-tag-start tag)) + (end (semantic-tag-end tag))) + (save-excursion + (and buffer (set-buffer buffer)) + ;; Unless point is inside TAG bounds, move it to the + ;; beginning of TAG. + (or (and (>= (point) start) (< (point) end)) + (goto-char start)) + (semantic-ctxt-current-mode))))) + +(defsubst semantic--tag-attributes-cdr (tag) + "Return the cons cell whose car is the ATTRIBUTES part of TAG. +That function is for internal use only." + (nthcdr 2 tag)) + +(defsubst semantic-tag-put-attribute (tag attribute value) + "Change value in TAG of ATTRIBUTE to VALUE. +If ATTRIBUTE already exists, its value is set to VALUE, otherwise the +new ATTRIBUTE VALUE pair is added. +Return TAG. +Use this function in a parser when not all attributes are known at the +same time." + (let* ((plist-cdr (semantic--tag-attributes-cdr tag))) + (when (consp plist-cdr) + (setcar plist-cdr + (semantic-tag-make-plist + (plist-put (car plist-cdr) attribute value)))) + tag)) + +(defun semantic-tag-put-attribute-no-side-effect (tag attribute value) + "Change value in TAG of ATTRIBUTE to VALUE without side effects. +All cons cells in the attribute list are replicated so that there +are no side effects if TAG is in shared lists. +If ATTRIBUTE already exists, its value is set to VALUE, otherwise the +new ATTRIBUTE VALUE pair is added. +Return TAG." + (let* ((plist-cdr (semantic--tag-attributes-cdr tag))) + (when (consp plist-cdr) + (setcar plist-cdr + (semantic-tag-make-plist + (plist-put (copy-sequence (car plist-cdr)) + attribute value)))) + tag)) + +(defsubst semantic-tag-get-attribute (tag attribute) + "From TAG, return the value of ATTRIBUTE. +ATTRIBUTE is a symbol whose specification value to get. +Return the value found, or nil if ATTRIBUTE is not one of the +attributes of TAG." + (plist-get (semantic-tag-attributes tag) attribute)) + +;; These functions are for internal use only! +(defsubst semantic--tag-properties-cdr (tag) + "Return the cons cell whose car is the PROPERTIES part of TAG. +That function is for internal use only." + (nthcdr 3 tag)) + +(defun semantic--tag-put-property (tag property value) + "Change value in TAG of PROPERTY to VALUE. +If PROPERTY already exists, its value is set to VALUE, otherwise the +new PROPERTY VALUE pair is added. +Return TAG. +That function is for internal use only." + (let* ((plist-cdr (semantic--tag-properties-cdr tag))) + (when (consp plist-cdr) + (setcar plist-cdr + (semantic-tag-make-plist + (plist-put (car plist-cdr) property value)))) + tag)) + +(defun semantic--tag-put-property-no-side-effect (tag property value) + "Change value in TAG of PROPERTY to VALUE without side effects. +All cons cells in the property list are replicated so that there +are no side effects if TAG is in shared lists. +If PROPERTY already exists, its value is set to VALUE, otherwise the +new PROPERTY VALUE pair is added. +Return TAG. +That function is for internal use only." + (let* ((plist-cdr (semantic--tag-properties-cdr tag))) + (when (consp plist-cdr) + (setcar plist-cdr + (semantic-tag-make-plist + (plist-put (copy-sequence (car plist-cdr)) + property value)))) + tag)) + +(defun semantic-tag-file-name (tag) + "Return the name of the file from which TAG originated. +Return nil if that information can't be obtained. +If TAG is from a loaded buffer, then that buffer's filename is used. +If TAG is unlinked, but has a :filename property, then that is used." + (let ((buffer (semantic-tag-in-buffer-p tag))) + (if buffer + (buffer-file-name buffer) + (semantic--tag-get-property tag :filename)))) + +;;; Tag tests and comparisons. +(defsubst semantic-tag-p (tag) + "Return non-nil if TAG is most likely a semantic tag." + (condition-case nil + (and (consp tag) + (stringp (car tag)) ; NAME + (symbolp (nth 1 tag)) (nth 1 tag) ; TAG-CLASS + (listp (nth 2 tag)) ; ATTRIBUTES + (listp (nth 3 tag)) ; PROPERTIES + ) + ;; If an error occurs, then it most certainly is not a tag. + (error nil))) + +(defsubst semantic-tag-of-class-p (tag class) + "Return non-nil if class of TAG is CLASS." + (eq (semantic-tag-class tag) class)) + +(defsubst semantic-tag-type-members (tag) + "Return the members of the type that TAG describes. +That is the value of the `:members' attribute." + (semantic-tag-get-attribute tag :members)) + +(defsubst semantic-tag-type (tag) + "Return the value of the `:type' attribute of TAG. +For a function it would be the data type of the return value. +For a variable, it is the storage type of that variable. +For a data type, the type is the style of datatype, such as +struct or union." + (semantic-tag-get-attribute tag :type)) + +(defun semantic-tag-with-position-p (tag) + "Return non-nil if TAG has positional information." + (and (semantic-tag-p tag) + (let ((o (semantic-tag-overlay tag))) + (or (and (semantic-overlay-p o) + (semantic-overlay-live-p o)) + (arrayp o))))) + +(defun semantic-equivalent-tag-p (tag1 tag2) + "Compare TAG1 and TAG2 and return non-nil if they are equivalent. +Use `equal' on elements the name, class, and position. +Use this function if tags are being copied and regrouped to test +for if two tags represent the same thing, but may be constructed +of different cons cells." + (and (equal (semantic-tag-name tag1) (semantic-tag-name tag2)) + (semantic-tag-of-class-p tag1 (semantic-tag-class tag2)) + (or (and (not (semantic-tag-overlay tag1)) + (not (semantic-tag-overlay tag2))) + (and (semantic-tag-overlay tag1) + (semantic-tag-overlay tag2) + (equal (semantic-tag-bounds tag1) + (semantic-tag-bounds tag2)))))) + +(defun semantic-tag-similar-p (tag1 tag2 &rest ignorable-attributes) + "Test to see if TAG1 and TAG2 are similar. +Two tags are similar if their name, datatype, and various attributes +are the same. + +Similar tags that have sub-tags such as arg lists or type members, +are similar w/out checking the sub-list of tags. +Optional argument IGNORABLE-ATTRIBUTES are attributes to ignore while comparing similarity." + (let* ((A1 (and (equal (semantic-tag-name tag1) (semantic-tag-name tag2)) + (semantic-tag-of-class-p tag1 (semantic-tag-class tag2)) + (semantic-tag-of-type-p tag1 (semantic-tag-type tag2)))) + (attr1 (semantic-tag-attributes tag1)) + (A2 (= (length attr1) (length (semantic-tag-attributes tag2)))) + (A3 t) + ) + (when (and (not A2) ignorable-attributes) + (setq A2 t)) + (while (and A2 attr1 A3) + (let ((a (car attr1)) + (v (car (cdr attr1)))) + + (cond ((or (eq a :type) ;; already tested above. + (memq a ignorable-attributes)) ;; Ignore them... + nil) + + ;; Don't test sublists of tags + ((and (listp v) (semantic-tag-p (car v))) + nil) + + ;; The attributes are not the same? + ((not (equal v (semantic-tag-get-attribute tag2 a))) + (setq A3 nil)) + (t + nil)) + ) + (setq attr1 (cdr (cdr attr1)))) + + (and A1 A2 A3) + )) + +(defun semantic-tag-similar-with-subtags-p (tag1 tag2 &rest ignorable-attributes) + "Test to see if TAG1 and TAG2 are similar. +Uses `semantic-tag-similar-p' but also recurses through sub-tags, such +as argument lists and type members. +Optional argument IGNORABLE-ATTRIBUTES is passed down to +`semantic-tag-similar-p'." + (let ((C1 (semantic-tag-components tag1)) + (C2 (semantic-tag-components tag2)) + ) + (if (or (/= (length C1) (length C2)) + (not (semantic-tag-similar-p tag1 tag2 ignorable-attributes)) + ) + ;; Basic test fails. + nil + ;; Else, check component lists. + (catch 'component-dissimilar + (while C1 + + (if (not (semantic-tag-similar-with-subtags-p + (car C1) (car C2) ignorable-attributes)) + (throw 'component-dissimilar nil)) + + (setq C1 (cdr C1)) + (setq C2 (cdr C2)) + ) + ;; If we made it this far, we are ok. + t) ))) + + +(defun semantic-tag-of-type-p (tag type) + "Compare TAG's type against TYPE. Non nil if equivalent. +TYPE can be a string, or a tag of class 'type. +This can be complex since some tags might have a :type that is a tag, +while other tags might just have a string. This function will also be +return true of TAG's type is compared directly to the declaration of a +data type." + (let* ((tagtype (semantic-tag-type tag)) + (tagtypestring (cond ((stringp tagtype) + tagtype) + ((and (semantic-tag-p tagtype) + (semantic-tag-of-class-p tagtype 'type)) + (semantic-tag-name tagtype)) + (t ""))) + (typestring (cond ((stringp type) + type) + ((and (semantic-tag-p type) + (semantic-tag-of-class-p type 'type)) + (semantic-tag-name type)) + (t ""))) + ) + (and + tagtypestring + (or + ;; Matching strings (input type is string) + (and (stringp type) + (string= tagtypestring type)) + ;; Matching strings (tag type is string) + (and (stringp tagtype) + (string= tagtype typestring)) + ;; Matching tokens, and the type of the type is the same. + (and (string= tagtypestring typestring) + (if (and (semantic-tag-type tagtype) (semantic-tag-type type)) + (equal (semantic-tag-type tagtype) (semantic-tag-type type)) + t)) + )) + )) + +(defun semantic-tag-type-compound-p (tag) + "Return non-nil the type of TAG is compound. +Compound implies a structure or similar data type. +Returns the list of tag members if it is compound." + (let* ((tagtype (semantic-tag-type tag)) + ) + (when (and (semantic-tag-p tagtype) + (semantic-tag-of-class-p tagtype 'type)) + ;; We have the potential of this being a nifty compound type. + (semantic-tag-type-members tagtype) + ))) + +(defun semantic-tag-faux-p (tag) + "Return non-nil if TAG is a FAUX tag. +FAUX tags are created to represent a construct that is +not known to exist in the code. + +Example: When the class browser sees methods to a class, but +cannot find the class, it will create a faux tag to represent the +class to store those methods." + (semantic--tag-get-property tag :faux-flag)) + +;;; Tag creation +;; + +;; Is this function still necessary? +(defun semantic-tag-make-plist (args) + "Create a property list with ARGS. +Args is a property list of the form (KEY1 VALUE1 ... KEYN VALUEN). +Where KEY is a symbol, and VALUE is the value for that symbol. +The return value will be a new property list, with these KEY/VALUE +pairs eliminated: + + - KEY associated to nil VALUE. + - KEY associated to an empty string VALUE. + - KEY associated to a zero VALUE." + (let (plist key val) + (while args + (setq key (car args) + val (nth 1 args) + args (nthcdr 2 args)) + (or (member val '("" nil)) + (and (numberp val) (zerop val)) + (setq plist (cons key (cons val plist))))) + ;; It is not useful to reverse the new plist. + plist)) + +(defsubst semantic-tag (name class &rest attributes) + "Create a generic semantic tag. +NAME is a string representing the name of this tag. +CLASS is the symbol that represents the class of tag this is, +such as 'variable, or 'function. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (list name class (semantic-tag-make-plist attributes) nil nil)) + +(defsubst semantic-tag-new-variable (name type &optional default-value &rest attributes) + "Create a semantic tag of class 'variable. +NAME is the name of this variable. +TYPE is a string or semantic tag representing the type of this variable. +Optional DEFAULT-VALUE is a string representing the default value of this variable. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (apply 'semantic-tag name 'variable + :type type + :default-value default-value + attributes)) + +(defsubst semantic-tag-new-function (name type arg-list &rest attributes) + "Create a semantic tag of class 'function. +NAME is the name of this function. +TYPE is a string or semantic tag representing the type of this function. +ARG-LIST is a list of strings or semantic tags representing the +arguments of this function. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (apply 'semantic-tag name 'function + :type type + :arguments arg-list + attributes)) + +(defsubst semantic-tag-new-type (name type members parents &rest attributes) + "Create a semantic tag of class 'type. +NAME is the name of this type. +TYPE is a string or semantic tag representing the type of this type. +MEMBERS is a list of strings or semantic tags representing the +elements that make up this type if it is a composite type. +PARENTS is a cons cell. (EXPLICIT-PARENTS . INTERFACE-PARENTS) +EXPLICIT-PARENTS can be a single string (Just one parent) or a +list of parents (in a multiple inheritance situation). It can also +be nil. +INTERFACE-PARENTS is a list of strings representing the names of +all INTERFACES, or abstract classes inherited from. It can also be +nil. +This slot can be interesting because the form: + ( nil \"string\") +is a valid parent where there is no explicit parent, and only an +interface. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (apply 'semantic-tag name 'type + :type type + :members members + :superclasses (car parents) + :interfaces (cdr parents) + attributes)) + +(defsubst semantic-tag-new-include (name system-flag &rest attributes) + "Create a semantic tag of class 'include. +NAME is the name of this include. +SYSTEM-FLAG represents that we were able to identify this include as belonging +to the system, as opposed to belonging to the local project. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (apply 'semantic-tag name 'include + :system-flag system-flag + attributes)) + +(defsubst semantic-tag-new-package (name detail &rest attributes) + "Create a semantic tag of class 'package. +NAME is the name of this package. +DETAIL is extra information about this package, such as a location where +it can be found. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (apply 'semantic-tag name 'package + :detail detail + attributes)) + +(defsubst semantic-tag-new-code (name detail &rest attributes) + "Create a semantic tag of class 'code. +NAME is a name for this code. +DETAIL is extra information about the code. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (apply 'semantic-tag name 'code + :detail detail + attributes)) + +(defsubst semantic-tag-set-faux (tag) + "Set TAG to be a new FAUX tag. +FAUX tags represent constructs not found in the source code. +You can identify a faux tag with `semantic-tag-faux-p'" + (semantic--tag-put-property tag :faux-flag t)) + +(defsubst semantic-tag-set-name (tag name) + "Set TAG name to NAME." + (setcar tag name)) + +;;; Copying and cloning tags. +;; +(defsubst semantic-tag-clone (tag &optional name) + "Clone TAG, creating a new TAG. +If optional argument NAME is not nil it specifies a new name for the +cloned tag." + ;; Right now, TAG is a list. + (list (or name (semantic-tag-name tag)) + (semantic-tag-class tag) + (copy-sequence (semantic-tag-attributes tag)) + (copy-sequence (semantic-tag-properties tag)) + (semantic-tag-overlay tag))) + +(defun semantic-tag-copy (tag &optional name keep-file) + "Return a copy of TAG unlinked from the originating buffer. +If optional argument NAME is non-nil it specifies a new name for the +copied tag. +If optional argument KEEP-FILE is non-nil, and TAG was linked to a +buffer, the originating buffer file name is kept in the `:filename' +property of the copied tag. +If KEEP-FILE is a string, and the orginating buffer is NOT available, +then KEEP-FILE is stored on the `:filename' property. +This runs the tag hook `unlink-copy-hook`." + ;; Right now, TAG is a list. + (let ((copy (semantic-tag-clone tag name))) + + ;; Keep the filename if needed. + (when keep-file + (semantic--tag-put-property + copy :filename (or (semantic-tag-file-name copy) + (and (stringp keep-file) + keep-file) + ))) + + (when (semantic-tag-with-position-p tag) + ;; Convert the overlay to a vector, effectively 'unlinking' the tag. + (semantic--tag-set-overlay + copy (vector (semantic-tag-start copy) (semantic-tag-end copy))) + + ;; Force the children to be copied also. + ;;(let ((chil (semantic--tag-copy-list + ;; (semantic-tag-components-with-overlays tag) + ;; keep-file))) + ;;;; Put the list into TAG. + ;;) + + ;; Call the unlink-copy hook. This should tell tools that + ;; this tag is not part of any buffer. + (when (semantic-overlay-p (semantic-tag-overlay tag)) + (semantic--tag-run-hooks copy 'unlink-copy-hook)) + ) + copy)) + +;;(defun semantic--tag-copy-list (tags &optional keep-file) +;; "Make copies of TAGS and return the list of TAGS." +;; (let ((out nil)) +;; (dolist (tag tags out) +;; (setq out (cons (semantic-tag-copy tag nil keep-file) +;; out)) +;; ))) + +(defun semantic--tag-copy-properties (tag1 tag2) + "Copy private properties from TAG1 to TAG2. +Return TAG2. +This function is for internal use only." + (let ((plist (semantic-tag-properties tag1))) + (while plist + (semantic--tag-put-property tag2 (car plist) (nth 1 plist)) + (setq plist (nthcdr 2 plist))) + tag2)) + +;;; DEEP COPIES +;; +(defun semantic-tag-deep-copy-one-tag (tag &optional filter) + "Make a deep copy of TAG, applying FILTER to each child-tag. +Properties and overlay info are not copied. +FILTER takes TAG as an argument, and should returns a semantic-tag. +It is safe for FILTER to modify the input tag and return it." + (when (not filter) (setq filter 'identity)) + (when (not (semantic-tag-p tag)) + (signal 'wrong-type-argument (list tag 'semantic-tag-p))) + (funcall filter (list (semantic-tag-name tag) + (semantic-tag-class tag) + (semantic--tag-deep-copy-attributes + (semantic-tag-attributes tag) filter) + nil + nil))) + +(defun semantic--tag-deep-copy-attributes (attrs &optional filter) + "Make a deep copy of ATTRS, applying FILTER to each child-tag. + +It is safe to modify ATTR, and return a permutaion of that list. + +FILTER takes TAG as an argument, and should returns a semantic-tag. +It is safe for FILTER to modify the input tag and return it." + (when (car attrs) + (when (not (symbolp (car attrs))) (error "Bad Attribute List in tag")) + (cons (car attrs) + (cons (semantic--tag-deep-copy-value (nth 1 attrs) filter) + (semantic--tag-deep-copy-attributes (nthcdr 2 attrs) filter))))) + +(defun semantic--tag-deep-copy-value (value &optional filter) + "Make a deep copy of VALUE, applying FILTER to each child-tag. + +It is safe to modify VALUE, and return a permutaion of that list. + +FILTER takes TAG as an argument, and should returns a semantic-tag. +It is safe for FILTER to modify the input tag and return it." + (cond + ;; Another tag. + ((semantic-tag-p value) + (semantic-tag-deep-copy-one-tag value filter)) + + ;; A list of more tags + ((and (listp value) (semantic-tag-p (car value))) + (semantic--tag-deep-copy-tag-list value filter)) + + ;; Some arbitrary data. + (t value))) + +(defun semantic--tag-deep-copy-tag-list (tags &optional filter) + "Make a deep copy of TAGS, applying FILTER to each child-tag. + +It is safe to modify the TAGS list, and return a permutaion of that list. + +FILTER takes TAG as an argument, and should returns a semantic-tag. +It is safe for FILTER to modify the input tag and return it." + (when (car tags) + (if (semantic-tag-p (car tags)) + (cons (semantic-tag-deep-copy-one-tag (car tags) filter) + (semantic--tag-deep-copy-tag-list (cdr tags) filter)) + (cons (car tags) (semantic--tag-deep-copy-tag-list (cdr tags) filter))))) + + +;;; Standard Tag Access +;; + +;;; Common +;; +(defsubst semantic-tag-modifiers (tag) + "Return the value of the `:typemodifiers' attribute of TAG." + (semantic-tag-get-attribute tag :typemodifiers)) + +(defun semantic-tag-docstring (tag &optional buffer) + "Return the documentation of TAG. +That is the value defined by the `:documentation' attribute. +Optional argument BUFFER indicates where to get the text from. +If not provided, then only the POSITION can be provided. + +If you want to get documentation for languages that do not store +the documentation string in the tag itself, use +`semantic-documentation-for-tag' instead." + (let ((p (semantic-tag-get-attribute tag :documentation))) + (cond + ((stringp p) p) ;; it is the doc string. + + ((semantic-lex-token-with-text-p p) + (semantic-lex-token-text p)) + + ((and (semantic-lex-token-without-text-p p) + buffer) + (with-current-buffer buffer + (semantic-lex-token-text (car (semantic-lex p (1+ p)))))) + + (t nil)))) + +;;; Generic attributes for tags of any class. +;; +(defsubst semantic-tag-named-parent (tag) + "Return the parent of TAG. +That is the value of the `:parent' attribute. +If a definition can occur outside an actual parent structure, but +refers to that parent by name, then the :parent attribute should be used." + (semantic-tag-get-attribute tag :parent)) + +;;; Tags of class `type' + +(defun semantic-tag-type-superclasses (tag) + "Return the list of superclass names of the type that TAG describes." + (let ((supers (semantic-tag-get-attribute tag :superclasses))) + (cond ((stringp supers) + ;; If we have a string, make it a list. + (list supers)) + ((semantic-tag-p supers) + ;; If we have one tag, return just the name. + (list (semantic-tag-name supers))) + ((and (consp supers) (semantic-tag-p (car supers))) + ;; If we have a tag list, then return the names. + (mapcar (lambda (s) (semantic-tag-name s)) + supers)) + ((consp supers) + ;; A list of something, return it. + supers)))) + +(defun semantic--tag-find-parent-by-name (name supers) + "Find the superclass NAME in the list of SUPERS. +If a simple search doesn't do it, try splitting up the names +in SUPERS." + (let ((stag nil)) + (setq stag (semantic-find-first-tag-by-name name supers)) + + (when (not stag) + (require 'semantic/analyze/fcn) + (dolist (S supers) + (let* ((sname (semantic-tag-name S)) + (splitparts (semantic-analyze-split-name sname)) + (parts (if (stringp splitparts) + (list splitparts) + (nreverse splitparts)))) + (when (string= name (car parts)) + (setq stag S)) + ))) + + stag)) + +(defun semantic-tag-type-superclass-protection (tag parentstring) + "Return the inheritance protection in TAG from PARENTSTRING. +PARENTSTRING is the name of the parent being inherited. +The return protection is a symbol, 'public, 'protection, and 'private." + (let ((supers (semantic-tag-get-attribute tag :superclasses))) + (cond ((stringp supers) + 'public) + ((semantic-tag-p supers) + (let ((prot (semantic-tag-get-attribute supers :protection))) + (or (cdr (assoc prot '(("public" . public) + ("protected" . protected) + ("private" . private)))) + 'public))) + ((and (consp supers) (stringp (car supers))) + 'public) + ((and (consp supers) (semantic-tag-p (car supers))) + (let* ((stag (semantic--tag-find-parent-by-name parentstring supers)) + (prot (when stag + (semantic-tag-get-attribute stag :protection)))) + (or (cdr (assoc prot '(("public" . public) + ("protected" . protected) + ("private" . private)))) + (when (equal prot "unspecified") + (if (semantic-tag-of-type-p tag "class") + 'private + 'public)) + 'public)))) + )) + +(defsubst semantic-tag-type-interfaces (tag) + "Return the list of interfaces of the type that TAG describes." + ;; @todo - make this as robust as the above. + (semantic-tag-get-attribute tag :interfaces)) + +;;; Tags of class `function' +;; +(defsubst semantic-tag-function-arguments (tag) + "Return the arguments of the function that TAG describes. +That is the value of the `:arguments' attribute." + (semantic-tag-get-attribute tag :arguments)) + +(defsubst semantic-tag-function-throws (tag) + "Return the exceptions the function that TAG describes can throw. +That is the value of the `:throws' attribute." + (semantic-tag-get-attribute tag :throws)) + +(defsubst semantic-tag-function-parent (tag) + "Return the parent of the function that TAG describes. +That is the value of the `:parent' attribute. +A function has a parent if it is a method of a class, and if the +function does not appear in body of it's parent class." + (semantic-tag-named-parent tag)) + +(defsubst semantic-tag-function-destructor-p (tag) + "Return non-nil if TAG describes a destructor function. +That is the value of the `:destructor-flag' attribute." + (semantic-tag-get-attribute tag :destructor-flag)) + +(defsubst semantic-tag-function-constructor-p (tag) + "Return non-nil if TAG describes a constructor function. +That is the value of the `:constructor-flag' attribute." + (semantic-tag-get-attribute tag :constructor-flag)) + +;;; Tags of class `variable' +;; +(defsubst semantic-tag-variable-default (tag) + "Return the default value of the variable that TAG describes. +That is the value of the attribute `:default-value'." + (semantic-tag-get-attribute tag :default-value)) + +(defsubst semantic-tag-variable-constant-p (tag) + "Return non-nil if the variable that TAG describes is a constant. +That is the value of the attribute `:constant-flag'." + (semantic-tag-get-attribute tag :constant-flag)) + +;;; Tags of class `include' +;; +(defsubst semantic-tag-include-system-p (tag) + "Return non-nil if the include that TAG describes is a system include. +That is the value of the attribute `:system-flag'." + (semantic-tag-get-attribute tag :system-flag)) + +(define-overloadable-function semantic-tag-include-filename (tag) + "Return a filename representation of TAG. +The default action is to return the `semantic-tag-name'. +Some languages do not use full filenames in their include statements. +Override this method to translate the code represenation +into a filename. (A relative filename if necessary.) + +See `semantic-dependency-tag-file' to expand an include +tag to a full file name.") + +(defun semantic-tag-include-filename-default (tag) + "Return a filename representation of TAG. +Returns `semantic-tag-name'." + (semantic-tag-name tag)) + +;;; Tags of class `code' +;; +(defsubst semantic-tag-code-detail (tag) + "Return detail information from code that TAG describes. +That is the value of the attribute `:detail'." + (semantic-tag-get-attribute tag :detail)) + +;;; Tags of class `alias' +;; +(defsubst semantic-tag-new-alias (name meta-tag-class value &rest attributes) + "Create a semantic tag of class alias. +NAME is a name for this alias. +META-TAG-CLASS is the class of the tag this tag is an alias. +VALUE is the aliased definition. +ATTRIBUTES is a list of additional attributes belonging to this tag." + (apply 'semantic-tag name 'alias + :aliasclass meta-tag-class + :definition value + attributes)) + +(defsubst semantic-tag-alias-class (tag) + "Return the class of tag TAG is an alias." + (semantic-tag-get-attribute tag :aliasclass)) + +(define-overloadable-function semantic-tag-alias-definition (tag) + "Return the definition TAG is an alias. +The returned value is a tag of the class that +`semantic-tag-alias-class' returns for TAG. +The default is to return the value of the :definition attribute. +Return nil if TAG is not of class 'alias." + (when (semantic-tag-of-class-p tag 'alias) + (:override + (semantic-tag-get-attribute tag :definition)))) + +;;; Language Specific Tag access via overload +;; +;;;###autoload +(define-overloadable-function semantic-tag-components (tag) + "Return a list of components for TAG. +A Component is a part of TAG which itself may be a TAG. +Examples include the elements of a structure in a +tag of class `type, or the list of arguments to a +tag of class 'function." + ) + +(defun semantic-tag-components-default (tag) + "Return a list of components for TAG. +Perform the described task in `semantic-tag-components'." + (cond ((semantic-tag-of-class-p tag 'type) + (semantic-tag-type-members tag)) + ((semantic-tag-of-class-p tag 'function) + (semantic-tag-function-arguments tag)) + (t nil))) + +(define-overloadable-function semantic-tag-components-with-overlays (tag) + "Return the list of top level components belonging to TAG. +Children are any sub-tags which contain overlays. + +Default behavior is to get `semantic-tag-components' in addition +to the components of an anonymous types (if applicable.) + +Note for language authors: + If a mode defines a language tag that has tags in it with overlays +you should still return them with this function. +Ignoring this step will prevent several features from working correctly." + ) + +(defun semantic-tag-components-with-overlays-default (tag) + "Return the list of top level components belonging to TAG. +Children are any sub-tags which contain overlays. +The default action collects regular components of TAG, in addition +to any components beloning to an anonymous type." + (let ((explicit-children (semantic-tag-components tag)) + (type (semantic-tag-type tag)) + (anon-type-children nil) + (all-children nil)) + ;; Identify if this tag has an anonymous structure as + ;; its type. This implies it may have children with overlays. + (when (and type (semantic-tag-p type)) + (setq anon-type-children (semantic-tag-components type)) + ;; Add anonymous children + (while anon-type-children + (when (semantic-tag-with-position-p (car anon-type-children)) + (setq all-children (cons (car anon-type-children) all-children))) + (setq anon-type-children (cdr anon-type-children)))) + ;; Add explicit children + (while explicit-children + (when (semantic-tag-with-position-p (car explicit-children)) + (setq all-children (cons (car explicit-children) all-children))) + (setq explicit-children (cdr explicit-children))) + ;; Return + (nreverse all-children))) + +(defun semantic-tag-children-compatibility (tag &optional positiononly) + "Return children of TAG. +If POSITIONONLY is nil, use `semantic-tag-components'. +If POSITIONONLY is non-nil, use `semantic-tag-components-with-overlays'. +DO NOT use this fcn in new code. Use one of the above instead." + (if positiononly + (semantic-tag-components-with-overlays tag) + (semantic-tag-components tag))) + +;;; Tag Region +;; +;; A Tag represents a region in a buffer. You can narrow to that tag. +;; +(defun semantic-narrow-to-tag (&optional tag) + "Narrow to the region specified by the bounds of TAG. +See `semantic-tag-bounds'." + (interactive) + (if (not tag) (setq tag (semantic-current-tag))) + (narrow-to-region (semantic-tag-start tag) + (semantic-tag-end tag))) + +(defmacro semantic-with-buffer-narrowed-to-current-tag (&rest body) + "Execute BODY with the buffer narrowed to the current tag." + `(save-restriction + (semantic-narrow-to-tag (semantic-current-tag)) + ,@body)) +(put 'semantic-with-buffer-narrowed-to-current-tag 'lisp-indent-function 0) +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec semantic-with-buffer-narrowed-to-current-tag + (def-body)))) + +(defmacro semantic-with-buffer-narrowed-to-tag (tag &rest body) + "Narrow to TAG, and execute BODY." + `(save-restriction + (semantic-narrow-to-tag ,tag) + ,@body)) +(put 'semantic-with-buffer-narrowed-to-tag 'lisp-indent-function 1) +(add-hook 'edebug-setup-hook + (lambda () + (def-edebug-spec semantic-with-buffer-narrowed-to-tag + (def-body)))) + +;;; Tag Hooks +;; +;; Semantic may want to provide special hooks when specific operations +;; are about to happen on a given tag. These routines allow for hook +;; maintenance on a tag. + +;; Internal global variable used to manage tag hooks. For example, +;; some implementation of `remove-hook' checks that the hook variable +;; is `default-boundp'. +(defvar semantic--tag-hook-value) + +(defun semantic-tag-add-hook (tag hook function &optional append) + "Onto TAG, add to the value of HOOK the function FUNCTION. +FUNCTION is added (if necessary) at the beginning of the hook list +unless the optional argument APPEND is non-nil, in which case +FUNCTION is added at the end. +HOOK should be a symbol, and FUNCTION may be any valid function. +See also the function `add-hook'." + (let ((semantic--tag-hook-value (semantic--tag-get-property tag hook))) + (add-hook 'semantic--tag-hook-value function append) + (semantic--tag-put-property tag hook semantic--tag-hook-value) + semantic--tag-hook-value)) + +(defun semantic-tag-remove-hook (tag hook function) + "Onto TAG, remove from the value of HOOK the function FUNCTION. +HOOK should be a symbol, and FUNCTION may be any valid function. If +FUNCTION isn't the value of HOOK, or, if FUNCTION doesn't appear in +the list of hooks to run in HOOK, then nothing is done. +See also the function `remove-hook'." + (let ((semantic--tag-hook-value (semantic--tag-get-property tag hook))) + (remove-hook 'semantic--tag-hook-value function) + (semantic--tag-put-property tag hook semantic--tag-hook-value) + semantic--tag-hook-value)) + +(defun semantic--tag-run-hooks (tag hook &rest args) + "Run for TAG all expressions saved on the property HOOK. +Each hook expression must take at least one argument, the TAG. +For any given situation, additional ARGS may be passed." + (let ((semantic--tag-hook-value (semantic--tag-get-property tag hook)) + (arglist (cons tag args))) + (condition-case err + ;; If a hook bombs, ignore it! Usually this is tied into + ;; some sort of critical system. + (apply 'run-hook-with-args 'semantic--tag-hook-value arglist) + (error (message "Error: %S" err))))) + +;;; Tags and Overlays +;; +;; Overlays are used so that we can quickly identify tags from +;; buffer positions and regions using built in Emacs commands. +;; +(defsubst semantic--tag-unlink-list-from-buffer (tags) + "Convert TAGS from using an overlay to using an overlay proxy. +This function is for internal use only." + (mapcar 'semantic--tag-unlink-from-buffer tags)) + +(defun semantic--tag-unlink-from-buffer (tag) + "Convert TAG from using an overlay to using an overlay proxy. +This function is for internal use only." + (when (semantic-tag-p tag) + (let ((o (semantic-tag-overlay tag))) + (when (semantic-overlay-p o) + (semantic--tag-set-overlay + tag (vector (semantic-overlay-start o) + (semantic-overlay-end o))) + (semantic-overlay-delete o)) + ;; Look for a link hook on TAG. + (semantic--tag-run-hooks tag 'unlink-hook) + ;; Fix the sub-tags which contain overlays. + (semantic--tag-unlink-list-from-buffer + (semantic-tag-components-with-overlays tag))))) + +(defsubst semantic--tag-link-list-to-buffer (tags) + "Convert TAGS from using an overlay proxy to using an overlay. +This function is for internal use only." + (mapcar 'semantic--tag-link-to-buffer tags)) + +(defun semantic--tag-link-to-buffer (tag) + "Convert TAG from using an overlay proxy to using an overlay. +This function is for internal use only." + (when (semantic-tag-p tag) + (let ((o (semantic-tag-overlay tag))) + (when (and (vectorp o) (= (length o) 2)) + (setq o (semantic-make-overlay (aref o 0) (aref o 1) + (current-buffer))) + (semantic--tag-set-overlay tag o) + (semantic-overlay-put o 'semantic tag) + ;; Clear the :filename property + (semantic--tag-put-property tag :filename nil)) + ;; Look for a link hook on TAG. + (semantic--tag-run-hooks tag 'link-hook) + ;; Fix the sub-tags which contain overlays. + (semantic--tag-link-list-to-buffer + (semantic-tag-components-with-overlays tag))))) + +(defun semantic--tag-unlink-cache-from-buffer () + "Convert all tags in the current cache to use overlay proxys. +This function is for internal use only." + (require 'semantic) + (semantic--tag-unlink-list-from-buffer + ;; @todo- use fetch-tags-fast? + (semantic-fetch-tags))) + +(defvar semantic--buffer-cache) + +(defun semantic--tag-link-cache-to-buffer () + "Convert all tags in the current cache to use overlays. +This function is for internal use only." + (require 'semantic) + (condition-case nil + ;; In this unique case, we cannot call the usual toplevel fn. + ;; because we don't want a reparse, we want the old overlays. + (semantic--tag-link-list-to-buffer + semantic--buffer-cache) + ;; Recover when there is an error restoring the cache. + (error (message "Error recovering tag list") + (semantic-clear-toplevel-cache) + nil))) + +;;; Tag Cooking +;; +;; Raw tags from a parser follow a different positional format than +;; those used in the buffer cache. Raw tags need to be cooked into +;; semantic cache friendly tags for use by the masses. +;; +(defsubst semantic--tag-expanded-p (tag) + "Return non-nil if TAG is expanded. +This function is for internal use only. +See also the function `semantic--expand-tag'." + ;; In fact a cooked tag is actually a list of cooked tags + ;; because a raw tag can be expanded in several cooked ones! + (when (consp tag) + (while (and (semantic-tag-p (car tag)) + (vectorp (semantic-tag-overlay (car tag)))) + (setq tag (cdr tag))) + (null tag))) + +(defvar semantic-tag-expand-function nil + "Function used to expand a tag. +It is passed each tag production, and must return a list of tags +derived from it, or nil if it does not need to be expanded. + +Languages with compound definitions should use this function to expand +from one compound symbol into several. For example, in C or Java the +following definition is easily parsed into one tag: + + int a, b; + +This function should take this compound tag and turn it into two tags, +one for A, and the other for B.") +(make-variable-buffer-local 'semantic-tag-expand-function) + +(defun semantic--tag-expand (tag) + "Convert TAG from a raw state to a cooked state, and expand it. +Returns a list of cooked tags. + + The parser returns raw tags with positional data START END at the +end of the tag data structure (a list for now). We convert it from +that to a cooked state that uses an overlay proxy, that is, a vector +\[START END]. + + The raw tag is changed with side effects and maybe expanded in +several derived tags when the variable `semantic-tag-expand-function' +is set. + +This function is for internal use only." + (if (semantic--tag-expanded-p tag) + ;; Just return TAG if it is already expanded (by a grammar + ;; semantic action), or if it isn't recognized as a valid + ;; semantic tag. + tag + + ;; Try to cook the tag. This code will be removed when tag will + ;; be directly created with the right format. + (condition-case nil + (let ((ocdr (semantic--tag-overlay-cdr tag))) + ;; OCDR contains the sub-list of TAG whose car is the + ;; OVERLAY part of TAG. That is, a list (OVERLAY START END). + ;; Convert it into an overlay proxy ([START END]). + (semantic--tag-set-overlay + tag (vector (nth 1 ocdr) (nth 2 ocdr))) + ;; Remove START END positions at end of tag. + (setcdr ocdr nil) + ;; At this point (length TAG) must be 5! + ;;(unless (= (length tag) 5) + ;; (error "Tag expansion failed")) + ) + (error + (message "A Rule must return a single tag-line list!") + (debug tag) + nil)) + ;; Expand based on local configuration + (if semantic-tag-expand-function + (or (funcall semantic-tag-expand-function tag) + (list tag)) + (list tag)))) + +;; Foreign tags +;; +(defmacro semantic-foreign-tag-invalid (tag) + "Signal that TAG is an invalid foreign tag." + `(signal 'wrong-type-argument '(semantic-foreign-tag-p ,tag))) + +(defsubst semantic-foreign-tag-p (tag) + "Return non-nil if TAG is a foreign tag. +That is, a tag unlinked from the originating buffer, which carries the +originating buffer file name, and major mode." + (and (semantic-tag-p tag) + (semantic--tag-get-property tag :foreign-flag))) + +(defsubst semantic-foreign-tag-check (tag) + "Check that TAG is a valid foreign tag. +Signal an error if not." + (or (semantic-foreign-tag-p tag) + (semantic-foreign-tag-invalid tag))) + +(defun semantic-foreign-tag (&optional tag) + "Return a copy of TAG as a foreign tag, or nil if it can't be done. +TAG defaults to the tag at point in current buffer. +See also `semantic-foreign-tag-p'." + (or tag (setq tag (semantic-current-tag))) + (when (semantic-tag-p tag) + (let ((ftag (semantic-tag-copy tag nil t)) + ;; Do extra work for the doc strings, since this is a + ;; common use case. + (doc (condition-case nil + (semantic-documentation-for-tag tag) + (error nil)))) + ;; A foreign tag must carry its originating buffer file name! + (when (semantic--tag-get-property ftag :filename) + (semantic--tag-put-property ftag :mode (semantic-tag-mode tag)) + (semantic--tag-put-property ftag :documentation doc) + (semantic--tag-put-property ftag :foreign-flag t) + ftag)))) + +;; High level obtain/insert foreign tag overloads +(define-overloadable-function semantic-obtain-foreign-tag (&optional tag) + "Obtain a foreign tag from TAG. +TAG defaults to the tag at point in current buffer. +Return the obtained foreign tag or nil if failed." + (semantic-foreign-tag tag)) + +(defun semantic-insert-foreign-tag-default (foreign-tag) + "Insert FOREIGN-TAG into the current buffer. +The default behavior assumes the current buffer is a language file, +and attempts to insert a prototype/function call." + ;; Long term goal: Have a mechanism for a tempo-like template insert + ;; for the given tag. + (insert (semantic-format-tag-prototype foreign-tag))) + +(define-overloadable-function semantic-insert-foreign-tag (foreign-tag) + "Insert FOREIGN-TAG into the current buffer. +Signal an error if FOREIGN-TAG is not a valid foreign tag. +This function is overridable with the symbol `insert-foreign-tag'." + (semantic-foreign-tag-check foreign-tag) + (:override) + (message (semantic-format-tag-summarize foreign-tag))) + +;;; Support log modes here +(define-mode-local-override semantic-insert-foreign-tag + log-edit-mode (foreign-tag) + "Insert foreign tags into log-edit mode." + (insert (concat "(" (semantic-format-tag-name foreign-tag) "): "))) + +(define-mode-local-override semantic-insert-foreign-tag + change-log-mode (foreign-tag) + "Insert foreign tags into log-edit mode." + (insert (concat "(" (semantic-format-tag-name foreign-tag) "): "))) + +;;; Compatibility +;; +(defconst semantic-token-version + semantic-tag-version) +(defconst semantic-token-incompatible-version + semantic-tag-incompatible-version) + +(defsubst semantic-token-type-parent (tag) + "Return the parent of the type that TAG describes. +The return value is a list. A value of nil means no parents. +The `car' of the list is either the parent class, or a list +of parent classes. The `cdr' of the list is the list of +interfaces, or abstract classes which are parents of TAG." + (cons (semantic-tag-get-attribute tag :superclasses) + (semantic-tag-type-interfaces tag))) +(make-obsolete 'semantic-token-type-parent + "\ +use `semantic-tag-type-superclass' \ +and `semantic-tag-type-interfaces' instead") + +(semantic-alias-obsolete 'semantic-tag-make-assoc-list + 'semantic-tag-make-plist) + +(semantic-varalias-obsolete 'semantic-expand-nonterminal + 'semantic-tag-expand-function) + +(provide 'semantic/tag) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/tag" +;; End: + +;;; semantic/tag.el ends here diff --git a/lisp/cedet/semantic/texi.el b/lisp/cedet/semantic/texi.el new file mode 100644 index 00000000000..5f00b1ab928 --- /dev/null +++ b/lisp/cedet/semantic/texi.el @@ -0,0 +1,682 @@ +;;; semantic/texi.el --- Semantic details for Texinfo files + +;;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Author: Eric M. Ludlam <zappo@gnu.org> + +;; 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: +;; +;; Parse Texinfo buffers using regular expressions. The core parser +;; engine is the function `semantic-texi-parse-headings'. The +;; parser plug-in is the function `semantic-texi-parse-region' that +;; overrides `semantic-parse-region'. + +(require 'semantic) +(require 'semantic/format) +(require 'texinfo) + +(eval-when-compile + (require 'semantic/db) + (require 'semantic/db-find) + (require 'semantic/ctxt) + (require 'semantic/find) + (require 'semantic/doc)) + +(defvar ede-minor-mode) +(declare-function lookup-words "ispell") +(declare-function ede-current-project "ede") + +(defvar semantic-texi-super-regex + "^@\\(top\\|chapter\\|\\(sub\\)*section\\|unnumbered\\(\\(sub\\)*sec\\)?\\|\ +\\(chap\\|\\(sub\\)+\\|major\\)?heading\\|appendix\\(\\(sub\\)*sec\\)?\\|\ +centerchap\\|def\\(var\\|un\\|fn\\|opt\\)x?\\)" + "Regular expression used to find special sections in a Texinfo file.") + +(defvar semantic-texi-name-field-list + '( ("defvar" . 1) + ("defvarx" . 1) + ("defun" . 1) + ("defunx" . 1) + ("defopt" . 1) + ("deffn" . 2) + ("deffnx" . 2) + ) + "List of definition commands, and the field position. +The field position is the field number (based at 1) where the +name of this section is.") + +;;; Code: +(defun semantic-texi-parse-region (&rest ignore) + "Parse the current texinfo buffer for semantic tags. +IGNORE any arguments, always parse the whole buffer. +Each tag returned is of the form: + (\"NAME\" section (:members CHILDREN)) +or + (\"NAME\" def) + +It is an override of 'parse-region and must be installed by the +function `semantic-install-function-overrides'." + (mapcar 'semantic-texi-expand-tag + (semantic-texi-parse-headings))) + +(defun semantic-texi-parse-changes () + "Parse changes in the current texinfo buffer." + ;; NOTE: For now, just schedule a full reparse. + ;; To be implemented later. + (semantic-parse-tree-set-needs-rebuild)) + +(defun semantic-texi-expand-tag (tag) + "Expand the texinfo tag TAG." + (let ((chil (semantic-tag-components tag))) + (if chil + (semantic-tag-put-attribute + tag :members (mapcar 'semantic-texi-expand-tag chil))) + (car (semantic--tag-expand tag)))) + +(defun semantic-texi-parse-headings () + "Parse the current texinfo buffer for all semantic tags now." + (let ((pass1 nil)) + ;; First search and snarf. + (save-excursion + (goto-char (point-min)) + (let ((semantic--progress-reporter + (make-progress-reporter + (format "Parsing %s..." + (file-name-nondirectory buffer-file-name)) + (point-min) (point-max)))) + (while (re-search-forward semantic-texi-super-regex nil t) + (setq pass1 (cons (match-beginning 0) pass1)) + (progress-reporter-update semantic--progress-reporter (point))) + (progress-reporter-done semantic--progress-reporter))) + (setq pass1 (nreverse pass1)) + ;; Now, make some tags while creating a set of children. + (car (semantic-texi-recursive-combobulate-list pass1 0)) + )) + +(defsubst semantic-texi-new-section-tag (name members start end) + "Create a semantic tag of class section. +NAME is the name of this section. +MEMBERS is a list of semantic tags representing the elements that make +up this section. +START and END define the location of data described by the tag." + (append (semantic-tag name 'section :members members) + (list start end))) + +(defsubst semantic-texi-new-def-tag (name start end) + "Create a semantic tag of class def. +NAME is the name of this definition. +START and END define the location of data described by the tag." + (append (semantic-tag name 'def) + (list start end))) + +(defun semantic-texi-set-endpoint (metataglist pnt) + "Set the end point of the first section tag in METATAGLIST to PNT. +METATAGLIST is a list of tags in the intermediate tag format used by the +texinfo parser. PNT is the new point to set." + (let ((metatag nil)) + (while (and metataglist + (not (eq (semantic-tag-class (car metataglist)) 'section))) + (setq metataglist (cdr metataglist))) + (setq metatag (car metataglist)) + (when metatag + (setcar (nthcdr (1- (length metatag)) metatag) pnt) + metatag))) + +(defun semantic-texi-recursive-combobulate-list (sectionlist level) + "Rearrange SECTIONLIST to be a hierarchical tag list starting at LEVEL. +Return the rearranged new list, with all remaining tags from +SECTIONLIST starting at ELT 2. Sections not are not dealt with as soon as a +tag with greater section value than LEVEL is found." + (let ((newl nil) + (oldl sectionlist) + tag + ) + (save-excursion + (catch 'level-jump + (while oldl + (goto-char (car oldl)) + (if (looking-at "@\\(\\w+\\)") + (let* ((word (match-string 1)) + (levelmatch (assoc word texinfo-section-list)) + text begin tmp + ) + ;; Set begin to the right location + (setq begin (point)) + ;; Get out of here if there if we made it that far. + (if (and levelmatch (<= (car (cdr levelmatch)) level)) + (progn + (when newl + (semantic-texi-set-endpoint newl begin)) + (throw 'level-jump t))) + ;; Recombobulate + (if levelmatch + (let ((end (match-end 1))) + ;; Levels sometimes have a @node just in front. + ;; That node statement should be included in the space + ;; for this entry. + (save-excursion + (skip-chars-backward "\n \t") + (beginning-of-line) + (when (looking-at "@node\\>") + (setq begin (point)))) + ;; When there is a match, the descriptive text + ;; consists of the rest of the line. + (goto-char end) + (skip-chars-forward " \t") + (setq text (buffer-substring-no-properties + (point) + (progn (end-of-line) (point)))) + ;; Next, recurse into the body to find the end. + (setq tmp (semantic-texi-recursive-combobulate-list + (cdr oldl) (car (cdr levelmatch)))) + ;; Build a tag + (setq tag (semantic-texi-new-section-tag + text (car tmp) begin (point))) + ;; Before appending the newtag, update the previous tag + ;; if it is a section tag. + (when newl + (semantic-texi-set-endpoint newl begin)) + ;; Append new tag to our master list. + (setq newl (cons tag newl)) + ;; continue + (setq oldl (cdr tmp)) + ) + ;; No match means we have a def*, so get the name from + ;; it based on the type of thingy we found. + (setq levelmatch (assoc word semantic-texi-name-field-list) + tmp (or (cdr levelmatch) 1)) + (forward-sexp tmp) + (skip-chars-forward " \t") + (setq text (buffer-substring-no-properties + (point) + (progn (forward-sexp 1) (point)))) + ;; Seek the end of this definition + (goto-char begin) + (semantic-texi-forward-deffn) + (setq tag (semantic-texi-new-def-tag text begin (point)) + newl (cons tag newl)) + ;; continue + (setq oldl (cdr oldl))) + ) + (error "Problem finding section in semantic/texi parser")) + ;; (setq oldl (cdr oldl)) + ) + ;; When oldl runs out, force a new endpoint as point-max + (when (not oldl) + (semantic-texi-set-endpoint newl (point-max))) + )) + (cons (nreverse newl) oldl))) + +(defun semantic-texi-forward-deffn () + "Move forward over one deffn type definition. +The cursor should be on the @ sign." + (when (looking-at "@\\(\\w+\\)") + (let* ((type (match-string 1)) + (seek (concat "^@end\\s-+" (regexp-quote type)))) + (re-search-forward seek nil t)))) + +(define-mode-local-override semantic-tag-components + texinfo-mode (tag) + "Return components belonging to TAG." + (semantic-tag-get-attribute tag :members)) + + +;;; Overrides: Context Parsing +;; +;; How to treat texi as a language? +;; +(defvar semantic-texi-environment-regexp + (if (string-match texinfo-environment-regexp "@menu") + ;; Make sure our Emacs has menus in it. + texinfo-environment-regexp + ;; If no menus, then merge in the menu concept. + (when (string-match "cartouche" texinfo-environment-regexp) + (concat (substring texinfo-environment-regexp + 0 (match-beginning 0)) + "menu\\|" + (substring texinfo-environment-regexp + (match-beginning 0))))) + "Regular expression for matching texinfo enviroments. +uses `texinfo-environment-regexp', but makes sure that it +can handle the @menu environment.") + +(define-mode-local-override semantic-up-context texinfo-mode () + "Handle texinfo constructs which do not use parenthetical nesting." + (let ((done nil)) + (save-excursion + (let ((parenthetical (semantic-up-context-default)) + ) + (when (not parenthetical) + ;; We are in parenthises. Are they the types of parens + ;; belonging to a texinfo construct? + (forward-word -1) + (when (looking-at "@\\w+{") + (setq done (point)))))) + ;; If we are not in a parenthetical node, then find a block instead. + ;; Use the texinfo support to find block start/end constructs. + (save-excursion + (while (and (not done) + (re-search-backward semantic-texi-environment-regexp nil t)) + ;; For any hit, if we find an @end foo, then jump to the + ;; matching @foo. If it is not an end, then we win! + (if (not (looking-at "@end\\s-+\\(\\w+\\)")) + (setq done (point)) + ;; Skip over this block + (let ((env (match-string 1))) + (re-search-backward (concat "@" env)))) + )) + ;; All over, post what we find. + (if done + ;; We found something, so use it. + (progn (goto-char done) + nil) + t))) + +(define-mode-local-override semantic-beginning-of-context texinfo-mode (&optional point) + "Move to the beginning of the context surrounding POINT." + (if (semantic-up-context point) + ;; If we can't go up, we can't do this either. + t + ;; We moved, so now we need to skip into whatever this thing is. + (forward-word 1) ;; skip the command + (if (looking-at "\\s-*{") + ;; In a short command. Go in. + (down-list 1) + ;; An environment. Go to the next line. + (end-of-line) + (forward-char 1)) + nil)) + +(define-mode-local-override semantic-ctxt-current-class-list + texinfo-mode (&optional point) + "Determine the class of tags that can be used at POINT. +For texinfo, there two possibilities returned. +1) 'function - for a call to a texinfo function +2) 'word - indicates an english word. +It would be nice to know function arguments too, but not today." + (let ((sym (semantic-ctxt-current-symbol))) + (if (and sym (= (aref (car sym) 0) ?@)) + '(function) + '(word)))) + + +;;; Overrides : Formatting +;; +;; Various override to better format texi tags. +;; + +(define-mode-local-override semantic-format-tag-abbreviate + texinfo-mode (tag &optional parent color) + "Texinfo tags abbreviation." + (let ((class (semantic-tag-class tag)) + (name (semantic-format-tag-name tag parent color)) + ) + (cond ((eq class 'function) + (concat name "{ }")) + (t (semantic-format-tag-abbreviate-default tag parent color))) + )) + +(define-mode-local-override semantic-format-tag-prototype + texinfo-mode (tag &optional parent color) + "Texinfo tags abbreviation." + (semantic-format-tag-abbreviate tag parent color)) + + +;;; Texi Unique Features +;; +(defun semantic-tag-texi-section-text-bounds (tag) + "Get the bounds to the text of TAG. +The text bounds is the text belonging to this node excluding +the text of any child nodes, but including any defuns." + (let ((memb (semantic-tag-components tag))) + ;; Members.. if one is a section, check it out. + (while (and memb (not (semantic-tag-of-class-p (car memb) 'section))) + (setq memb (cdr memb))) + ;; No members? ... then a simple problem! + (if (not memb) + (semantic-tag-bounds tag) + ;; Our end is their beginning... + (list (semantic-tag-start tag) (semantic-tag-start (car memb)))))) + +(defun semantic-texi-current-environment (&optional point) + "Return as a string the type of the current environment. +Optional argument POINT is where to look for the environment." + (save-excursion + (when point (goto-char (point))) + (while (and (or (not (looking-at semantic-texi-environment-regexp)) + (looking-at "@end")) + (not (semantic-up-context))) + ) + (when (looking-at semantic-texi-environment-regexp) + (match-string 1)))) + + +;;; Analyzer +;; +(eval-when-compile + (require 'semantic/analyze)) + +(define-mode-local-override semantic-analyze-current-context + texinfo-mode (point) + "Analysis context makes no sense for texinfo. Return nil." + (let* ((prefixandbounds (semantic-ctxt-current-symbol-and-bounds (point))) + (prefix (car prefixandbounds)) + (bounds (nth 2 prefixandbounds)) + (prefixclass (semantic-ctxt-current-class-list)) + ) + (when prefix + (require 'semantic/analyze) + (semantic-analyze-context + "Context-for-texinfo" + :buffer (current-buffer) + :scope nil + :bounds bounds + :prefix prefix + :prefixtypes nil + :prefixclass prefixclass) + ) + )) + +(defvar semantic-texi-command-completion-list + (append (mapcar (lambda (a) (car a)) texinfo-section-list) + (condition-case nil + texinfo-environments + (error + ;; XEmacs doesn't use the above. Split up its regexp + (split-string texinfo-environment-regexp "\\\\|\\|\\^@\\\\(\\|\\\\)") + )) + ;; Is there a better list somewhere? Here are few + ;; of the top of my head. + "anchor" "asis" + "bullet" + "code" "copyright" + "defun" "deffn" "defoption" "defvar" "dfn" + "emph" "end" + "ifinfo" "iftex" "inforef" "item" "itemx" + "kdb" + "node" + "ref" + "set" "setfilename" "settitle" + "value" "var" + "xref" + ) + "List of commands that we might bother completing.") + +(define-mode-local-override semantic-analyze-possible-completions + texinfo-mode (context) + "List smart completions at point. +Since texinfo is not a programming language the default version is not +useful. Insted, look at the current symbol. If it is a command +do primitive texinfo built ins. If not, use ispell to lookup words +that start with that symbol." + (let ((prefix (car (oref context :prefix))) + ) + (cond ((member 'function (oref context :prefixclass)) + ;; Do completion for texinfo commands + (let* ((cmd (substring prefix 1)) + (lst (all-completions + cmd semantic-texi-command-completion-list))) + (mapcar (lambda (f) (semantic-tag (concat "@" f) 'function)) + lst)) + ) + ((member 'word (oref context :prefixclass)) + ;; Do completion for words via ispell. + (require 'ispell) + (let ((word-list (lookup-words prefix))) + (mapcar (lambda (f) (semantic-tag f 'word)) word-list)) + ) + (t nil)) + )) + + +;;; Parser Setup +;; +(defun semantic-default-texi-setup () + "Set up a buffer for parsing of Texinfo files." + ;; This will use our parser. + (semantic-install-function-overrides + '((parse-region . semantic-texi-parse-region) + (parse-changes . semantic-texi-parse-changes))) + (setq semantic-parser-name "TEXI" + ;; Setup a dummy parser table to enable parsing! + semantic--parse-table t + imenu-create-index-function 'semantic-create-imenu-index + semantic-command-separation-character "@" + semantic-type-relation-separator-character '(":") + semantic-symbol->name-assoc-list '((section . "Section") + (def . "Definition") + ) + semantic-imenu-expandable-tag-classes '(section) + semantic-imenu-bucketize-file nil + semantic-imenu-bucketize-type-members nil + senator-step-at-start-end-tag-classes '(section) + semantic-stickyfunc-sticky-classes '(section) + ) + ;; (local-set-key [(f9)] 'semantic-texi-update-doc-from-texi) + ) + +(add-hook 'texinfo-mode-hook 'semantic-default-texi-setup) + + +;;; Special features of Texinfo tag streams +;; +;; This section provides specialized access into texinfo files. +;; Because texinfo files often directly refer to functions and programs +;; it is useful to access the texinfo file from the C code for document +;; maintainance. +(defun semantic-texi-associated-files (&optional buffer) + "Find texinfo files associated with BUFFER." + (save-excursion + (if buffer (set-buffer buffer)) + (cond ((and (fboundp 'ede-documentation-files) + ede-minor-mode (ede-current-project)) + ;; When EDE is active, ask it. + (ede-documentation-files) + ) + ((and (featurep 'semantic/db) (semanticdb-minor-mode-p)) + ;; See what texinfo files we have loaded in the database + (let ((tabs (semanticdb-get-database-tables + semanticdb-current-database)) + (r nil)) + (while tabs + (if (eq (oref (car tabs) major-mode) 'texinfo-mode) + (setq r (cons (oref (car tabs) file) r))) + (setq tabs (cdr tabs))) + r)) + (t + (directory-files default-directory nil "\\.texi$")) + ))) + +;; Turns out this might not be useful. +;; Delete later if that is true. +(defun semantic-texi-find-documentation (name &optional type) + "Find the function or variable NAME of TYPE in the texinfo source. +NAME is a string representing some functional symbol. +TYPE is a string, such as \"variable\" or \"Command\" used to find +the correct definition in case NAME qualifies as several things. +When this function exists, POINT is at the definition. +If the doc was not found, an error is thrown. +Note: TYPE not yet implemented." + (let ((f (semantic-texi-associated-files)) + stream match) + (while (and f (not match)) + (unless stream + (with-current-buffer (find-file-noselect (car f)) + (setq stream (semantic-fetch-tags)))) + (setq match (semantic-find-first-tag-by-name name stream)) + (when match + (set-buffer (semantic-tag-buffer match)) + (goto-char (semantic-tag-start match))) + (setq f (cdr f))))) + +;; (defun semantic-texi-update-doc-from-texi (&optional tag) +;; "Update the documentation in the texinfo deffn class tag TAG. +;; The current buffer must be a texinfo file containing TAG. +;; If TAG is nil, determine a tag based on the current position." +;; (interactive) +;; (unless (or (featurep 'semantic/db) +;; (require 'semantic/db-mode) +;; (semanticdb-minor-mode-p)) +;; (error "Texinfo updating only works when `semanticdb' is being used")) +;; (semantic-fetch-tags) +;; (unless tag +;; (beginning-of-line) +;; (setq tag (semantic-current-tag))) +;; (unless (semantic-tag-of-class-p tag 'def) +;; (error "Only deffns (or defun or defvar) can be updated")) +;; (let* ((name (semantic-tag-name tag)) +;; (tags (semanticdb-strip-find-results +;; (semanticdb-with-match-any-mode +;; (semanticdb-brute-deep-find-tags-by-name name)) +;; 'name)) +;; (docstring nil) +;; (docstringproto nil) +;; (docstringvar nil) +;; (doctag nil) +;; (doctagproto nil) +;; (doctagvar nil) +;; ) +;; (save-excursion +;; (while (and tags (not docstring)) +;; (let ((sourcetag (car tags))) +;; ;; There could be more than one! Come up with a better +;; ;; solution someday. +;; (when (semantic-tag-buffer sourcetag) +;; (set-buffer (semantic-tag-buffer sourcetag)) +;; (unless (eq major-mode 'texinfo-mode) +;; (cond ((semantic-tag-get-attribute sourcetag :prototype-flag) +;; ;; If we found a match with doc that is a prototype, then store +;; ;; that, but don't exit till we find the real deal. +;; (setq docstringproto (semantic-documentation-for-tag sourcetag) +;; doctagproto sourcetag)) +;; ((eq (semantic-tag-class sourcetag) 'variable) +;; (setq docstringvar (semantic-documentation-for-tag sourcetag) +;; doctagvar sourcetag)) +;; ((semantic-tag-get-attribute sourcetag :override-function-flag) +;; nil) +;; (t +;; (setq docstring (semantic-documentation-for-tag sourcetag)))) +;; (setq doctag (if docstring sourcetag nil)))) +;; (setq tags (cdr tags))))) +;; ;; If we found a prototype of the function that has some doc, but not the +;; ;; actual function, lets make due with that. +;; (if (not docstring) +;; (cond ((stringp docstringvar) +;; (setq docstring docstringvar +;; doctag doctagvar)) +;; ((stringp docstringproto) +;; (setq docstring docstringproto +;; doctag doctagproto)))) +;; ;; Test for doc string +;; (unless docstring +;; (error "Could not find documentation for %s" (semantic-tag-name tag))) +;; ;; If we have a string, do the replacement. +;; (delete-region (semantic-tag-start tag) +;; (semantic-tag-end tag)) +;; ;; Use useful functions from the docaument library. +;; (require 'document) +;; (document-insert-texinfo doctag (semantic-tag-buffer doctag)) +;; )) + +;; (defun semantic-texi-update-doc-from-source (&optional tag) +;; "Update the documentation for the source TAG. +;; The current buffer must be a non-texinfo source file containing TAG. +;; If TAG is nil, determine the tag based on the current position. +;; The current buffer must include TAG." +;; (interactive) +;; (when (eq major-mode 'texinfo-mode) +;; (error "Not a source file")) +;; (semantic-fetch-tags) +;; (unless tag +;; (setq tag (semantic-current-tag))) +;; (unless (semantic-documentation-for-tag tag) +;; (error "Cannot find interesting documentation to use for %s" +;; (semantic-tag-name tag))) +;; (let* ((name (semantic-tag-name tag)) +;; (texi (semantic-texi-associated-files)) +;; (doctag nil) +;; (docbuff nil)) +;; (while (and texi (not doctag)) +;; (set-buffer (find-file-noselect (car texi))) +;; (setq doctag (car (semantic-deep-find-tags-by-name +;; name (semantic-fetch-tags))) +;; docbuff (if doctag (current-buffer) nil)) +;; (setq texi (cdr texi))) +;; (unless doctag +;; (error "Tag %s is not yet documented. Use the `document' command" +;; name)) +;; ;; Ok, we should have everything we need. Do the deed. +;; (if (get-buffer-window docbuff) +;; (set-buffer docbuff) +;; (switch-to-buffer docbuff)) +;; (goto-char (semantic-tag-start doctag)) +;; (delete-region (semantic-tag-start doctag) +;; (semantic-tag-end doctag)) +;; ;; Use useful functions from the document library. +;; (require 'document) +;; (document-insert-texinfo tag (semantic-tag-buffer tag)) +;; )) + +;; (defun semantic-texi-update-doc (&optional tag) +;; "Update the documentation for TAG. +;; If the current buffer is a texinfo file, then find the source doc, and +;; update it. If the current buffer is a source file, then get the +;; documentation for this item, find the existing doc in the associated +;; manual, and update that." +;; (interactive) +;; (cond ;;((eq major-mode 'texinfo-mode) +;; ;; (semantic-texi-update-doc-from-texi tag)) +;; (t +;; (semantic-texi-update-doc-from-source tag)))) + +(defun semantic-texi-goto-source (&optional tag) + "Jump to the source for the definition in the texinfo file TAG. +If TAG is nil, it is derived from the deffn under POINT." + (interactive) + (unless (or (featurep 'semantic/db) (semanticdb-minor-mode-p)) + (error "Texinfo updating only works when `semanticdb' is being used")) + (semantic-fetch-tags) + (unless tag + (beginning-of-line) + (setq tag (semantic-current-tag))) + (unless (semantic-tag-of-class-p tag 'def) + (error "Only deffns (or defun or defvar) can be updated")) + (let* ((name (semantic-tag-name tag)) + (tags (semanticdb-fast-strip-find-results + (semanticdb-with-match-any-mode + (semanticdb-brute-deep-find-tags-by-name name nil 'name)) + )) + + (done nil) + ) + (save-excursion + (while (and tags (not done)) + (set-buffer (semantic-tag-buffer (car tags))) + (unless (eq major-mode 'texinfo-mode) + (switch-to-buffer (semantic-tag-buffer (car tags))) + (goto-char (semantic-tag-start (car tags))) + (setq done t)) + (setq tags (cdr tags))) + (if (not done) + (error "Could not find tag for %s" (semantic-tag-name tag))) + ))) + +(provide 'semantic/texi) + +;;; semantic/texi.el ends here diff --git a/lisp/cedet/semantic/util-modes.el b/lisp/cedet/semantic/util-modes.el new file mode 100644 index 00000000000..6b64287840e --- /dev/null +++ b/lisp/cedet/semantic/util-modes.el @@ -0,0 +1,1237 @@ +;;; semantic/util-modes.el --- Semantic minor modes + +;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 +;;; Free Software Foundation, Inc. + +;; Authors: Eric M. Ludlam <zappo@gnu.org> +;; David Ponce <david@dponce.com> +;; 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: +;; +;; Semantic utility minor modes. +;; + +;;; Code: +(require 'semantic) + +;;; Group for all semantic enhancing modes +(defgroup semantic-modes nil + "Minor modes associated with the Semantic architecture." + :group 'semantic) + +;;;; +;;;; Semantic minor modes stuff +;;;; +(defcustom semantic-update-mode-line t + "If non-nil, show enabled minor modes in the mode line. +Only minor modes that are not turned on globally are shown in the mode +line." + :group 'semantic + :type 'boolean + :require 'semantic/util-modes + :initialize 'custom-initialize-default + :set (lambda (sym val) + (set-default sym val) + ;; Update status of all Semantic enabled buffers + (semantic-map-buffers + #'semantic-mode-line-update))) + +(defcustom semantic-mode-line-prefix + (propertize "S" 'face 'bold) + "Prefix added to minor mode indicators in the mode line." + :group 'semantic + :type 'string + :require 'semantic/util-modes + :initialize 'custom-initialize-default) + +(defvar semantic-minor-modes-status nil + "String showing Semantic minor modes which are locally enabled. +It is displayed in the mode line.") +(make-variable-buffer-local 'semantic-minor-modes-status) + +(defvar semantic-minor-mode-alist nil + "Alist saying how to show Semantic minor modes in the mode line. +Like variable `minor-mode-alist'.") + +(defun semantic-mode-line-update () + "Update display of Semantic minor modes in the mode line. +Only minor modes that are locally enabled are shown in the mode line." + (setq semantic-minor-modes-status nil) + (if semantic-update-mode-line + (let ((ml semantic-minor-mode-alist) + mm ms see) + (while ml + (setq mm (car ml) + ms (cadr mm) + mm (car mm) + ml (cdr ml)) + (when (and (symbol-value mm) + ;; Only show local minor mode status + (not (memq mm semantic-init-hook))) + (and ms + (symbolp ms) + (setq ms (symbol-value ms))) + (and (stringp ms) + (not (member ms see)) ;; Don't duplicate same status + (setq see (cons ms see) + ms (if (string-match "^[ ]*\\(.+\\)" ms) + (match-string 1 ms))) + (setq semantic-minor-modes-status + (if semantic-minor-modes-status + (concat semantic-minor-modes-status "/" ms) + ms))))) + (if semantic-minor-modes-status + (setq semantic-minor-modes-status + (concat + " " + (if (string-match "^[ ]*\\(.+\\)" + semantic-mode-line-prefix) + (match-string 1 semantic-mode-line-prefix) + "S") + "/" + semantic-minor-modes-status)))))) + +(defun semantic-desktop-ignore-this-minor-mode (buffer) + "Installed as a minor-mode initializer for Desktop mode. +BUFFER is the buffer to not initialize a Semantic minor mode in." + nil) + +(defun semantic-add-minor-mode (toggle name &optional keymap) + "Register a new Semantic minor mode. +TOGGLE is a symbol which is the name of a buffer-local variable that +is toggled on or off to say whether the minor mode is active or not. +It is also an interactive function to toggle the mode. + +NAME specifies what will appear in the mode line when the minor mode +is active. NAME should be either a string starting with a space, or a +symbol whose value is such a string. + +Optional KEYMAP is the keymap for the minor mode that will be added to +`minor-mode-map-alist'." + ;; Add a dymmy semantic minor mode to display the status + (or (assq 'semantic-minor-modes-status minor-mode-alist) + (setq minor-mode-alist (cons (list 'semantic-minor-modes-status + 'semantic-minor-modes-status) + minor-mode-alist))) + (if (fboundp 'add-minor-mode) + ;; Emacs 21 & XEmacs + (add-minor-mode toggle "" keymap) + ;; Emacs 20 + (or (assq toggle minor-mode-alist) + (setq minor-mode-alist (cons (list toggle "") minor-mode-alist))) + (or (not keymap) + (assq toggle minor-mode-map-alist) + (setq minor-mode-map-alist (cons (cons toggle keymap) + minor-mode-map-alist)))) + ;; Record how to display this minor mode in the mode line + (let ((mm (assq toggle semantic-minor-mode-alist))) + (if mm + (setcdr mm (list name)) + (setq semantic-minor-mode-alist (cons (list toggle name) + semantic-minor-mode-alist)))) + + ;; Semantic minor modes don't work w/ Desktop restore. + ;; This line will disable this minor mode from being restored + ;; by Desktop. + (when (boundp 'desktop-minor-mode-handlers) + (add-to-list 'desktop-minor-mode-handlers + (cons toggle 'semantic-desktop-ignore-this-minor-mode))) + ) + +(defun semantic-toggle-minor-mode-globally (mode &optional arg) + "Toggle minor mode MODE in every Semantic enabled buffer. +Return non-nil if MODE is turned on in every Semantic enabled buffer. +If ARG is positive, enable, if it is negative, disable. If ARG is +nil, then toggle. Otherwise do nothing. MODE must be a valid minor +mode defined in `minor-mode-alist' and must be too an interactive +function used to toggle the mode." + (or (and (fboundp mode) (assq mode minor-mode-alist)) + (error "Semantic minor mode %s not found" mode)) + (if (not arg) + (if (memq mode semantic-init-hook) + (setq arg -1) + (setq arg 1))) + ;; Add or remove the MODE toggle function from + ;; `semantic-init-hook'. Then turn MODE on or off in every + ;; Semantic enabled buffer. + (cond + ;; Turn off if ARG < 0 + ((< arg 0) + (remove-hook 'semantic-init-hook mode) + (semantic-map-buffers #'(lambda () (funcall mode -1))) + nil) + ;; Turn on if ARG > 0 + ((> arg 0) + (add-hook 'semantic-init-hook mode) + (semantic-map-buffers #'(lambda () (funcall mode 1))) + t) + ;; Otherwise just check MODE state + (t + (memq mode semantic-init-hook)) + )) + +;;;; +;;;; Minor mode to highlight areas that a user edits. +;;;; + +;;;###autoload +(defun global-semantic-highlight-edits-mode (&optional arg) + "Toggle global use of option `semantic-highlight-edits-mode'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-highlight-edits-mode + (semantic-toggle-minor-mode-globally + 'semantic-highlight-edits-mode arg))) + +;;;###autoload +(defcustom global-semantic-highlight-edits-mode nil + "If non-nil enable global use of variable `semantic-highlight-edits-mode'. +When this mode is enabled, changes made to a buffer are highlighted +until the buffer is reparsed." + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic/util-modes + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-highlight-edits-mode (if val 1 -1)))) + +(defcustom semantic-highlight-edits-mode-hook nil + "Hook run at the end of function `semantic-highlight-edits-mode'." + :group 'semantic + :type 'hook) + +(defface semantic-highlight-edits-face + '((((class color) (background dark)) + ;; Put this back to something closer to black later. + (:background "gray20")) + (((class color) (background light)) + (:background "gray90"))) + "Face used to show dirty tokens in `semantic-highlight-edits-mode'." + :group 'semantic-faces) + +(defun semantic-highlight-edits-new-change-hook-fcn (overlay) + "Function set into `semantic-edits-new-change-hook'. +Argument OVERLAY is the overlay created to mark the change. +This function will set the face property on this overlay." + (semantic-overlay-put overlay 'face 'semantic-highlight-edits-face)) + +(defvar semantic-highlight-edits-mode-map + (let ((km (make-sparse-keymap))) + km) + "Keymap for highlight-edits minor mode.") + +(defvar semantic-highlight-edits-mode nil + "Non-nil if highlight-edits minor mode is enabled. +Use the command `semantic-highlight-edits-mode' to change this variable.") +(make-variable-buffer-local 'semantic-highlight-edits-mode) + +(defun semantic-highlight-edits-mode-setup () + "Setup option `semantic-highlight-edits-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-highlight-edits-mode + (if (not (and (featurep 'semantic) (semantic-active-p))) + (progn + ;; Disable minor mode if semantic stuff not available + (setq semantic-highlight-edits-mode nil) + (error "Buffer %s was not set up for parsing" + (buffer-name))) + (semantic-make-local-hook 'semantic-edits-new-change-hooks) + (add-hook 'semantic-edits-new-change-hooks + 'semantic-highlight-edits-new-change-hook-fcn nil t) + ) + ;; Remove hooks + (remove-hook 'semantic-edits-new-change-hooks + 'semantic-highlight-edits-new-change-hook-fcn t) + ) + semantic-highlight-edits-mode) + +;;;###autoload +(defun semantic-highlight-edits-mode (&optional arg) + "Minor mode for highlighting changes made in a buffer. +Changes are tracked by semantic so that the incremental parser can work +properly. +This mode will highlight those changes as they are made, and clear them +when the incremental parser accounts for those edits. +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-highlight-edits-mode 0 1)))) + (setq semantic-highlight-edits-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-highlight-edits-mode))) + (semantic-highlight-edits-mode-setup) + (run-hooks 'semantic-highlight-edits-mode-hook) + (if (interactive-p) + (message "highlight-edits minor mode %sabled" + (if semantic-highlight-edits-mode "en" "dis"))) + (semantic-mode-line-update) + semantic-highlight-edits-mode) + +(semantic-add-minor-mode 'semantic-highlight-edits-mode + "e" + semantic-highlight-edits-mode-map) + + +;;;; +;;;; Minor mode to show unmatched-syntax elements +;;;; + +;;;###autoload +(defun global-semantic-show-unmatched-syntax-mode (&optional arg) + "Toggle global use of option `semantic-show-unmatched-syntax-mode'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-show-unmatched-syntax-mode + (semantic-toggle-minor-mode-globally + 'semantic-show-unmatched-syntax-mode arg))) + +;;;###autoload +(defcustom global-semantic-show-unmatched-syntax-mode nil + "If non-nil, enable global use of `semantic-show-unmatched-syntax-mode'. +When this mode is enabled, syntax in the current buffer which the +semantic parser cannot match is highlighted with a red underline." + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic/util-modes + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-show-unmatched-syntax-mode (if val 1 -1)))) + +(defcustom semantic-show-unmatched-syntax-mode-hook nil + "Hook run at the end of function `semantic-show-unmatched-syntax-mode'." + :group 'semantic + :type 'hook) + +(defface semantic-unmatched-syntax-face + '((((class color) (background dark)) + (:underline "red")) + (((class color) (background light)) + (:underline "red"))) + "Face used to show unmatched syntax in. +The face is used in `semantic-show-unmatched-syntax-mode'." + :group 'semantic-faces) + +(defsubst semantic-unmatched-syntax-overlay-p (overlay) + "Return non-nil if OVERLAY is an unmatched syntax one." + (eq (semantic-overlay-get overlay 'semantic) 'unmatched)) + +(defun semantic-showing-unmatched-syntax-p () + "Return non-nil if an unmatched syntax overlay was found in buffer." + (let ((ol (semantic-overlays-in (point-min) (point-max))) + found) + (while (and ol (not found)) + (setq found (semantic-unmatched-syntax-overlay-p (car ol)) + ol (cdr ol))) + found)) + +(defun semantic-show-unmatched-lex-tokens-fetch () + "Fetch a list of unmatched lexical tokens from the current buffer. +Uses the overlays which have accurate bounds, and rebuilds what was +originally passed in." + (let ((ol (semantic-overlays-in (point-min) (point-max))) + (ustc nil)) + (while ol + (if (semantic-unmatched-syntax-overlay-p (car ol)) + (setq ustc (cons (cons 'thing + (cons (semantic-overlay-start (car ol)) + (semantic-overlay-end (car ol)))) + ustc))) + (setq ol (cdr ol))) + (nreverse ustc)) + ) + +(defun semantic-clean-unmatched-syntax-in-region (beg end) + "Remove all unmatched syntax overlays between BEG and END." + (let ((ol (semantic-overlays-in beg end))) + (while ol + (if (semantic-unmatched-syntax-overlay-p (car ol)) + (semantic-overlay-delete (car ol))) + (setq ol (cdr ol))))) + +(defsubst semantic-clean-unmatched-syntax-in-buffer () + "Remove all unmatched syntax overlays found in current buffer." + (semantic-clean-unmatched-syntax-in-region + (point-min) (point-max))) + +(defsubst semantic-clean-token-of-unmatched-syntax (token) + "Clean the area covered by TOKEN of unmatched syntax markers." + (semantic-clean-unmatched-syntax-in-region + (semantic-tag-start token) (semantic-tag-end token))) + +(defun semantic-show-unmatched-syntax (syntax) + "Function set into `semantic-unmatched-syntax-hook'. +This will highlight elements in SYNTAX as unmatched syntax." + ;; This is called when `semantic-show-unmatched-syntax-mode' is + ;; enabled. Highlight the unmatched syntax, and then add a semantic + ;; property to that overlay so we can add it to the official list of + ;; semantic supported overlays. This gets it cleaned up for errors, + ;; buffer cleaning, and the like. + (semantic-clean-unmatched-syntax-in-buffer) ;Clear previous highlighting + (if syntax + (let (o) + (while syntax + (setq o (semantic-make-overlay (semantic-lex-token-start (car syntax)) + (semantic-lex-token-end (car syntax)))) + (semantic-overlay-put o 'semantic 'unmatched) + (semantic-overlay-put o 'face 'semantic-unmatched-syntax-face) + (setq syntax (cdr syntax)))) + )) + +(defun semantic-next-unmatched-syntax (point &optional bound) + "Find the next overlay for unmatched syntax after POINT. +Do not search past BOUND if non-nil." + (save-excursion + (goto-char point) + (let ((os point) (ol nil)) + (while (and os (< os (or bound (point-max))) (not ol)) + (setq os (semantic-overlay-next-change os)) + (when os + ;; Get overlays at position + (setq ol (semantic-overlays-at os)) + ;; find the overlay that belongs to semantic + ;; and starts at the found position. + (while (and ol (listp ol)) + (and (semantic-unmatched-syntax-overlay-p (car ol)) + (setq ol (car ol))) + (if (listp ol) + (setq ol (cdr ol)))))) + ol))) + +(defvar semantic-show-unmatched-syntax-mode-map + (let ((km (make-sparse-keymap))) + (define-key km "\C-c,`" 'semantic-show-unmatched-syntax-next) + km) + "Keymap for command `semantic-show-unmatched-syntax-mode'.") + +(defvar semantic-show-unmatched-syntax-mode nil + "Non-nil if show-unmatched-syntax minor mode is enabled. +Use the command `semantic-show-unmatched-syntax-mode' to change this +variable.") +(make-variable-buffer-local 'semantic-show-unmatched-syntax-mode) + +(defun semantic-show-unmatched-syntax-mode-setup () + "Setup the `semantic-show-unmatched-syntax' minor 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-show-unmatched-syntax-mode + (if (not (and (featurep 'semantic) (semantic-active-p))) + (progn + ;; Disable minor mode if semantic stuff not available + (setq semantic-show-unmatched-syntax-mode nil) + (error "Buffer %s was not set up for parsing" + (buffer-name))) + ;; Add hooks + (semantic-make-local-hook 'semantic-unmatched-syntax-hook) + (add-hook 'semantic-unmatched-syntax-hook + 'semantic-show-unmatched-syntax nil t) + (semantic-make-local-hook 'semantic-pre-clean-token-hooks) + (add-hook 'semantic-pre-clean-token-hooks + 'semantic-clean-token-of-unmatched-syntax nil t) + ;; Show unmatched syntax elements + (if (not (semantic--umatched-syntax-needs-refresh-p)) + (semantic-show-unmatched-syntax + (semantic-unmatched-syntax-tokens)))) + ;; Remove hooks + (remove-hook 'semantic-unmatched-syntax-hook + 'semantic-show-unmatched-syntax t) + (remove-hook 'semantic-pre-clean-token-hooks + 'semantic-clean-token-of-unmatched-syntax t) + ;; Cleanup unmatched-syntax highlighting + (semantic-clean-unmatched-syntax-in-buffer)) + semantic-show-unmatched-syntax-mode) + +;;;###autoload +(defun semantic-show-unmatched-syntax-mode (&optional arg) + "Minor mode to highlight unmatched lexical syntax tokens. +When a parser executes, some elements in the buffer may not match any +parser rules. These text characters are considered unmatched syntax. +Often time, the display of unmatched syntax can expose coding +problems before the compiler is run. + +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. + +\\{semantic-show-unmatched-syntax-mode-map}" + (interactive + (list (or current-prefix-arg + (if semantic-show-unmatched-syntax-mode 0 1)))) + (setq semantic-show-unmatched-syntax-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-show-unmatched-syntax-mode))) + (semantic-show-unmatched-syntax-mode-setup) + (run-hooks 'semantic-show-unmatched-syntax-mode-hook) + (if (interactive-p) + (message "show-unmatched-syntax minor mode %sabled" + (if semantic-show-unmatched-syntax-mode "en" "dis"))) + (semantic-mode-line-update) + semantic-show-unmatched-syntax-mode) + +(semantic-add-minor-mode 'semantic-show-unmatched-syntax-mode + "u" + semantic-show-unmatched-syntax-mode-map) + +(defun semantic-show-unmatched-syntax-next () + "Move forward to the next occurrence of unmatched syntax." + (interactive) + (let ((o (semantic-next-unmatched-syntax (point)))) + (if o + (goto-char (semantic-overlay-start o))))) + + +;;;; +;;;; Minor mode to display the parser state in the modeline. +;;;; + +;;;###autoload +(defcustom global-semantic-show-parser-state-mode nil + "If non-nil enable global use of `semantic-show-parser-state-mode'. +When enabled, the current parse state of the current buffer is displayed +in the mode line. See `semantic-show-parser-state-marker' for details +on what is displayed." + :group 'semantic + :type 'boolean + :require 'semantic/util-modes + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-show-parser-state-mode (if val 1 -1)))) + +;;;###autoload +(defun global-semantic-show-parser-state-mode (&optional arg) + "Toggle global use of option `semantic-show-parser-state-mode'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-show-parser-state-mode + (semantic-toggle-minor-mode-globally + 'semantic-show-parser-state-mode arg))) + +(defcustom semantic-show-parser-state-mode-hook nil + "Hook run at the end of function `semantic-show-parser-state-mode'." + :group 'semantic + :type 'hook) + +(defvar semantic-show-parser-state-mode-map + (let ((km (make-sparse-keymap))) + km) + "Keymap for show-parser-state minor mode.") + +(defvar semantic-show-parser-state-mode nil + "Non-nil if show-parser-state minor mode is enabled. +Use the command `semantic-show-parser-state-mode' to change this variable.") +(make-variable-buffer-local 'semantic-show-parser-state-mode) + +(defun semantic-show-parser-state-mode-setup () + "Setup option `semantic-show-parser-state-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-show-parser-state-mode + (if (not (and (featurep 'semantic) (semantic-active-p))) + (progn + ;; Disable minor mode if semantic stuff not available + (setq semantic-show-parser-state-mode nil) + (error "Buffer %s was not set up for parsing" + (buffer-name))) + ;; Set up mode line + + (when (not + (memq 'semantic-show-parser-state-string mode-line-modified)) + (setq mode-line-modified + (append mode-line-modified + '(semantic-show-parser-state-string)))) + ;; Add hooks + (semantic-make-local-hook 'semantic-edits-new-change-hooks) + (add-hook 'semantic-edits-new-change-hooks + 'semantic-show-parser-state-marker nil t) + (semantic-make-local-hook 'semantic-edits-incremental-reparse-failed-hook) + (add-hook 'semantic-edits-incremental-reparse-failed-hook + 'semantic-show-parser-state-marker nil t) + (semantic-make-local-hook 'semantic-after-partial-cache-change-hook) + (add-hook 'semantic-after-partial-cache-change-hook + 'semantic-show-parser-state-marker nil t) + (semantic-make-local-hook 'semantic-after-toplevel-cache-change-hook) + (add-hook 'semantic-after-toplevel-cache-change-hook + 'semantic-show-parser-state-marker nil t) + (semantic-show-parser-state-marker) + + (semantic-make-local-hook 'semantic-before-auto-parse-hooks) + (add-hook 'semantic-before-auto-parse-hooks + 'semantic-show-parser-state-auto-marker nil t) + (semantic-make-local-hook 'semantic-after-auto-parse-hooks) + (add-hook 'semantic-after-auto-parse-hooks + 'semantic-show-parser-state-marker nil t) + + (semantic-make-local-hook 'semantic-before-idle-scheduler-reparse-hook) + (add-hook 'semantic-before-idle-scheduler-reparse-hook + 'semantic-show-parser-state-auto-marker nil t) + (semantic-make-local-hook 'semantic-after-idle-scheduler-reparse-hook) + (add-hook 'semantic-after-idle-scheduler-reparse-hook + 'semantic-show-parser-state-marker nil t) + ) + ;; Remove parts of mode line + (setq mode-line-modified + (delq 'semantic-show-parser-state-string mode-line-modified)) + ;; Remove hooks + (remove-hook 'semantic-edits-new-change-hooks + 'semantic-show-parser-state-marker t) + (remove-hook 'semantic-edits-incremental-reparse-failed-hook + 'semantic-show-parser-state-marker t) + (remove-hook 'semantic-after-partial-cache-change-hook + 'semantic-show-parser-state-marker t) + (remove-hook 'semantic-after-toplevel-cache-change-hook + 'semantic-show-parser-state-marker t) + + (remove-hook 'semantic-before-auto-parse-hooks + 'semantic-show-parser-state-auto-marker t) + (remove-hook 'semantic-after-auto-parse-hooks + 'semantic-show-parser-state-marker t) + + (remove-hook 'semantic-before-idle-scheduler-reparse-hook + 'semantic-show-parser-state-auto-marker t) + (remove-hook 'semantic-after-idle-scheduler-reparse-hook + 'semantic-show-parser-state-marker t) + ) + semantic-show-parser-state-mode) + +;;;###autoload +(defun semantic-show-parser-state-mode (&optional arg) + "Minor mode for displaying parser cache state in the modeline. +The cache can be in one of three states. They are +Up to date, Partial reprase needed, and Full reparse needed. +The state is indicated in the modeline with the following characters: + `-' -> The cache is up to date. + `!' -> The cache requires a full update. + `~' -> The cache needs to be incrementally parsed. + `%' -> The cache is not currently parseable. + `@' -> Auto-parse in progress (not set here.) +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-show-parser-state-mode 0 1)))) + (setq semantic-show-parser-state-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-show-parser-state-mode))) + (semantic-show-parser-state-mode-setup) + (run-hooks 'semantic-show-parser-state-mode-hook) + (if (interactive-p) + (message "show-parser-state minor mode %sabled" + (if semantic-show-parser-state-mode "en" "dis"))) + (semantic-mode-line-update) + semantic-show-parser-state-mode) + +(semantic-add-minor-mode 'semantic-show-parser-state-mode + "" + semantic-show-parser-state-mode-map) + +(defvar semantic-show-parser-state-string nil + "String showing the parser state for this buffer. +See `semantic-show-parser-state-marker' for details.") +(make-variable-buffer-local 'semantic-show-parser-state-string) + +(defun semantic-show-parser-state-marker (&rest ignore) + "Set `semantic-show-parser-state-string' to indicate parser state. +This marker is one of the following: + `-' -> The cache is up to date. + `!' -> The cache requires a full update. + `~' -> The cache needs to be incrementally parsed. + `%' -> The cache is not currently parseable. + `@' -> Auto-parse in progress (not set here.) +Arguments IGNORE are ignored, and accepted so this can be used as a hook +in many situations." + (setq semantic-show-parser-state-string + (cond ((semantic-parse-tree-needs-rebuild-p) + "!") + ((semantic-parse-tree-needs-update-p) + "^") + ((semantic-parse-tree-unparseable-p) + "%") + (t + "-"))) + ;;(message "Setup mode line indicator to [%s]" semantic-show-parser-state-string) + (semantic-mode-line-update)) + +(defun semantic-show-parser-state-auto-marker () + "Hook function run before an autoparse. +Set up `semantic-show-parser-state-marker' to show `@' +to indicate a parse in progress." + (unless (semantic-parse-tree-up-to-date-p) + (setq semantic-show-parser-state-string "@") + (semantic-mode-line-update) + ;; For testing. + ;;(sit-for 1) + )) + + +;;;; +;;;; Minor mode to make function decls sticky. +;;;; + +;;;###autoload +(defun global-semantic-stickyfunc-mode (&optional arg) + "Toggle global use of option `semantic-stickyfunc-mode'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-stickyfunc-mode + (semantic-toggle-minor-mode-globally + 'semantic-stickyfunc-mode arg))) + +;;;###autoload +(defcustom global-semantic-stickyfunc-mode nil + "If non-nil, enable global use of `semantic-stickyfunc-mode'. +This minor mode only works for Emacs 21 or later. +When enabled, the header line is enabled, and the first line +of the current function or method is displayed in it. +This makes it appear that the first line of that tag is +`sticky' to the top of the window." + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic/util-modes + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-stickyfunc-mode (if val 1 -1)))) + +(defcustom semantic-stickyfunc-mode-hook nil + "Hook run at the end of function `semantic-stickyfunc-mode'." + :group 'semantic + :type 'hook) + +(defvar semantic-stickyfunc-mode-map + (let ((km (make-sparse-keymap))) + (define-key km [ header-line down-mouse-1 ] 'semantic-stickyfunc-menu) + km) + "Keymap for stickyfunc minor mode.") + +(defvar semantic-stickyfunc-popup-menu nil + "Menu used if the user clicks on the header line used by stickyfunc mode.") + +(easy-menu-define + semantic-stickyfunc-popup-menu + semantic-stickyfunc-mode-map + "Stickyfunc Menu" + '("Stickyfunc Mode" :visible (progn nil) + [ "Copy Headerline Tag" senator-copy-tag + :active (semantic-current-tag) + :help "Copy the current tag to the tag ring"] + [ "Kill Headerline Tag" senator-kill-tag + :active (semantic-current-tag) + :help "Kill tag text to the kill ring, and copy the tag to the tag ring" + ] + [ "Copy Headerline Tag to Register" senator-copy-tag-to-register + :active (semantic-current-tag) + :help "Copy the current tag to a register" + ] + [ "Narrow To Headerline Tag" senator-narrow-to-defun + :active (semantic-current-tag) + :help "Narrow to the bounds of the current tag."] + [ "Fold Headerline Tag" senator-fold-tag-toggle + :active (semantic-current-tag) + :style toggle + :selected (let ((tag (semantic-current-tag))) + (and tag (semantic-tag-folded-p tag))) + :help "Fold the current tag to one line" + ] + "---" + [ "About This Header Line" + (lambda () (interactive) + (describe-function 'semantic-stickyfunc-mode)) t]) + ) + +(defvar semantic-stickyfunc-mode nil + "Non-nil if stickyfunc minor mode is enabled. +Use the command `semantic-stickyfunc-mode' to change this variable.") +(make-variable-buffer-local 'semantic-stickyfunc-mode) + +(defcustom semantic-stickyfunc-indent-string + (if (and window-system (not (featurep 'xemacs))) + (concat + (condition-case nil + ;; Test scroll bar location + (let ((charwidth (frame-char-width)) + (scrollpos (frame-parameter (selected-frame) + 'vertical-scroll-bars)) + ) + (if (or (eq scrollpos 'left) + ;; Now wait a minute. If you turn scroll-bar-mode + ;; on, then off, the new value is t, not left. + ;; Will this mess up older emacs where the default + ;; was on the right? I don't think so since they don't + ;; support a header line. + (eq scrollpos t)) + (let ((w (when (boundp 'scroll-bar-width) + (symbol-value 'scroll-bar-width)))) + + (if (not w) + (setq w (frame-parameter (selected-frame) + 'scroll-bar-width))) + + ;; in 21.2, the frame parameter is sometimes empty + ;; so we need to get the value here. + (if (not w) + (setq w (+ (get 'scroll-bar-width 'x-frame-parameter) + ;; In 21.4, or perhaps 22.1 the x-frame + ;; parameter is different from the frame + ;; parameter by only 1 pixel. + 1))) + + (if (not w) + " " + (setq w (+ 2 w)) ; Some sort of border around + ; the scrollbar. + (make-string (/ w charwidth) ? ))) + "")) + (error "")) + (condition-case nil + ;; Test fringe size. + (let* ((f (window-fringes)) + (fw (car f)) + (numspace (/ fw (frame-char-width))) + ) + (make-string numspace ? )) + (error + ;; Well, the fancy new Emacs functions failed. Try older + ;; tricks. + (condition-case nil + ;; I'm not so sure what's up with the 21.1-21.3 fringe. + ;; It looks to be about 1 space wide. + (if (get 'fringe 'face) + " " + "") + (error "")))) + ) + ;; Not Emacs or a window system means no scrollbar or fringe, + ;; and perhaps not even a header line to worry about. + "") + "String used to indent the stickyfunc header. +Customize this string to match the space used by scrollbars and +fringe so it does not appear that the code is moving left/right +when it lands in the sticky line." + :group 'semantic + :type 'string) + +(defvar semantic-stickyfunc-old-hlf nil + "Value of the header line when entering sticky func mode.") + +(defconst semantic-stickyfunc-header-line-format + (cond ((featurep 'xemacs) + nil) + ((>= emacs-major-version 22) + '(:eval (list + ;; Magic bit I found on emacswiki. + (propertize " " 'display '((space :align-to 0))) + (semantic-stickyfunc-fetch-stickyline)))) + ((= emacs-major-version 21) + '(:eval (list semantic-stickyfunc-indent-string + (semantic-stickyfunc-fetch-stickyline)))) + (t nil)) + "The header line format used by sticky func mode.") + +(defun semantic-stickyfunc-mode-setup () + "Setup option `semantic-stickyfunc-mode'. +For semantic enabled buffers, make the function declaration for the top most +function \"sticky\". This is accomplished by putting the first line of +text for that function in Emacs 21's header line." + (if semantic-stickyfunc-mode + (progn + (unless (and (featurep 'semantic) (semantic-active-p)) + ;; Disable minor mode if semantic stuff not available + (setq semantic-stickyfunc-mode nil) + (error "Buffer %s was not set up for parsing" (buffer-name))) + (unless (boundp 'default-header-line-format) + ;; Disable if there are no header lines to use. + (setq semantic-stickyfunc-mode nil) + (error "Sticky Function mode requires Emacs 21")) + ;; Enable the mode + ;; Save previous buffer local value of header line format. + (when (and (local-variable-p 'header-line-format (current-buffer)) + (not (eq header-line-format + semantic-stickyfunc-header-line-format))) + (set (make-local-variable 'semantic-stickyfunc-old-hlf) + header-line-format)) + (setq header-line-format semantic-stickyfunc-header-line-format) + ) + ;; Disable sticky func mode + ;; Restore previous buffer local value of header line format if + ;; the current one is the sticky func one. + (when (eq header-line-format semantic-stickyfunc-header-line-format) + (kill-local-variable 'header-line-format) + (when (local-variable-p 'semantic-stickyfunc-old-hlf (current-buffer)) + (setq header-line-format semantic-stickyfunc-old-hlf) + (kill-local-variable 'semantic-stickyfunc-old-hlf)))) + semantic-stickyfunc-mode) + +;;;###autoload +(defun semantic-stickyfunc-mode (&optional arg) + "Minor mode to show the title of a tag in the header line. +Enables/disables making the header line of functions sticky. +A function (or other tag class specified by +`semantic-stickyfunc-sticky-classes') has a header line, meaning the +first line which describes the rest of the construct. This first +line is what is displayed in the Emacs 21 header line. + +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-stickyfunc-mode 0 1)))) + (setq semantic-stickyfunc-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-stickyfunc-mode))) + (semantic-stickyfunc-mode-setup) + (run-hooks 'semantic-stickyfunc-mode-hook) + (if (interactive-p) + (message "Stickyfunc minor mode %sabled" + (if semantic-stickyfunc-mode "en" "dis"))) + (semantic-mode-line-update) + semantic-stickyfunc-mode) + +(defvar semantic-stickyfunc-sticky-classes + '(function type) + "List of tag classes which sticky func will display in the header line.") +(make-variable-buffer-local 'semantic-stickyfunc-sticky-classes) + +(defun semantic-stickyfunc-tag-to-stick () + "Return the tag to stick at the current point." + (let ((tags (nreverse (semantic-find-tag-by-overlay (point))))) + ;; Get rid of non-matching tags. + (while (and tags + (not (member + (semantic-tag-class (car tags)) + semantic-stickyfunc-sticky-classes)) + ) + (setq tags (cdr tags))) + (car tags))) + +(defun semantic-stickyfunc-fetch-stickyline () + "Make the function at the top of the current window sticky. +Capture it's function declaration, and place it in the header line. +If there is no function, disable the header line." + (let ((str + (save-excursion + (goto-char (window-start (selected-window))) + (forward-line -1) + (end-of-line) + ;; Capture this function + (let* ((tag (semantic-stickyfunc-tag-to-stick))) + ;; TAG is nil if there was nothing of the apropriate type there. + (if (not tag) + ;; Set it to be the text under the header line + (buffer-substring (point-at-bol) (point-at-eol)) + ;; Get it + (goto-char (semantic-tag-start tag)) + ;; Klaus Berndl <klaus.berndl@sdm.de>: + ;; goto the tag name; this is especially needed for languages + ;; like c++ where a often used style is like: + ;; void + ;; ClassX::methodM(arg1...) + ;; { + ;; ... + ;; } + ;; Without going to the tag-name we would get"void" in the + ;; header line which is IMHO not really useful + (search-forward (semantic-tag-name tag) nil t) + (buffer-substring (point-at-bol) (point-at-eol)) + )))) + (start 0)) + (while (string-match "%" str start) + (setq str (replace-match "%%" t t str 0) + start (1+ (match-end 0))) + ) + ;; In 21.4 (or 22.1) the heder doesn't expand tabs. Hmmmm. + ;; We should replace them here. + ;; + ;; This hack assumes that tabs are kept smartly at tab boundaries + ;; instead of in a tab boundary where it might only represent 4 spaces. + (while (string-match "\t" str start) + (setq str (replace-match " " t t str 0))) + str)) + +(defun semantic-stickyfunc-menu (event) + "Popup a menu that can help a user understand stickyfunc-mode. +Argument EVENT describes the event that caused this function to be called." + (interactive "e") + (let* ((startwin (selected-window)) + (win (car (car (cdr event)))) + ) + (select-window win t) + (save-excursion + (goto-char (window-start win)) + (sit-for 0) + (popup-menu semantic-stickyfunc-popup-menu event) + ) + (select-window startwin))) + + +(semantic-add-minor-mode 'semantic-stickyfunc-mode + "" ;; Don't need indicator. It's quite visible + semantic-stickyfunc-mode-map) + + + +;;;; +;;;; Minor mode to make highlight the current function +;;;; + +;; Highlight the first like of the function we are in if it is different +;; from the the tag going off the top of the screen. + +;;;###autoload +(defun global-semantic-highlight-func-mode (&optional arg) + "Toggle global use of option `semantic-highlight-func-mode'. +If ARG is positive, enable, if it is negative, disable. +If ARG is nil, then toggle." + (interactive "P") + (setq global-semantic-highlight-func-mode + (semantic-toggle-minor-mode-globally + 'semantic-highlight-func-mode arg))) + +;;;###autoload +(defcustom global-semantic-highlight-func-mode nil + "If non-nil, enable global use of `semantic-highlight-func-mode'. +When enabled, the first line of the current tag is highlighted." + :group 'semantic + :group 'semantic-modes + :type 'boolean + :require 'semantic/util-modes + :initialize 'custom-initialize-default + :set (lambda (sym val) + (global-semantic-highlight-func-mode (if val 1 -1)))) + +(defcustom semantic-highlight-func-mode-hook nil + "Hook run at the end of function `semantic-highlight-func-mode'." + :group 'semantic + :type 'hook) + +(defvar semantic-highlight-func-mode-map + (let ((km (make-sparse-keymap)) + (m3 (if (featurep 'xemacs) [ button3 ] [ mouse-3 ])) + ) + (define-key km m3 'semantic-highlight-func-menu) + km) + "Keymap for highlight-func minor mode.") + +(defvar semantic-highlight-func-popup-menu nil + "Menu used if the user clicks on the header line used by `semantic-highlight-func-mode'.") + +(easy-menu-define + semantic-highlight-func-popup-menu + semantic-highlight-func-mode-map + "Highlight-Func Menu" + '("Highlight-Func Mode" :visible (progn nil) + [ "Copy Tag" senator-copy-tag + :active (semantic-current-tag) + :help "Copy the current tag to the tag ring"] + [ "Kill Tag" senator-kill-tag + :active (semantic-current-tag) + :help "Kill tag text to the kill ring, and copy the tag to the tag ring" + ] + [ "Copy Tag to Register" senator-copy-tag-to-register + :active (semantic-current-tag) + :help "Copy the current tag to a register" + ] + [ "Narrow To Tag" senator-narrow-to-defun + :active (semantic-current-tag) + :help "Narrow to the bounds of the current tag."] + [ "Fold Tag" senator-fold-tag-toggle + :active (semantic-current-tag) + :style toggle + :selected (let ((tag (semantic-stickyfunc-tag-to-stick))) + (and tag (semantic-tag-folded-p tag))) + :help "Fold the current tag to one line" + ] + "---" + [ "About This Tag" semantic-describe-tag t]) + ) + +(defun semantic-highlight-func-menu (event) + "Popup a menu that displays things to do to the current tag. +Argument EVENT describes the event that caused this function to be called." + (interactive "e") + (let* ((startwin (selected-window)) + (win (semantic-event-window event)) + ) + (select-window win t) + (save-excursion + ;(goto-char (window-start win)) + (mouse-set-point event) + (sit-for 0) + (semantic-popup-menu semantic-highlight-func-popup-menu) + ) + (select-window startwin))) + +(defvar semantic-highlight-func-mode nil + "Non-nil if highlight-func minor mode is enabled. +Use the command `semantic-highlight-func-mode' to change this variable.") +(make-variable-buffer-local 'semantic-highlight-func-mode) + +(defvar semantic-highlight-func-ct-overlay nil + "Overlay used to highlight the tag the cursor is in.") +(make-variable-buffer-local 'semantic-highlight-func-ct-overlay) + +(defface semantic-highlight-func-current-tag-face + '((((class color) (background dark)) + ;; Put this back to something closer to black later. + (:background "gray20")) + (((class color) (background light)) + (:background "gray90"))) + "Face used to show the top of current function." + :group 'semantic-faces) + + +(defun semantic-highlight-func-mode-setup () + "Setup option `semantic-highlight-func-mode'. +For semantic enabled buffers, highlight the first line of the +current tag declaration." + (if semantic-highlight-func-mode + (progn + (unless (and (featurep 'semantic) (semantic-active-p)) + ;; Disable minor mode if semantic stuff not available + (setq semantic-highlight-func-mode nil) + (error "Buffer %s was not set up for parsing" (buffer-name))) + ;; Setup our hook + (add-hook 'post-command-hook 'semantic-highlight-func-highlight-current-tag nil t) + ) + ;; Disable highlight func mode + (remove-hook 'post-command-hook 'semantic-highlight-func-highlight-current-tag t) + (semantic-highlight-func-highlight-current-tag t) + ) + semantic-highlight-func-mode) + +;;;###autoload +(defun semantic-highlight-func-mode (&optional arg) + "Minor mode to highlight the first line of the current tag. +Enables/disables making the header line of functions sticky. +A function (or other tag class specified by +`semantic-stickyfunc-sticky-classes') is highlighted, meaning the +first line which describes the rest of the construct. + +See `semantic-stickyfunc-mode' for putting a function in the +header line. This mode recycles the stickyfunc configuration +classes list. + +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-highlight-func-mode 0 1)))) + (setq semantic-highlight-func-mode + (if arg + (> + (prefix-numeric-value arg) + 0) + (not semantic-highlight-func-mode))) + (semantic-highlight-func-mode-setup) + (run-hooks 'semantic-highlight-func-mode-hook) + (if (interactive-p) + (message "Highlight-Func minor mode %sabled" + (if semantic-highlight-func-mode "en" "dis"))) + semantic-highlight-func-mode) + +(defun semantic-highlight-func-highlight-current-tag (&optional disable) + "Highlight the current tag under point. +Optional argument DISABLE will turn off any active highlight. +If the current tag for this buffer is different from the last time this +function was called, move the overlay." + (when (and (not (minibufferp)) + (or (not semantic-highlight-func-ct-overlay) + (eq (semantic-overlay-buffer + semantic-highlight-func-ct-overlay) + (current-buffer)))) + (let* ((tag (semantic-stickyfunc-tag-to-stick)) + (ol semantic-highlight-func-ct-overlay)) + (when (not ol) + ;; No overlay in this buffer. Make one. + (setq ol (semantic-make-overlay (point-min) (point-min) + (current-buffer) t nil)) + (semantic-overlay-put ol 'highlight-func t) + (semantic-overlay-put ol 'face 'semantic-highlight-func-current-tag-face) + (semantic-overlay-put ol 'keymap semantic-highlight-func-mode-map) + (semantic-overlay-put ol 'help-echo + "Current Function : mouse-3 - Context menu") + (setq semantic-highlight-func-ct-overlay ol) + ) + + ;; TAG is nil if there was nothing of the apropriate type there. + (if (or (not tag) disable) + ;; No tag, make the overlay go away. + (progn + (semantic-overlay-put ol 'tag nil) + (semantic-overlay-move ol (point-min) (point-min) (current-buffer)) + ) + + ;; We have a tag, if it is the same, do nothing. + (unless (eq (semantic-overlay-get ol 'tag) tag) + (save-excursion + (goto-char (semantic-tag-start tag)) + (search-forward (semantic-tag-name tag) nil t) + (semantic-overlay-put ol 'tag tag) + (semantic-overlay-move ol (point-at-bol) (point-at-eol)) + ) + ) + ))) + nil) + +(semantic-add-minor-mode 'semantic-highlight-func-mode + "" ;; Don't need indicator. It's quite visible + nil) + +(provide 'semantic/util-modes) + +;; Local variables: +;; generated-autoload-file: "loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/util-modes" +;; End: + +;;; semantic/util-modes.el ends here diff --git a/lisp/cedet/semantic/util.el b/lisp/cedet/semantic/util.el new file mode 100644 index 00000000000..669bf68f432 --- /dev/null +++ b/lisp/cedet/semantic/util.el @@ -0,0 +1,508 @@ +;;; semantic/util.el --- Utilities for use with semantic tag tables + +;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, +;;; 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: +;; +;; Semantic utility API for use with semantic tag tables. +;; + +(require 'semantic) + +(eval-when-compile + (require 'semantic/db-find) + ;; For semantic-find-tags-by-class, semantic--find-tags-by-function, + ;; and semantic-brute-find-tag-standard: + (require 'semantic/find)) + +(declare-function data-debug-insert-stuff-list "data-debug") +(declare-function data-debug-insert-thing "data-debug") +(declare-function semantic-ctxt-current-symbol-and-bounds "semantic/ctxt") + +;;; Code: + +(defvar semantic-type-relation-separator-character '(".") + "Character strings used to separate a parent/child relationship. +This list of strings are used for displaying or finding separators +in variable field dereferencing. The first character will be used for +display. In C, a type field is separated like this: \"type.field\" +thus, the character is a \".\". In C, and additional value of \"->\" +would be in the list, so that \"type->field\" could be found.") +(make-variable-buffer-local 'semantic-type-relation-separator-character) + +(defvar semantic-equivalent-major-modes nil + "List of major modes which are considered equivalent. +Equivalent modes share a parser, and a set of override methods. +A value of nil means that the current major mode is the only one.") +(make-variable-buffer-local 'semantic-equivalent-major-modes) + +;; These semanticdb calls will throw warnings in the byte compiler. +;; Doing the right thing to make them available at compile time +;; really messes up the compilation sequence. +(defun semantic-file-tag-table (file) + "Return a tag table for FILE. +If it is loaded, return the stream after making sure it's ok. +If FILE is not loaded, check to see if `semanticdb' feature exists, + and use it to get tags from files not in memory. +If FILE is not loaded, and semanticdb is not available, find the file + and parse it." + (save-match-data + (if (find-buffer-visiting file) + (save-excursion + (set-buffer (find-buffer-visiting file)) + (semantic-fetch-tags)) + ;; File not loaded + (if (and (require 'semantic/db-mode) + (semanticdb-minor-mode-p)) + ;; semanticdb is around, use it. + (semanticdb-file-stream file) + ;; Get the stream ourselves. + (save-excursion + (set-buffer (find-file-noselect file)) + (semantic-fetch-tags)))))) + +(semantic-alias-obsolete 'semantic-file-token-stream + 'semantic-file-tag-table) + +(defun semantic-something-to-tag-table (something) + "Convert SOMETHING into a semantic tag table. +Something can be a tag with a valid BUFFER property, a tag table, a +buffer, or a filename. If SOMETHING is nil return nil." + (cond + ;; A list of tags + ((and (listp something) + (semantic-tag-p (car something))) + something) + ;; A buffer + ((bufferp something) + (save-excursion + (set-buffer something) + (semantic-fetch-tags))) + ;; A Tag: Get that tag's buffer + ((and (semantic-tag-with-position-p something) + (semantic-tag-in-buffer-p something)) + (save-excursion + (set-buffer (semantic-tag-buffer something)) + (semantic-fetch-tags))) + ;; Tag with a file name in it + ((and (semantic-tag-p something) + (semantic-tag-file-name something) + (file-exists-p (semantic-tag-file-name something))) + (semantic-file-tag-table + (semantic-tag-file-name something))) + ;; A file name + ((and (stringp something) + (file-exists-p something)) + (semantic-file-tag-table something)) + ;; A Semanticdb table + ((and (featurep 'semantic/db) + (semanticdb-minor-mode-p) + (semanticdb-abstract-table-child-p something)) + (semanticdb-refresh-table something) + (semanticdb-get-tags something)) + ;; Semanticdb find-results + ((and (featurep 'semantic/db) + (semanticdb-minor-mode-p) + (require 'semantic/db-find) + (semanticdb-find-results-p something)) + (semanticdb-strip-find-results something)) + ;; NOTE: This commented out since if a search result returns + ;; empty, that empty would turn into everything on the next search. + ;; Use the current buffer for nil +;; ((null something) +;; (semantic-fetch-tags)) + ;; don't know what it is + (t nil))) + +(semantic-alias-obsolete 'semantic-something-to-stream + 'semantic-something-to-tag-table) + +;;; Recursive searching through dependency trees +;; +;; This will depend on the general searching APIS defined above. +;; but will add full recursion through the dependencies list per +;; stream. +(defun semantic-recursive-find-nonterminal-by-name (name buffer) + "Recursively find the first occurrence of NAME. +Start search with BUFFER. Recurse through all dependencies till found. +The return item is of the form (BUFFER TOKEN) where BUFFER is the buffer +in which TOKEN (the token found to match NAME) was found. + +THIS ISN'T USED IN SEMANTIC. DELETE ME SOON." + (save-excursion + (set-buffer buffer) + (let* ((stream (semantic-fetch-tags)) + (includelist (or (semantic-find-tags-by-class 'include stream) + "empty.silly.thing")) + (found (semantic-find-first-tag-by-name name stream)) + (unfound nil)) + (while (and (not found) includelist) + (let ((fn (semantic-dependency-tag-file (car includelist)))) + (if (and fn (not (member fn unfound))) + (save-excursion + (save-match-data + (set-buffer (find-file-noselect fn))) + (message "Scanning %s" (buffer-file-name)) + (setq stream (semantic-fetch-tags)) + (setq found (semantic-find-first-tag-by-name name stream)) + (if found + (setq found (cons (current-buffer) (list found))) + (setq includelist + (append includelist + (semantic-find-tags-by-class + 'include stream)))) + (setq unfound (cons fn unfound))))) + (setq includelist (cdr includelist))) + found))) +(make-obsolete 'semantic-recursive-find-nonterminal-by-name + "Do not use this function.") + +;;; Completion APIs +;; +;; These functions provide minibuffer reading/completion for lists of +;; nonterminals. +(defvar semantic-read-symbol-history nil + "History for a symbol read.") + +(defun semantic-read-symbol (prompt &optional default stream filter) + "Read a symbol name from the user for the current buffer. +PROMPT is the prompt to use. +Optional arguments: +DEFAULT is the default choice. If no default is given, one is read +from under point. +STREAM is the list of tokens to complete from. +FILTER is provides a filter on the types of things to complete. +FILTER must be a function to call on each element." + (if (not default) (setq default (thing-at-point 'symbol))) + (if (not stream) (setq stream (semantic-fetch-tags))) + (setq stream + (if filter + (semantic--find-tags-by-function filter stream) + (semantic-brute-find-tag-standard stream))) + (if (and default (string-match ":" prompt)) + (setq prompt + (concat (substring prompt 0 (match-end 0)) + " (default: " default ") "))) + (completing-read prompt stream nil t "" + 'semantic-read-symbol-history + default)) + +(defun semantic-read-variable (prompt &optional default stream) + "Read a variable name from the user for the current buffer. +PROMPT is the prompt to use. +Optional arguments: +DEFAULT is the default choice. If no default is given, one is read +from under point. +STREAM is the list of tokens to complete from." + (semantic-read-symbol + prompt default + (or (semantic-find-tags-by-class + 'variable (or stream (current-buffer))) + (error "No local variables")))) + +(defun semantic-read-function (prompt &optional default stream) + "Read a function name from the user for the current buffer. +PROMPT is the prompt to use. +Optional arguments: +DEFAULT is the default choice. If no default is given, one is read +from under point. +STREAM is the list of tags to complete from." + (semantic-read-symbol + prompt default + (or (semantic-find-tags-by-class + 'function (or stream (current-buffer))) + (error "No local functions")))) + +(defun semantic-read-type (prompt &optional default stream) + "Read a type name from the user for the current buffer. +PROMPT is the prompt to use. +Optional arguments: +DEFAULT is the default choice. If no default is given, one is read +from under point. +STREAM is the list of tags to complete from." + (semantic-read-symbol + prompt default + (or (semantic-find-tags-by-class + 'type (or stream (current-buffer))) + (error "No local types")))) + + +;;; Interactive Functions for +;; +(defun semantic-describe-tag (&optional tag) + "Describe TAG in the minibuffer. +If TAG is nil, describe the tag under the cursor." + (interactive) + (if (not tag) (setq tag (semantic-current-tag))) + (semantic-fetch-tags) + (if tag (message (semantic-format-tag-summarize tag)))) + + +;;; Putting keys on tags. +;; +(defun semantic-add-label (label value &optional tag) + "Add a LABEL with VALUE on TAG. +If TAG is not specified, use the tag at point." + (interactive "sLabel: \nXValue (eval): ") + (if (not tag) + (progn + (semantic-fetch-tags) + (setq tag (semantic-current-tag)))) + (semantic--tag-put-property tag (intern label) value) + (message "Added label %s with value %S" label value)) + +(defun semantic-show-label (label &optional tag) + "Show the value of LABEL on TAG. +If TAG is not specified, use the tag at point." + (interactive "sLabel: ") + (if (not tag) + (progn + (semantic-fetch-tags) + (setq tag (semantic-current-tag)))) + (message "%s: %S" label (semantic--tag-get-property tag (intern label)))) + + +;;; Hacks +;; +;; Some hacks to help me test these functions +(defun semantic-describe-buffer-var-helper (varsym buffer) + "Display to standard out the value of VARSYM in BUFFER." + (require 'data-debug) + (let ((value (save-excursion + (set-buffer buffer) + (symbol-value varsym)))) + (cond + ((and (consp value) + (< (length value) 10)) + ;; Draw the list of things in the list. + (princ (format " %s: #<list of %d items>\n" + varsym (length value))) + (data-debug-insert-stuff-list + value " " ) + ) + (t + ;; Else do a one-liner. + (data-debug-insert-thing + value " " (concat " " (symbol-name varsym) ": ")) + )))) + +(defun semantic-describe-buffer () + "Describe the semantic environment for the current buffer." + (interactive) + (let ((buff (current-buffer)) + ) + + (with-output-to-temp-buffer (help-buffer) + (help-setup-xref (list #'semantic-describe-buffer) (interactive-p)) + (with-current-buffer standard-output + (princ "Semantic Configuration in ") + (princ (buffer-name buff)) + (princ "\n\n") + + (princ "Buffer specific configuration items:\n") + (let ((vars '(major-mode + semantic-case-fold + semantic-expand-nonterminal + semantic-parser-name + semantic-parse-tree-state + semantic-lex-analyzer + semantic-lex-reset-hooks + ))) + (dolist (V vars) + (semantic-describe-buffer-var-helper V buff))) + + (princ "\nGeneral configuration items:\n") + (let ((vars '(semantic-inhibit-functions + semantic-init-hook + semantic-init-db-hook + semantic-unmatched-syntax-hook + semantic--before-fetch-tags-hook + semantic-after-toplevel-bovinate-hook + semantic-after-toplevel-cache-change-hook + semantic-before-toplevel-cache-flush-hook + semantic-dump-parse + + ))) + (dolist (V vars) + (semantic-describe-buffer-var-helper V buff))) + + (princ "\n\n") + (mode-local-describe-bindings-2 buff) + ))) + ) + +(defun semantic-current-tag-interactive (p) + "Display the current token. +Argument P is the point to search from in the current buffer." + (interactive "d") + (require 'semantic/find) + (let ((tok (semantic-brute-find-innermost-tag-by-position + p (current-buffer)))) + (message (mapconcat 'semantic-abbreviate-nonterminal tok ",")) + (car tok)) + ) + +(defun semantic-hack-search () + "Display info about something under the cursor using generic methods." + (interactive) + (require 'semantic/find) + (let ((strm (cdr (semantic-fetch-tags))) + (res nil)) + (setq res (semantic-brute-find-tag-by-position (point) strm)) + (if res + (progn + (pop-to-buffer "*SEMANTIC HACK RESULTS*") + (require 'pp) + (erase-buffer) + (insert (pp-to-string res) "\n") + (goto-char (point-min)) + (shrink-window-if-larger-than-buffer)) + (message "nil")))) + +(defun semantic-assert-valid-token (tok) + "Assert that TOK is a valid token." + (if (semantic-tag-p tok) + (if (semantic-tag-with-position-p tok) + (let ((o (semantic-tag-overlay tok))) + (if (and (semantic-overlay-p o) + (not (semantic-overlay-live-p o))) + (let ((debug-on-error t)) + (error "Tag %s is invalid!" (semantic-tag-name tok))) + ;; else, tag is OK. + )) + ;; Positionless tags are also ok. + ) + (let ((debug-on-error t)) + (error "Not a semantic tag: %S" tok)))) + +(defun semantic-sanity-check (&optional cache over notfirst) + "Perform a sanity check on the current buffer. +The buffer's set of overlays, and those overlays found via the cache +are verified against each other. +CACHE, and OVER are the semantic cache, and the overlay list. +NOTFIRST indicates that this was not the first call in the recursive use." + (interactive) + (if (and (not cache) (not over) (not notfirst)) + (setq cache semantic--buffer-cache + over (semantic-overlays-in (point-min) (point-max)))) + (while cache + (let ((chil (semantic-tag-components-with-overlays (car cache)))) + (if (not (memq (semantic-tag-overlay (car cache)) over)) + (message "Tag %s not in buffer overlay list." + (semantic-format-tag-concise-prototype (car cache)))) + (setq over (delq (semantic-tag-overlay (car cache)) over)) + (setq over (semantic-sanity-check chil over t)) + (setq cache (cdr cache)))) + (if (not notfirst) + ;; Strip out all overlays which aren't semantic overlays + (let ((o nil)) + (while over + (when (and (semantic-overlay-get (car over) 'semantic) + (not (eq (semantic-overlay-get (car over) 'semantic) + 'unmatched))) + (setq o (cons (car over) o))) + (setq over (cdr over))) + (message "Remaining overlays: %S" o))) + over) + +;;; Interactive commands (from Senator). + +;; The Senator library from upstream CEDET is not included in the +;; built-in version of Emacs. The plan is to fold it into the +;; different parts of CEDET and Emacs, so that it works +;; "transparently". Here are some interactive commands based on +;; Senator. + +;; Symbol completion + +(defun semantic-find-tag-for-completion (prefix) + "Find all tags with name starting with PREFIX. +This uses `semanticdb' when available." + (let (result ctxt) + ;; Try the Semantic analyzer + (condition-case nil + (and (featurep 'semantic/analyze) + (setq ctxt (semantic-analyze-current-context)) + (setq result (semantic-analyze-possible-completions ctxt))) + (error nil)) + (or result + ;; If the analyzer fails, then go into boring completion. + (if (and (featurep 'semantic/db) + (semanticdb-minor-mode-p) + (require 'semantic/db-find)) + (semanticdb-fast-strip-find-results + (semanticdb-deep-find-tags-for-completion prefix)) + (semantic-deep-find-tags-for-completion prefix (current-buffer)))))) + +(defun semantic-complete-symbol (&optional predicate) + "Complete the symbol under point, using Semantic facilities. +When called from a program, optional arg PREDICATE is a predicate +determining which symbols are considered." + (interactive) + (require 'semantic/ctxt) + (let* ((start (car (nth 2 (semantic-ctxt-current-symbol-and-bounds + (point))))) + (pattern (regexp-quote (buffer-substring start (point)))) + collection completion) + (when start + (if (and semantic--completion-cache + (eq (nth 0 semantic--completion-cache) (current-buffer)) + (= (nth 1 semantic--completion-cache) start) + (save-excursion + (goto-char start) + (looking-at (nth 3 semantic--completion-cache)))) + ;; Use cached value. + (setq collection (nthcdr 4 semantic--completion-cache)) + ;; Perform new query. + (setq collection (semantic-find-tag-for-completion pattern)) + (setq semantic--completion-cache + (append (list (current-buffer) start 0 pattern) + collection)))) + (if (null collection) + (let ((str (if pattern (format " for \"%s\"" pattern) ""))) + (if (window-minibuffer-p (selected-window)) + (minibuffer-message (format " [No completions%s]" str)) + (message "Can't find completion%s" str))) + (setq completion (try-completion pattern collection predicate)) + (if (string= pattern completion) + (let ((list (all-completions pattern collection predicate))) + (setq list (sort list 'string<)) + (if (> (length list) 1) + (with-output-to-temp-buffer "*Completions*" + (display-completion-list list pattern)) + ;; Bury any out-of-date completions buffer. + (let ((win (get-buffer-window "*Completions*" 0))) + (if win (with-selected-window win (bury-buffer)))))) + ;; Exact match + (delete-region start (point)) + (insert completion) + ;; Bury any out-of-date completions buffer. + (let ((win (get-buffer-window "*Completions*" 0))) + (if win (with-selected-window win (bury-buffer)))))))) + +(provide 'semantic/util) + +;;; Minor modes +;; +(require 'semantic/util-modes) + +;;; semantic/util.el ends here diff --git a/lisp/cedet/semantic/wisent.el b/lisp/cedet/semantic/wisent.el new file mode 100644 index 00000000000..35671aa7ada --- /dev/null +++ b/lisp/cedet/semantic/wisent.el @@ -0,0 +1,346 @@ +;;; semantic/wisent.el --- Wisent - Semantic gateway + +;;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 +;;; Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> +;; Maintainer: David Ponce <david@dponce.com> +;; Created: 30 Aug 2001 +;; 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: +;; +;; Here are functions necessary to use the Wisent LALR parser from +;; Semantic environment. + +;;; History: +;; + +;;; Code: + +(require 'semantic) +(require 'semantic/wisent/wisent) + +;;; Lexical analysis +;; +(defvar wisent-lex-istream nil + "Input stream of `semantic-lex' syntactic tokens.") + +(defvar wisent-lex-lookahead nil + "Extra lookahead token. +When non-nil it is directly returned by `wisent-lex-function'.") + +;; Maintain this alias for compatibility until all WY grammars have +;; been translated again to Elisp code. +(semantic-alias-obsolete 'wisent-lex-make-token-table + 'semantic-lex-make-type-table) + +(defmacro wisent-lex-eoi () + "Return an End-Of-Input lexical token. +The EOI token is like this: ($EOI "" POINT-MAX . POINT-MAX)." + `(cons ',wisent-eoi-term + (cons "" + (cons (point-max) (point-max))))) + +(defmacro define-wisent-lexer (name doc &rest body) + "Create a new lexical analyzer with NAME. +DOC is a documentation string describing this analyzer. +When a token is available in `wisent-lex-istream', eval BODY forms +sequentially. BODY must return a lexical token for the LALR parser. + +Each token in input was produced by `semantic-lex', it is a list: + + (TOKSYM START . END) + +TOKSYM is a terminal symbol used in the grammar. +START and END mark boundary in the current buffer of that token's +value. + +Returned tokens must have the form: + + (TOKSYM VALUE START . END) + +where VALUE is the buffer substring between START and END positions." + `(defun + ,name () ,doc + (cond + (wisent-lex-lookahead + (prog1 wisent-lex-lookahead + (setq wisent-lex-lookahead nil))) + (wisent-lex-istream + ,@body) + ((wisent-lex-eoi))))) + +(define-wisent-lexer wisent-lex + "Return the next available lexical token in Wisent's form. +The variable `wisent-lex-istream' contains the list of lexical tokens +produced by `semantic-lex'. Pop the next token available and convert +it to a form suitable for the Wisent's parser." + (let* ((tk (car wisent-lex-istream))) + ;; Eat input stream + (setq wisent-lex-istream (cdr wisent-lex-istream)) + (cons (semantic-lex-token-class tk) + (cons (semantic-lex-token-text tk) + (semantic-lex-token-bounds tk))))) + +;;; Syntax analysis +;; +(defvar wisent-error-function nil + "Function used to report parse error. +By default use the function `wisent-message'.") +(make-variable-buffer-local 'wisent-error-function) + +(defvar wisent-lexer-function 'wisent-lex + "Function used to obtain the next lexical token in input. +Should be a lexical analyzer created with `define-wisent-lexer'.") +(make-variable-buffer-local 'wisent-lexer-function) + +;; Tag production +;; +(defsubst wisent-raw-tag (semantic-tag) + "Return raw form of given Semantic tag SEMANTIC-TAG. +Should be used in semantic actions, in grammars, to build a Semantic +parse tree." + (nconc semantic-tag + (if (or $region + (setq $region (nthcdr 2 wisent-input))) + (list (car $region) (cdr $region)) + (list (point-max) (point-max))))) + +(defsubst wisent-cook-tag (raw-tag) + "From raw form of Semantic tag RAW-TAG, return a list of cooked tags. +Should be used in semantic actions, in grammars, to build a Semantic +parse tree." + (let* ((cooked (semantic--tag-expand raw-tag)) + (l cooked)) + (while l + (semantic--tag-put-property (car l) 'reparse-symbol $nterm) + (setq l (cdr l))) + cooked)) + +;; Unmatched syntax collector +;; +(defun wisent-collect-unmatched-syntax (nomatch) + "Add lexical token NOMATCH to the cache of unmatched tokens. +See also the variable `semantic-unmatched-syntax-cache'. + +NOMATCH is in Wisent's form: (SYMBOL VALUE START . END) +and will be collected in `semantic-lex' form: (SYMBOL START . END)." + (let ((region (cddr nomatch))) + (and (number-or-marker-p (car region)) + (number-or-marker-p (cdr region)) + (setq semantic-unmatched-syntax-cache + (cons (cons (car nomatch) region) + semantic-unmatched-syntax-cache))))) + +;; Parser plug-ins +;; +;; The following functions permit to plug the Wisent LALR parser in +;; Semantic toolkit. They use the standard API provided by Semantic +;; to plug parsers in. +;; +;; Two plug-ins are available, BUT ONLY ONE MUST BE USED AT A TIME: +;; +;; - `wisent-parse-stream' designed to override the standard function +;; `semantic-parse-stream'. +;; +;; - `wisent-parse-region' designed to override the standard function +;; `semantic-parse-region'. +;; +;; Maybe the latter is faster because it eliminates a lot of function +;; call. +;; +(defun wisent-parse-stream (stream goal) + "Parse STREAM using the Wisent LALR parser. +GOAL is a nonterminal symbol to start parsing at. +Return the list (STREAM SEMANTIC-STREAM) where STREAM are those +elements of STREAM that have not been used. SEMANTIC-STREAM is the +list of semantic tags found. +The LALR parser automaton must be available in buffer local variable +`semantic--parse-table'. + +Must be installed by `semantic-install-function-overrides' to override +the standard function `semantic-parse-stream'." + (let (wisent-lex-istream wisent-lex-lookahead la-elt cache) + + ;; IMPLEMENTATION NOTES: + ;; `wisent-parse' returns a lookahead token when it stopped + ;; parsing before encountering the end of input. To re-enter the + ;; parser it is necessary to push back in the lexical input stream + ;; the last lookahead token issued. Because the format of + ;; lookahead tokens and tokens in STREAM can be different the + ;; lookahead token is put in the variable `wisent-lex-lookahead' + ;; before calling `wisent-parse'. Wisent's lexers always pop the + ;; next lexical token from that variable when non nil, then from + ;; the lexical input stream. + ;; + ;; The first element of STREAM is used to keep lookahead tokens + ;; across successive calls to `wisent-parse-stream'. In fact + ;; what is kept is a stack of lookaheads encountered so far. It + ;; is cleared when `wisent-parse' returns a valid semantic tag, + ;; or twice the same lookahead token! The latter indicates that + ;; there is a syntax error on that token. If so, tokens currently + ;; in the lookahead stack have not been used, and are moved into + ;; `semantic-unmatched-syntax-cache'. When the parser will be + ;; re-entered, a new lexical token will be read from STREAM. + ;; + ;; The first element of STREAM that contains the lookahead stack + ;; has this format (compatible with the format of `semantic-lex' + ;; tokens): + ;; + ;; (LOOKAHEAD-STACK START . END) + ;; + ;; where LOOKAHEAD-STACK is a list of lookahead tokens. And + ;; START/END are the bounds of the lookahead at top of stack. + + ;; Retrieve lookahead token from stack + (setq la-elt (car stream)) + (if (consp (car la-elt)) + ;; The first elt of STREAM contains a lookahead stack + (setq wisent-lex-lookahead (caar la-elt) + stream (cdr stream)) + (setq la-elt nil)) + ;; Parse + (setq wisent-lex-istream stream + cache (semantic-safe "wisent-parse-stream: %s" + (condition-case error-to-filter + (wisent-parse semantic--parse-table + wisent-lexer-function + wisent-error-function + goal) + (args-out-of-range + (if (and (not debug-on-error) + (= wisent-parse-max-stack-size + (nth 2 error-to-filter))) + (progn + (message "wisent-parse-stream: %s" + (error-message-string error-to-filter)) + (message "wisent-parse-max-stack-size \ +might need to be increased")) + (apply 'signal error-to-filter)))))) + ;; Manage returned lookahead token + (if wisent-lookahead + (if (eq (caar la-elt) wisent-lookahead) + ;; It is already at top of lookahead stack + (progn + (setq cache nil + la-elt (car la-elt)) + (while la-elt + ;; Collect unmatched tokens from the stack + (run-hook-with-args + 'wisent-discarding-token-functions (car la-elt)) + (setq la-elt (cdr la-elt)))) + ;; New lookahead token + (if (or (consp cache) ;; Clear the stack if parse succeeded + (null la-elt)) + (setq la-elt (cons nil nil))) + ;; Push it into the stack + (setcar la-elt (cons wisent-lookahead (car la-elt))) + ;; Update START/END + (setcdr la-elt (cddr wisent-lookahead)) + ;; Push (LOOKAHEAD-STACK START . END) in STREAM + (setq wisent-lex-istream (cons la-elt wisent-lex-istream)))) + ;; Return (STREAM SEMANTIC-STREAM) + (list wisent-lex-istream + (if (consp cache) cache '(nil)) + ))) + +(defun wisent-parse-region (start end &optional goal depth returnonerror) + "Parse the area between START and END using the Wisent LALR parser. +Return the list of semantic tags found. +Optional arguments GOAL is a nonterminal symbol to start parsing at, +DEPTH is the lexical depth to scan, and RETURNONERROR is a flag to +stop parsing on syntax error, when non-nil. +The LALR parser automaton must be available in buffer local variable +`semantic--parse-table'. + +Must be installed by `semantic-install-function-overrides' to override +the standard function `semantic-parse-region'." + (if (or (< start (point-min)) (> end (point-max)) (< end start)) + (error "Invalid bounds [%s %s] passed to `wisent-parse-region'" + start end)) + (let* ((case-fold-search semantic-case-fold) + (wisent-lex-istream (semantic-lex start end depth)) + ptree tag cooked lstack wisent-lex-lookahead) + ;; Loop while there are lexical tokens available + (while wisent-lex-istream + ;; Parse + (setq wisent-lex-lookahead (car lstack) + tag (semantic-safe "wisent-parse-region: %s" + (wisent-parse semantic--parse-table + wisent-lexer-function + wisent-error-function + goal))) + ;; Manage returned lookahead token + (if wisent-lookahead + (if (eq (car lstack) wisent-lookahead) + ;; It is already at top of lookahead stack + (progn + (setq tag nil) + (while lstack + ;; Collect unmatched tokens from lookahead stack + (run-hook-with-args + 'wisent-discarding-token-functions (car lstack)) + (setq lstack (cdr lstack)))) + ;; Push new lookahead token into the stack + (setq lstack (cons wisent-lookahead lstack)))) + ;; Manage the parser result + (cond + ;; Parse succeeded, cook result + ((consp tag) + (setq lstack nil ;; Clear the lookahead stack + cooked (semantic--tag-expand tag) + ptree (append cooked ptree)) + (while cooked + (setq tag (car cooked) + cooked (cdr cooked)) + (or (semantic--tag-get-property tag 'reparse-symbol) + (semantic--tag-put-property tag 'reparse-symbol goal))) + ) + ;; Return on error if requested + (returnonerror + (setq wisent-lex-istream nil) + )) + ;; Work in progress... + (if wisent-lex-istream + (and (eq semantic-working-type 'percent) + (boundp 'semantic--progress-reporter) + semantic--progress-reporter + (progress-reporter-update + semantic--progress-reporter + (/ (* 100 (semantic-lex-token-start + (car wisent-lex-istream))) + (point-max)))))) + ;; Return parse tree + (nreverse ptree))) + +;;; Interfacing with edebug +;; +(add-hook + 'edebug-setup-hook + #'(lambda () + + (def-edebug-spec define-wisent-lexer + (&define name stringp def-body) + ) + + )) + +(provide 'semantic/wisent) + +;;; semantic/wisent.el ends here diff --git a/lisp/cedet/semantic/wisent/comp.el b/lisp/cedet/semantic/wisent/comp.el new file mode 100644 index 00000000000..64426e51d98 --- /dev/null +++ b/lisp/cedet/semantic/wisent/comp.el @@ -0,0 +1,3539 @@ +;;; semantic/wisent/comp.el --- GNU Bison for Emacs - Grammar compiler + +;; Copyright (C) 1984, 1986, 1989, 1992, 1995, 2000, 2001, 2002, 2003, +;; 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> +;; Maintainer: David Ponce <david@dponce.com> +;; Created: 30 January 2002 +;; 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: +;; +;; Grammar compiler that produces Wisent's LALR automatons. +;; +;; Wisent (the European Bison ;-) is an Elisp implementation of the +;; GNU Compiler Compiler Bison. The Elisp code is a port of the C +;; code of GNU Bison 1.28 & 1.31. +;; +;; For more details on the basic concepts for understanding Wisent, +;; read the Bison manual ;) +;; +;; For more details on Wisent itself read the Wisent manual. + +;;; History: +;; + +;;; Code: +(require 'semantic/wisent) + +;;;; ------------------- +;;;; Misc. useful things +;;;; ------------------- + +;; As much as possible I would like to keep the name of global +;; variables used in Bison without polluting too much the Elisp global +;; name space. Elisp dynamic binding allows that ;-) + +;; Here are simple macros to easily define and use set of variables +;; binded locally, without all these "reference to free variable" +;; compiler warnings! + +(defmacro wisent-context-name (name) + "Return the context name from NAME." + `(if (and ,name (symbolp ,name)) + (intern (format "wisent-context-%s" ,name)) + (error "Invalid context name: %S" ,name))) + +(defmacro wisent-context-bindings (name) + "Return the variables in context NAME." + `(symbol-value (wisent-context-name ,name))) + +(defmacro wisent-defcontext (name &rest vars) + "Define a context NAME that will bind variables VARS." + (let* ((context (wisent-context-name name)) + (bindings (mapcar #'(lambda (v) (list 'defvar v)) vars))) + `(eval-when-compile + ,@bindings + (defvar ,context ',vars)))) +(put 'wisent-defcontext 'lisp-indent-function 1) + +(defmacro wisent-with-context (name &rest body) + "Bind variables in context NAME then eval BODY." + `(let* ,(wisent-context-bindings name) + ,@body)) +(put 'wisent-with-context 'lisp-indent-function 1) + +;; A naive implementation of data structures! But it suffice here ;-) + +(defmacro wisent-struct (name &rest fields) + "Define a simple data structure called NAME. +Which contains data stored in FIELDS. FIELDS is a list of symbols +which are field names or pairs (FIELD INITIAL-VALUE) where +INITIAL-VALUE is a constant used as the initial value of FIELD when +the data structure is created. INITIAL-VALUE defaults to nil. + +This defines a `make-NAME' constructor, get-able `NAME-FIELD' and +set-able `set-NAME-FIELD' accessors." + (let ((size (length fields)) + (i 0) + accors field sufx fun ivals) + (while (< i size) + (setq field (car fields) + fields (cdr fields)) + (if (consp field) + (setq ivals (cons (cadr field) ivals) + field (car field)) + (setq ivals (cons nil ivals))) + (setq sufx (format "%s-%s" name field) + fun (intern (format "%s" sufx)) + accors (cons `(defmacro ,fun (s) + (list 'aref s ,i)) + accors) + fun (intern (format "set-%s" sufx)) + accors (cons `(defmacro ,fun (s v) + (list 'aset s ,i v)) + accors) + i (1+ i))) + `(progn + (defmacro ,(intern (format "make-%s" name)) () + (cons 'vector ',(nreverse ivals))) + ,@accors))) +(put 'wisent-struct 'lisp-indent-function 1) + +;; Other utilities + +(defsubst wisent-pad-string (s n &optional left) + "Fill string S with spaces. +Return a new string of at least N characters. Insert spaces on right. +If optional LEFT is non-nil insert spaces on left." + (let ((i (length s))) + (if (< i n) + (if left + (concat (make-string (- n i) ?\ ) s) + (concat s (make-string (- n i) ?\ ))) + s))) + +;;;; ------------------------ +;;;; Environment dependencies +;;;; ------------------------ + +(defconst wisent-BITS-PER-WORD + (let ((i 1)) + (while (not (zerop (lsh 1 i))) + (setq i (1+ i))) + i)) + +(defsubst wisent-WORDSIZE (n) + "(N + BITS-PER-WORD - 1) / BITS-PER-WORD." + (/ (1- (+ n wisent-BITS-PER-WORD)) wisent-BITS-PER-WORD)) + +(defsubst wisent-SETBIT (x i) + "X[I/BITS-PER-WORD] |= 1 << (I % BITS-PER-WORD)." + (let ((k (/ i wisent-BITS-PER-WORD))) + (aset x k (logior (aref x k) + (lsh 1 (% i wisent-BITS-PER-WORD)))))) + +(defsubst wisent-RESETBIT (x i) + "X[I/BITS-PER-WORD] &= ~(1 << (I % BITS-PER-WORD))." + (let ((k (/ i wisent-BITS-PER-WORD))) + (aset x k (logand (aref x k) + (lognot (lsh 1 (% i wisent-BITS-PER-WORD))))))) + +(defsubst wisent-BITISSET (x i) + "(X[I/BITS-PER-WORD] & (1 << (I % BITS-PER-WORD))) != 0." + (not (zerop (logand (aref x (/ i wisent-BITS-PER-WORD)) + (lsh 1 (% i wisent-BITS-PER-WORD)))))) + +(eval-when-compile + (or (fboundp 'noninteractive) + ;; Silence the Emacs byte compiler + (defun noninteractive nil)) + ) + +(defsubst wisent-noninteractive () + "Return non-nil if running without interactive terminal." + (if (featurep 'xemacs) + (noninteractive) + noninteractive)) + +(defvar wisent-debug-flag nil + "Non-nil means enable some debug stuff.") + +;;;; -------------- +;;;; Logging/Output +;;;; -------------- +(defconst wisent-log-buffer-name "*wisent-log*" + "Name of the log buffer.") + +(defvar wisent-new-log-flag nil + "Non-nil means to start a new report.") + +(defvar wisent-verbose-flag nil + "*Non-nil means to report verbose information on generated parser.") + +(defun wisent-toggle-verbose-flag () + "Toggle whether to report verbose information on generated parser." + (interactive) + (setq wisent-verbose-flag (not wisent-verbose-flag)) + (when (interactive-p) + (message "Verbose report %sabled" + (if wisent-verbose-flag "en" "dis")))) + +(defmacro wisent-log-buffer () + "Return the log buffer. +Its name is defined in constant `wisent-log-buffer-name'." + `(get-buffer-create wisent-log-buffer-name)) + +(defmacro wisent-clear-log () + "Delete the entire contents of the log buffer." + `(with-current-buffer (wisent-log-buffer) + (erase-buffer))) + +(eval-when-compile (defvar byte-compile-current-file)) + +(defun wisent-source () + "Return the current source file name or nil." + (let ((source (or (and (boundp 'byte-compile-current-file) + byte-compile-current-file) + load-file-name (buffer-file-name)))) + (if source + (file-relative-name source)))) + +(defun wisent-new-log () + "Start a new entry into the log buffer." + (setq wisent-new-log-flag nil) + (let ((text (format "\n\n*** Wisent %s - %s\n\n" + (or (wisent-source) (buffer-name)) + (format-time-string "%Y-%m-%d %R")))) + (with-current-buffer (wisent-log-buffer) + (goto-char (point-max)) + (insert text)))) + +(defsubst wisent-log (&rest args) + "Insert text into the log buffer. +`format' is applied to ARGS and the result string is inserted into the +log buffer returned by the function `wisent-log-buffer'." + (and wisent-new-log-flag (wisent-new-log)) + (with-current-buffer (wisent-log-buffer) + (insert (apply 'format args)))) + +(defconst wisent-log-file "wisent.output" + "The log file. +Used when running without interactive terminal.") + +(defun wisent-append-to-log-file () + "Append contents of logging buffer to `wisent-log-file'." + (if (get-buffer wisent-log-buffer-name) + (condition-case err + (with-current-buffer (wisent-log-buffer) + (widen) + (if (> (point-max) (point-min)) + (write-region (point-min) (point-max) + wisent-log-file t))) + (error + (message "*** %s" (error-message-string err)))))) + +;;;; ----------------------------------- +;;;; Representation of the grammar rules +;;;; ----------------------------------- + +;; ntokens is the number of tokens, and nvars is the number of +;; variables (nonterminals). nsyms is the total number, ntokens + +;; nvars. + +;; Each symbol (either token or variable) receives a symbol number. +;; Numbers 0 to ntokens-1 are for tokens, and ntokens to nsyms-1 are +;; for variables. Symbol number zero is the end-of-input token. This +;; token is counted in ntokens. + +;; The rules receive rule numbers 1 to nrules in the order they are +;; written. Actions and guards are accessed via the rule number. + +;; The rules themselves are described by three arrays: rrhs, rlhs and +;; ritem. rlhs[R] is the symbol number of the left hand side of rule +;; R. The right hand side is stored as symbol numbers in a portion of +;; ritem. rrhs[R] contains the index in ritem of the beginning of the +;; portion for rule R. + +;; The length of the portion is one greater than the number of symbols +;; in the rule's right hand side. The last element in the portion +;; contains minus R, which identifies it as the end of a portion and +;; says which rule it is for. + +;; The portions of ritem come in order of increasing rule number and +;; are followed by an element which is nil to mark the end. nitems is +;; the total length of ritem, not counting the final nil. Each +;; element of ritem is called an "item" and its index in ritem is an +;; item number. + +;; Item numbers are used in the finite state machine to represent +;; places that parsing can get to. + +;; The vector rprec contains for each rule, the item number of the +;; symbol giving its precedence level to this rule. The precedence +;; level and associativity of each symbol is recorded in respectively +;; the properties 'wisent--prec and 'wisent--assoc. + +;; Precedence levels are assigned in increasing order starting with 1 +;; so that numerically higher precedence values mean tighter binding +;; as they ought to. nil as a symbol or rule's precedence means none +;; is assigned. + +(defcustom wisent-state-table-size 1009 + "The size of the state table." + :type 'integer + :group 'wisent) + +;; These variables only exist locally in the function +;; `wisent-compile-grammar' and are shared by all other nested +;; callees. +(wisent-defcontext compile-grammar + F LA LAruleno accessing-symbol conflicts consistent default-prec + derives err-table fderives final-state first-reduction first-shift + first-state firsts from-state goto-map includes itemset nitemset + kernel-base kernel-end kernel-items last-reduction last-shift + last-state lookaheads lookaheadset lookback maxrhs ngotos nitems + nrules nshifts nstates nsyms ntokens nullable nvars rassoc redset + reduction-table ritem rlhs rprec rrc-count rrc-total rrhs ruseful + rcode ruleset rulesetsize shift-symbol shift-table shiftset + src-count src-total start-table state-table tags this-state to-state + tokensetsize ;; nb of words req. to hold a bit for each rule + varsetsize ;; nb of words req. to hold a bit for each variable + error-token-number start-symbol token-list var-list + N P V V1 nuseless-nonterminals nuseless-productions + ptable ;; symbols & characters properties + ) + +(defmacro wisent-ISTOKEN (s) + "Return non-nil if item number S defines a token (terminal). +That is if S < `ntokens'." + `(< ,s ntokens)) + +(defmacro wisent-ISVAR(s) + "Return non-nil if item number S defines a nonterminal. +That is if S >= `ntokens'." + `(>= ,s ntokens)) + +(defsubst wisent-tag (s) + "Return printable form of item number S." + (wisent-item-to-string (aref tags s))) + +;; Symbol and character properties + +(defsubst wisent-put (object propname value) + "Store OBJECT's PROPNAME property with value VALUE. +Use `eq' to locate OBJECT." + (let ((entry (assq object ptable))) + (or entry (setq entry (list object) ptable (cons entry ptable))) + (setcdr entry (plist-put (cdr entry) propname value)))) + +(defsubst wisent-get (object propname) + "Return the value of OBJECT's PROPNAME property. +Use `eq' to locate OBJECT." + (plist-get (cdr (assq object ptable)) propname)) + +(defsubst wisent-item-number (x) + "Return the item number of symbol X." + (wisent-get x 'wisent--item-no)) + +(defsubst wisent-set-item-number (x n) + "Set the item number of symbol X to N." + (wisent-put x 'wisent--item-no n)) + +(defsubst wisent-assoc (x) + "Return the associativity of symbol X." + (wisent-get x 'wisent--assoc)) + +(defsubst wisent-set-assoc (x a) + "Set the associativity of symbol X to A." + (wisent-put x 'wisent--assoc a)) + +(defsubst wisent-prec (x) + "Return the precedence level of symbol X." + (wisent-get x 'wisent--prec)) + +(defsubst wisent-set-prec (x p) + "Set the precedence level of symbol X to P." + (wisent-put x 'wisent--prec p)) + +;;;; ---------------------------------------------------------- +;;;; Type definitions for nondeterministic finite state machine +;;;; ---------------------------------------------------------- + +;; These type definitions are used to represent a nondeterministic +;; finite state machine that parses the specified grammar. This +;; information is generated by the function `wisent-generate-states'. + +;; Each state of the machine is described by a set of items -- +;; particular positions in particular rules -- that are the possible +;; places where parsing could continue when the machine is in this +;; state. These symbols at these items are the allowable inputs that +;; can follow now. + +;; A core represents one state. States are numbered in the number +;; field. When `wisent-generate-states' is finished, the starting +;; state is state 0 and `nstates' is the number of states. (A +;; transition to a state whose state number is `nstates' indicates +;; termination.) All the cores are chained together and `first-state' +;; points to the first one (state 0). + +;; For each state there is a particular symbol which must have been +;; the last thing accepted to reach that state. It is the +;; accessing-symbol of the core. + +;; Each core contains a vector of `nitems' items which are the indices +;; in the `ritems' vector of the items that are selected in this +;; state. + +;; The link field is used for chaining buckets that hash states by +;; their itemsets. This is for recognizing equivalent states and +;; combining them when the states are generated. + +;; The two types of transitions are shifts (push the lookahead token +;; and read another) and reductions (combine the last n things on the +;; stack via a rule, replace them with the symbol that the rule +;; derives, and leave the lookahead token alone). When the states are +;; generated, these transitions are represented in two other lists. + +;; Each shifts structure describes the possible shift transitions out +;; of one state, the state whose number is in the number field. The +;; shifts structures are linked through next and first-shift points to +;; them. Each contains a vector of numbers of the states that shift +;; transitions can go to. The accessing-symbol fields of those +;; states' cores say what kind of input leads to them. + +;; A shift to state zero should be ignored. Conflict resolution +;; deletes shifts by changing them to zero. + +;; Each reductions structure describes the possible reductions at the +;; state whose number is in the number field. The data is a list of +;; nreds rules, represented by their rule numbers. `first-reduction' +;; points to the list of these structures. + +;; Conflict resolution can decide that certain tokens in certain +;; states should explicitly be errors (for implementing %nonassoc). +;; For each state, the tokens that are errors for this reason are +;; recorded in an errs structure, which has the state number in its +;; number field. The rest of the errs structure is full of token +;; numbers. + +;; There is at least one shift transition present in state zero. It +;; leads to a next-to-final state whose accessing-symbol is the +;; grammar's start symbol. The next-to-final state has one shift to +;; the final state, whose accessing-symbol is zero (end of input). +;; The final state has one shift, which goes to the termination state +;; (whose number is `nstates'-1). +;; The reason for the extra state at the end is to placate the +;; parser's strategy of making all decisions one token ahead of its +;; actions. + +(wisent-struct core + next ; -> core + link ; -> core + (number 0) + (accessing-symbol 0) + (nitems 0) + (items [0])) + +(wisent-struct shifts + next ; -> shifts + (number 0) + (nshifts 0) + (shifts [0])) + +(wisent-struct reductions + next ; -> reductions + (number 0) + (nreds 0) + (rules [0])) + +(wisent-struct errs + (nerrs 0) + (errs [0])) + +;;;; -------------------------------------------------------- +;;;; Find unreachable terminals, nonterminals and productions +;;;; -------------------------------------------------------- + +(defun wisent-bits-equal (L R n) + "Visit L and R and return non-nil if their first N elements are `='. +L and R must be vectors of integers." + (let* ((i (1- n)) + (iseq t)) + (while (and iseq (natnump i)) + (setq iseq (= (aref L i) (aref R i)) + i (1- i))) + iseq)) + +(defun wisent-nbits (i) + "Return number of bits set in integer I." + (let ((count 0)) + (while (not (zerop i)) + ;; i ^= (i & ((unsigned) (-(int) i))) + (setq i (logxor i (logand i (- i))) + count (1+ count))) + count)) + +(defun wisent-bits-size (S n) + "In vector S count the total of bits set in first N elements. +S must be a vector of integers." + (let* ((i (1- n)) + (count 0)) + (while (natnump i) + (setq count (+ count (wisent-nbits (aref S i))) + i (1- i))) + count)) + +(defun wisent-useful-production (i N0) + "Return non-nil if production I is in useful set N0." + (let* ((useful t) + (r (aref rrhs i)) + n) + (while (and useful (> (setq n (aref ritem r)) 0)) + (if (wisent-ISVAR n) + (setq useful (wisent-BITISSET N0 (- n ntokens)))) + (setq r (1+ r))) + useful)) + +(defun wisent-useless-nonterminals () + "Find out which nonterminals are used." + (let (Np Ns i n break) + ;; N is set as built. Np is set being built this iteration. P is + ;; set of all productions which have a RHS all in N. + (setq n (wisent-WORDSIZE nvars) + Np (make-vector n 0)) + + ;; The set being computed is a set of nonterminals which can + ;; derive the empty string or strings consisting of all + ;; terminals. At each iteration a nonterminal is added to the set + ;; if there is a production with that nonterminal as its LHS for + ;; which all the nonterminals in its RHS are already in the set. + ;; Iterate until the set being computed remains unchanged. Any + ;; nonterminals not in the set at that point are useless in that + ;; they will never be used in deriving a sentence of the language. + + ;; This iteration doesn't use any special traversal over the + ;; productions. A set is kept of all productions for which all + ;; the nonterminals in the RHS are in useful. Only productions + ;; not in this set are scanned on each iteration. At the end, + ;; this set is saved to be used when finding useful productions: + ;; only productions in this set will appear in the final grammar. + + (while (not break) + (setq i (1- n)) + (while (natnump i) + ;; Np[i] = N[i] + (aset Np i (aref N i)) + (setq i (1- i))) + + (setq i 1) + (while (<= i nrules) + (if (not (wisent-BITISSET P i)) + (when (wisent-useful-production i N) + (wisent-SETBIT Np (- (aref rlhs i) ntokens)) + (wisent-SETBIT P i))) + (setq i (1+ i))) + (if (wisent-bits-equal N Np n) + (setq break t) + (setq Ns Np + Np N + N Ns))) + (setq N Np))) + +(defun wisent-inaccessable-symbols () + "Find out which productions are reachable and which symbols are used." + ;; Starting with an empty set of productions and a set of symbols + ;; which only has the start symbol in it, iterate over all + ;; productions until the set of productions remains unchanged for an + ;; iteration. For each production which has a LHS in the set of + ;; reachable symbols, add the production to the set of reachable + ;; productions, and add all of the nonterminals in the RHS of the + ;; production to the set of reachable symbols. + + ;; Consider only the (partially) reduced grammar which has only + ;; nonterminals in N and productions in P. + + ;; The result is the set P of productions in the reduced grammar, + ;; and the set V of symbols in the reduced grammar. + + ;; Although this algorithm also computes the set of terminals which + ;; are reachable, no terminal will be deleted from the grammar. Some + ;; terminals might not be in the grammar but might be generated by + ;; semantic routines, and so the user might want them available with + ;; specified numbers. (Is this true?) However, the non reachable + ;; terminals are printed (if running in verbose mode) so that the + ;; user can know. + (let (Vp Vs Pp i tt r n m break) + (setq n (wisent-WORDSIZE nsyms) + m (wisent-WORDSIZE (1+ nrules)) + Vp (make-vector n 0) + Pp (make-vector m 0)) + + ;; If the start symbol isn't useful, then nothing will be useful. + (when (wisent-BITISSET N (- start-symbol ntokens)) + (wisent-SETBIT V start-symbol) + (while (not break) + (setq i (1- n)) + (while (natnump i) + (aset Vp i (aref V i)) + (setq i (1- i))) + (setq i 1) + (while (<= i nrules) + (when (and (not (wisent-BITISSET Pp i)) + (wisent-BITISSET P i) + (wisent-BITISSET V (aref rlhs i))) + (setq r (aref rrhs i)) + (while (natnump (setq tt (aref ritem r))) + (if (or (wisent-ISTOKEN tt) + (wisent-BITISSET N (- tt ntokens))) + (wisent-SETBIT Vp tt)) + (setq r (1+ r))) + (wisent-SETBIT Pp i)) + (setq i (1+ i))) + (if (wisent-bits-equal V Vp n) + (setq break t) + (setq Vs Vp + Vp V + V Vs)))) + (setq V Vp) + + ;; Tokens 0, 1 are internal to Wisent. Consider them useful. + (wisent-SETBIT V 0) ;; end-of-input token + (wisent-SETBIT V 1) ;; error token + (setq P Pp) + + (setq nuseless-productions (- nrules (wisent-bits-size P m)) + nuseless-nonterminals nvars + i ntokens) + (while (< i nsyms) + (if (wisent-BITISSET V i) + (setq nuseless-nonterminals (1- nuseless-nonterminals))) + (setq i (1+ i))) + + ;; A token that was used in %prec should not be warned about. + (setq i 1) + (while (<= i nrules) + (if (aref rprec i) + (wisent-SETBIT V1 (aref rprec i))) + (setq i (1+ i))) + )) + +(defun wisent-reduce-grammar-tables () + "Disable useless productions." + (if (> nuseless-productions 0) + (let ((pn 1)) + (while (<= pn nrules) + (aset ruseful pn (wisent-BITISSET P pn)) + (setq pn (1+ pn)))))) + +(defun wisent-nonterminals-reduce () + "Remove useless nonterminals." + (let (i n r item nontermmap tags-sorted) + ;; Map the nonterminals to their new index: useful first, useless + ;; afterwards. Kept for later report. + (setq nontermmap (make-vector nvars 0) + n ntokens + i ntokens) + (while (< i nsyms) + (when (wisent-BITISSET V i) + (aset nontermmap (- i ntokens) n) + (setq n (1+ n))) + (setq i (1+ i))) + (setq i ntokens) + (while (< i nsyms) + (unless (wisent-BITISSET V i) + (aset nontermmap (- i ntokens) n) + (setq n (1+ n))) + (setq i (1+ i))) + ;; Shuffle elements of tables indexed by symbol number + (setq tags-sorted (make-vector nvars nil) + i ntokens) + (while (< i nsyms) + (setq n (aref nontermmap (- i ntokens))) + (aset tags-sorted (- n ntokens) (aref tags i)) + (setq i (1+ i))) + (setq i ntokens) + (while (< i nsyms) + (aset tags i (aref tags-sorted (- i ntokens))) + (setq i (1+ i))) + ;; Replace all symbol numbers in valid data structures. + (setq i 1) + (while (<= i nrules) + (aset rlhs i (aref nontermmap (- (aref rlhs i) ntokens))) + (setq i (1+ i))) + (setq r 0) + (while (setq item (aref ritem r)) + (if (wisent-ISVAR item) + (aset ritem r (aref nontermmap (- item ntokens)))) + (setq r (1+ r))) + (setq start-symbol (aref nontermmap (- start-symbol ntokens)) + nsyms (- nsyms nuseless-nonterminals) + nvars (- nvars nuseless-nonterminals)) + )) + +(defun wisent-total-useless () + "Report number of useless nonterminals and productions." + (let* ((src (wisent-source)) + (src (if src (concat " in " src) "")) + (msg (format "Grammar%s contains" src))) + (if (> nuseless-nonterminals 0) + (setq msg (format "%s %d useless nonterminal%s" + msg nuseless-nonterminals + (if (> nuseless-nonterminals 0) "s" "")))) + (if (and (> nuseless-nonterminals 0) (> nuseless-productions 0)) + (setq msg (format "%s and" msg))) + (if (> nuseless-productions 0) + (setq msg (format "%s %d useless rule%s" + msg nuseless-productions + (if (> nuseless-productions 0) "s" "")))) + (message msg))) + +(defun wisent-reduce-grammar () + "Find unreachable terminals, nonterminals and productions." + ;; Allocate the global sets used to compute the reduced grammar + (setq N (make-vector (wisent-WORDSIZE nvars) 0) + P (make-vector (wisent-WORDSIZE (1+ nrules)) 0) + V (make-vector (wisent-WORDSIZE nsyms) 0) + V1 (make-vector (wisent-WORDSIZE nsyms) 0) + nuseless-nonterminals 0 + nuseless-productions 0) + + (wisent-useless-nonterminals) + (wisent-inaccessable-symbols) + + (when (> (+ nuseless-nonterminals nuseless-productions) 0) + (wisent-total-useless) + (or (wisent-BITISSET N (- start-symbol ntokens)) + (error "Start symbol `%s' does not derive any sentence" + (wisent-tag start-symbol))) + (wisent-reduce-grammar-tables) + (if (> nuseless-nonterminals 0) + (wisent-nonterminals-reduce)))) + +(defun wisent-print-useless () + "Output the detailed results of the reductions." + (let (i b r) + (when (> nuseless-nonterminals 0) + ;; Useless nonterminals have been moved after useful ones. + (wisent-log "\n\nUseless nonterminals:\n\n") + (setq i 0) + (while (< i nuseless-nonterminals) + (wisent-log " %s\n" (wisent-tag (+ nsyms i))) + (setq i (1+ i)))) + (setq b nil + i 0) + (while (< i ntokens) + (unless (or (wisent-BITISSET V i) (wisent-BITISSET V1 i)) + (or b + (wisent-log "\n\nTerminals which are not used:\n\n")) + (setq b t) + (wisent-log " %s\n" (wisent-tag i))) + (setq i (1+ i))) + (when (> nuseless-productions 0) + (wisent-log "\n\nUseless rules:\n\n") + (setq i 1) + (while (<= i nrules) + (unless (aref ruseful i) + (wisent-log "#%s " (wisent-pad-string (format "%d" i) 4)) + (wisent-log "%s:" (wisent-tag (aref rlhs i))) + (setq r (aref rrhs i)) + (while (natnump (aref ritem r)) + (wisent-log " %s" (wisent-tag (aref ritem r))) + (setq r (1+ r))) + (wisent-log ";\n")) + (setq i (1+ i)))) + (if (or b (> nuseless-nonterminals 0) (> nuseless-productions 0)) + (wisent-log "\n\n")) + )) + +;;;; ----------------------------- +;;;; Match rules with nonterminals +;;;; ----------------------------- + +(defun wisent-set-derives () + "Find, for each variable (nonterminal), which rules can derive it. +It sets up the value of DERIVES so that DERIVES[i - NTOKENS] points to +a list of rule numbers, terminated with -1." + (let (i lhs p q dset delts) + (setq dset (make-vector nvars nil) + delts (make-vector (1+ nrules) 0)) + (setq p 0 ;; p = delts + i nrules) + (while (> i 0) + (when (aref ruseful i) + (setq lhs (aref rlhs i)) + ;; p->next = dset[lhs]; + ;; p->value = i; + (aset delts p (cons i (aref dset (- lhs ntokens)))) ;; (value . next) + (aset dset (- lhs ntokens) p) ;; dset[lhs] = p + (setq p (1+ p)) ;; p++ + ) + (setq i (1- i))) + + (setq derives (make-vector nvars nil) + i ntokens) + + (while (< i nsyms) + (setq q nil + p (aref dset (- i ntokens))) ;; p = dset[i] + + (while p + (setq p (aref delts p) + q (cons (car p) q) ;;q++ = p->value + p (cdr p))) ;; p = p->next + (setq q (nreverse (cons -1 q))) ;; *q++ = -1 + (aset derives (- i ntokens) q) ;; derives[i] = q + (setq i (1+ i))) + )) + +;;;; -------------------------------------------------------- +;;;; Find which nonterminals can expand into the null string. +;;;; -------------------------------------------------------- + +(defun wisent-print-nullable () + "Print NULLABLE." + (let (i) + (wisent-log "NULLABLE\n") + (setq i ntokens) + (while (< i nsyms) + (wisent-log "\t%s: %s\n" (wisent-tag i) + (if (aref nullable (- i ntokens)) + "yes" : "no")) + (setq i (1+ i))) + (wisent-log "\n\n"))) + +(defun wisent-set-nullable () + "Set up NULLABLE. +A vector saying which nonterminals can expand into the null string. +NULLABLE[i - NTOKENS] is nil if symbol I can do so." + (let (ruleno s1 s2 p r squeue rcount rsets relts item any-tokens) + (setq squeue (make-vector nvars 0) + rcount (make-vector (1+ nrules) 0) + rsets (make-vector nvars nil) ;; - ntokens + relts (make-vector (+ nitems nvars 1) nil) + nullable (make-vector nvars nil)) ;; - ntokens + (setq s1 0 s2 0 ;; s1 = s2 = squeue + p 0 ;; p = relts + ruleno 1) + (while (<= ruleno nrules) + (when (aref ruseful ruleno) + (if (> (aref ritem (aref rrhs ruleno)) 0) + (progn + ;; This rule has a non empty RHS. + (setq any-tokens nil + r (aref rrhs ruleno)) + (while (> (aref ritem r) 0) + (if (wisent-ISTOKEN (aref ritem r)) + (setq any-tokens t)) + (setq r (1+ r))) + + ;; This rule has only nonterminals: schedule it for the + ;; second pass. + (unless any-tokens + (setq r (aref rrhs ruleno)) + (while (> (setq item (aref ritem r)) 0) + (aset rcount ruleno (1+ (aref rcount ruleno))) + ;; p->next = rsets[item]; + ;; p->value = ruleno; + (aset relts p (cons ruleno (aref rsets (- item ntokens)))) + ;; rsets[item] = p; + (aset rsets (- item ntokens) p) + (setq p (1+ p) + r (1+ r))))) + ;; This rule has an empty RHS. + ;; assert (ritem[rrhs[ruleno]] == -ruleno) + (when (and (aref ruseful ruleno) + (setq item (aref rlhs ruleno)) + (not (aref nullable (- item ntokens)))) + (aset nullable (- item ntokens) t) + (aset squeue s2 item) + (setq s2 (1+ s2))) + ) + ) + (setq ruleno (1+ ruleno))) + + (while (< s1 s2) + ;; p = rsets[*s1++] + (setq p (aref rsets (- (aref squeue s1) ntokens)) + s1 (1+ s1)) + (while p + (setq p (aref relts p) + ruleno (car p) + p (cdr p)) ;; p = p->next + ;; if (--rcount[ruleno] == 0) + (when (zerop (aset rcount ruleno (1- (aref rcount ruleno)))) + (setq item (aref rlhs ruleno)) + (aset nullable (- item ntokens) t) + (aset squeue s2 item) + (setq s2 (1+ s2))))) + + (if wisent-debug-flag + (wisent-print-nullable)) + )) + +;;;; ----------- +;;;; Subroutines +;;;; ----------- + +(defun wisent-print-fderives () + "Print FDERIVES." + (let (i j rp) + (wisent-log "\n\n\nFDERIVES\n") + (setq i ntokens) + (while (< i nsyms) + (wisent-log "\n\n%s derives\n\n" (wisent-tag i)) + (setq rp (aref fderives (- i ntokens)) + j 0) + (while (<= j nrules) + (if (wisent-BITISSET rp j) + (wisent-log " %d\n" j)) + (setq j (1+ j))) + (setq i (1+ i))))) + +(defun wisent-set-fderives () + "Set up FDERIVES. +An NVARS by NRULES matrix of bits indicating which rules can help +derive the beginning of the data for each nonterminal. For example, +if symbol 5 can be derived as the sequence of symbols 8 3 20, and one +of the rules for deriving symbol 8 is rule 4, then the +\[5 - NTOKENS, 4] bit in FDERIVES is set." + (let (i j k) + (setq fderives (make-vector nvars nil)) + (setq i 0) + (while (< i nvars) + (aset fderives i (make-vector rulesetsize 0)) + (setq i (1+ i))) + + (wisent-set-firsts) + + (setq i ntokens) + (while (< i nsyms) + (setq j ntokens) + (while (< j nsyms) + ;; if (BITISSET (FIRSTS (i), j - ntokens)) + (when (wisent-BITISSET (aref firsts (- i ntokens)) (- j ntokens)) + (setq k (aref derives (- j ntokens))) + (while (> (car k) 0) ;; derives[j][k] > 0 + ;; SETBIT (FDERIVES (i), derives[j][k]); + (wisent-SETBIT (aref fderives (- i ntokens)) (car k)) + (setq k (cdr k)))) + (setq j (1+ j))) + (setq i (1+ i))) + + (if wisent-debug-flag + (wisent-print-fderives)) + )) + +(defun wisent-print-firsts () + "Print FIRSTS." + (let (i j v) + (wisent-log "\n\n\nFIRSTS\n\n") + (setq i ntokens) + (while (< i nsyms) + (wisent-log "\n\n%s firsts\n\n" (wisent-tag i)) + (setq v (aref firsts (- i ntokens)) + j 0) + (while (< j nvars) + (if (wisent-BITISSET v j) + (wisent-log "\t\t%d (%s)\n" + (+ j ntokens) (wisent-tag (+ j ntokens)))) + (setq j (1+ j))) + (setq i (1+ i))))) + +(defun wisent-TC (R n) + "Transitive closure. +Given R an N by N matrix of bits, modify its contents to be the +transitive closure of what was given." + (let (i j k) + ;; R (J, I) && R (I, K) => R (J, K). + ;; I *must* be the outer loop. + (setq i 0) + (while (< i n) + (setq j 0) + (while (< j n) + (when (wisent-BITISSET (aref R j) i) + (setq k 0) + (while (< k n) + (if (wisent-BITISSET (aref R i) k) + (wisent-SETBIT (aref R j) k)) + (setq k (1+ k)))) + (setq j (1+ j))) + (setq i (1+ i))))) + +(defun wisent-RTC (R n) + "Reflexive Transitive Closure. +Same as `wisent-TC' and then set all the bits on the diagonal of R, an +N by N matrix of bits." + (let (i) + (wisent-TC R n) + (setq i 0) + (while (< i n) + (wisent-SETBIT (aref R i) i) + (setq i (1+ i))))) + +(defun wisent-set-firsts () + "Set up FIRSTS. +An NVARS by NVARS bit matrix indicating which items can represent the +beginning of the input corresponding to which other items. For +example, if some rule expands symbol 5 into the sequence of symbols 8 +3 20, the symbol 8 can be the beginning of the data for symbol 5, so +the bit [8 - NTOKENS, 5 - NTOKENS] in FIRSTS is set." + (let (row symbol sp rowsize i) + (setq rowsize (wisent-WORDSIZE nvars) + varsetsize rowsize + firsts (make-vector nvars nil) + i 0) + (while (< i nvars) + (aset firsts i (make-vector rowsize 0)) + (setq i (1+ i))) + + (setq row 0 ;; row = firsts + i ntokens) + (while (< i nsyms) + (setq sp (aref derives (- i ntokens))) + (while (>= (car sp) 0) + (setq symbol (aref ritem (aref rrhs (car sp))) + sp (cdr sp)) + (when (wisent-ISVAR symbol) + (setq symbol (- symbol ntokens)) + (wisent-SETBIT (aref firsts row) symbol) + )) + (setq row (1+ row) + i (1+ i))) + + (wisent-RTC firsts nvars) + + (if wisent-debug-flag + (wisent-print-firsts)) + )) + +(defun wisent-initialize-closure (n) + "Allocate the ITEMSET and RULESET vectors. +And precompute useful data so that `wisent-closure' can be called. +N is the number of elements to allocate for ITEMSET." + (setq itemset (make-vector n 0) + rulesetsize (wisent-WORDSIZE (1+ nrules)) + ruleset (make-vector rulesetsize 0)) + + (wisent-set-fderives)) + +(defun wisent-print-closure () + "Print ITEMSET." + (let (i) + (wisent-log "\n\nclosure n = %d\n\n" nitemset) + (setq i 0) ;; isp = itemset + (while (< i nitemset) + (wisent-log " %d\n" (aref itemset i)) + (setq i (1+ i))))) + +(defun wisent-closure (core n) + "Set up RULESET and ITEMSET for the transitions out of CORE state. +Given a vector of item numbers items, of length N, set up RULESET and +ITEMSET to indicate what rules could be run and which items could be +accepted when those items are the active ones. + +RULESET contains a bit for each rule. `wisent-closure' sets the bits +for all rules which could potentially describe the next input to be +read. + +ITEMSET is a vector of item numbers; NITEMSET is the number of items +in ITEMSET. `wisent-closure' places there the indices of all items +which represent units of input that could arrive next." + (let (c r v symbol ruleno itemno) + (if (zerop n) + (progn + (setq r 0 + v (aref fderives (- start-symbol ntokens))) + (while (< r rulesetsize) + ;; ruleset[r] = FDERIVES (start-symbol)[r]; + (aset ruleset r (aref v r)) + (setq r (1+ r))) + ) + (fillarray ruleset 0) + (setq c 0) + (while (< c n) + (setq symbol (aref ritem (aref core c))) + (when (wisent-ISVAR symbol) + (setq r 0 + v (aref fderives (- symbol ntokens))) + (while (< r rulesetsize) + ;; ruleset[r] |= FDERIVES (ritem[core[c]])[r]; + (aset ruleset r (logior (aref ruleset r) (aref v r))) + (setq r (1+ r)))) + (setq c (1+ c))) + ) + (setq nitemset 0 + c 0 + ruleno 0 + r (* rulesetsize wisent-BITS-PER-WORD)) + (while (< ruleno r) + (when (wisent-BITISSET ruleset ruleno) + (setq itemno (aref rrhs ruleno)) + (while (and (< c n) (< (aref core c) itemno)) + (aset itemset nitemset (aref core c)) + (setq nitemset (1+ nitemset) + c (1+ c))) + (aset itemset nitemset itemno) + (setq nitemset (1+ nitemset))) + (setq ruleno (1+ ruleno))) + + (while (< c n) + (aset itemset nitemset (aref core c)) + (setq nitemset (1+ nitemset) + c (1+ c))) + + (if wisent-debug-flag + (wisent-print-closure)) + )) + +;;;; -------------------------------------------------- +;;;; Generate the nondeterministic finite state machine +;;;; -------------------------------------------------- + +(defun wisent-allocate-itemsets () + "Allocate storage for itemsets." + (let (symbol i count symbol-count) + ;; Count the number of occurrences of all the symbols in RITEMS. + ;; Note that useless productions (hence useless nonterminals) are + ;; browsed too, hence we need to allocate room for _all_ the + ;; symbols. + (setq count 0 + symbol-count (make-vector (+ nsyms nuseless-nonterminals) 0) + i 0) + (while (setq symbol (aref ritem i)) + (when (> symbol 0) + (setq count (1+ count)) + (aset symbol-count symbol (1+ (aref symbol-count symbol)))) + (setq i (1+ i))) + ;; See comments before `wisent-new-itemsets'. All the vectors of + ;; items live inside kernel-items. The number of active items + ;; after some symbol cannot be more than the number of times that + ;; symbol appears as an item, which is symbol-count[symbol]. We + ;; allocate that much space for each symbol. + (setq kernel-base (make-vector nsyms nil) + kernel-items (make-vector count 0) + count 0 + i 0) + (while (< i nsyms) + (aset kernel-base i count) + (setq count (+ count (aref symbol-count i)) + i (1+ i))) + (setq shift-symbol symbol-count + kernel-end (make-vector nsyms nil)) + )) + +(defun wisent-allocate-storage () + "Allocate storage for the state machine." + (wisent-allocate-itemsets) + (setq shiftset (make-vector nsyms 0) + redset (make-vector (1+ nrules) 0) + state-table (make-vector wisent-state-table-size nil))) + +(defun wisent-new-itemsets () + "Find which symbols can be shifted in the current state. +And for each one record which items would be active after that shift. +Uses the contents of ITEMSET. SHIFT-SYMBOL is set to a vector of the +symbols that can be shifted. For each symbol in the grammar, +KERNEL-BASE[symbol] points to a vector of item numbers activated if +that symbol is shifted, and KERNEL-END[symbol] points after the end of +that vector." + (let (i shiftcount isp ksp symbol) + (fillarray kernel-end nil) + (setq shiftcount 0 + isp 0) + (while (< isp nitemset) + (setq i (aref itemset isp) + isp (1+ isp) + symbol (aref ritem i)) + (when (> symbol 0) + (setq ksp (aref kernel-end symbol)) + (when (not ksp) + ;; shift-symbol[shiftcount++] = symbol; + (aset shift-symbol shiftcount symbol) + (setq shiftcount (1+ shiftcount) + ksp (aref kernel-base symbol))) + ;; *ksp++ = i + 1; + (aset kernel-items ksp (1+ i)) + (setq ksp (1+ ksp)) + (aset kernel-end symbol ksp))) + (setq nshifts shiftcount))) + +(defun wisent-new-state (symbol) + "Create a new state for those items, if necessary. +SYMBOL is the core accessing-symbol. +Subroutine of `wisent-get-state'." + (let (n p isp1 isp2 iend items) + (setq isp1 (aref kernel-base symbol) + iend (aref kernel-end symbol) + n (- iend isp1) + p (make-core) + items (make-vector n 0)) + (set-core-accessing-symbol p symbol) + (set-core-number p nstates) + (set-core-nitems p n) + (set-core-items p items) + (setq isp2 0) ;; isp2 = p->items + (while (< isp1 iend) + ;; *isp2++ = *isp1++; + (aset items isp2 (aref kernel-items isp1)) + (setq isp1 (1+ isp1) + isp2 (1+ isp2))) + (set-core-next last-state p) + (setq last-state p + nstates (1+ nstates)) + p)) + +(defun wisent-get-state (symbol) + "Find the state we would get to by shifting SYMBOL. +Return the state number for the state we would get to (from the +current state) by shifting SYMBOL. Create a new state if no +equivalent one exists already. Used by `wisent-append-states'." + (let (key isp1 isp2 iend sp sp2 found n) + (setq isp1 (aref kernel-base symbol) + iend (aref kernel-end symbol) + n (- iend isp1) + key 0) + ;; Add up the target state's active item numbers to get a hash key + (while (< isp1 iend) + (setq key (+ key (aref kernel-items isp1)) + isp1 (1+ isp1))) + (setq key (% key wisent-state-table-size) + sp (aref state-table key)) + (if sp + (progn + (setq found nil) + (while (not found) + (when (= (core-nitems sp) n) + (setq found t + isp1 (aref kernel-base symbol) + ;; isp2 = sp->items; + sp2 (core-items sp) + isp2 0) + + (while (and found (< isp1 iend)) + ;; if (*isp1++ != *isp2++) + (if (not (= (aref kernel-items isp1) + (aref sp2 isp2))) + (setq found nil)) + (setq isp1 (1+ isp1) + isp2 (1+ isp2)))) + (if (not found) + (if (core-link sp) + (setq sp (core-link sp)) + ;; sp = sp->link = new-state(symbol) + (setq sp (set-core-link sp (wisent-new-state symbol)) + found t))))) + ;; bucket is empty + ;; state-table[key] = sp = new-state(symbol) + (setq sp (wisent-new-state symbol)) + (aset state-table key sp)) + ;; return (sp->number); + (core-number sp))) + +(defun wisent-append-states () + "Find or create the core structures for states. +Use the information computed by `wisent-new-itemsets' to find the +state numbers reached by each shift transition from the current state. +SHIFTSET is set up as a vector of state numbers of those states." + (let (i j symbol) + ;; First sort shift-symbol into increasing order + (setq i 1) + (while (< i nshifts) + (setq symbol (aref shift-symbol i) + j i) + (while (and (> j 0) (> (aref shift-symbol (1- j)) symbol)) + (aset shift-symbol j (aref shift-symbol (1- j))) + (setq j (1- j))) + (aset shift-symbol j symbol) + (setq i (1+ i))) + (setq i 0) + (while (< i nshifts) + (setq symbol (aref shift-symbol i)) + (aset shiftset i (wisent-get-state symbol)) + (setq i (1+ i))) + )) + +(defun wisent-initialize-states () + "Initialize states." + (let ((p (make-core))) + (setq first-state p + last-state p + this-state p + nstates 1))) + +(defun wisent-save-shifts () + "Save the NSHIFTS of SHIFTSET into the current linked list." + (let (p i shifts) + (setq p (make-shifts) + shifts (make-vector nshifts 0) + i 0) + (set-shifts-number p (core-number this-state)) + (set-shifts-nshifts p nshifts) + (set-shifts-shifts p shifts) + (while (< i nshifts) + ;; (p->shifts)[i] = shiftset[i]; + (aset shifts i (aref shiftset i)) + (setq i (1+ i))) + + (if last-shift + (set-shifts-next last-shift p) + (setq first-shift p)) + (setq last-shift p))) + +(defun wisent-insert-start-shift () + "Create the next-to-final state. +That is the state to which a shift has already been made in the +initial state. Subroutine of `wisent-augment-automaton'." + (let (statep sp) + (setq statep (make-core)) + (set-core-number statep nstates) + (set-core-accessing-symbol statep start-symbol) + (set-core-next last-state statep) + (setq last-state statep) + ;; Make a shift from this state to (what will be) the final state. + (setq sp (make-shifts)) + (set-shifts-number sp nstates) + (setq nstates (1+ nstates)) + (set-shifts-nshifts sp 1) + (set-shifts-shifts sp (vector nstates)) + (set-shifts-next last-shift sp) + (setq last-shift sp))) + +(defun wisent-augment-automaton () + "Set up initial and final states as parser wants them. +Make sure that the initial state has a shift that accepts the +grammar's start symbol and goes to the next-to-final state, which has +a shift going to the final state, which has a shift to the termination +state. Create such states and shifts if they don't happen to exist +already." + (let (i k statep sp sp2 sp1 shifts) + (setq sp first-shift) + (if sp + (progn + (if (zerop (shifts-number sp)) + (progn + (setq k (shifts-nshifts sp) + statep (core-next first-state)) + ;; The states reached by shifts from first-state are + ;; numbered 1...K. Look for one reached by + ;; START-SYMBOL. + (while (and (< (core-accessing-symbol statep) start-symbol) + (< (core-number statep) k)) + (setq statep (core-next statep))) + (if (= (core-accessing-symbol statep) start-symbol) + (progn + ;; We already have a next-to-final state. Make + ;; sure it has a shift to what will be the final + ;; state. + (setq k (core-number statep)) + (while (and sp (< (shifts-number sp) k)) + (setq sp1 sp + sp (shifts-next sp))) + (if (and sp (= (shifts-number sp) k)) + (progn + (setq i (shifts-nshifts sp) + sp2 (make-shifts) + shifts (make-vector (1+ i) 0)) + (set-shifts-number sp2 k) + (set-shifts-nshifts sp2 (1+ i)) + (set-shifts-shifts sp2 shifts) + (aset shifts 0 nstates) + (while (> i 0) + ;; sp2->shifts[i] = sp->shifts[i - 1]; + (aset shifts i (aref (shifts-shifts sp) (1- i))) + (setq i (1- i))) + ;; Patch sp2 into the chain of shifts in + ;; place of sp, following sp1. + (set-shifts-next sp2 (shifts-next sp)) + (set-shifts-next sp1 sp2) + (if (eq sp last-shift) + (setq last-shift sp2)) + ) + (setq sp2 (make-shifts)) + (set-shifts-number sp2 k) + (set-shifts-nshifts sp2 1) + (set-shifts-shifts sp2 (vector nstates)) + ;; Patch sp2 into the chain of shifts between + ;; sp1 and sp. + (set-shifts-next sp2 sp) + (set-shifts-next sp1 sp2) + (if (not sp) + (setq last-shift sp2)) + ) + ) + ;; There is no next-to-final state as yet. + ;; Add one more shift in FIRST-SHIFT, going to the + ;; next-to-final state (yet to be made). + (setq sp first-shift + sp2 (make-shifts) + i (shifts-nshifts sp) + shifts (make-vector (1+ i) 0)) + (set-shifts-nshifts sp2 (1+ i)) + (set-shifts-shifts sp2 shifts) + ;; Stick this shift into the vector at the proper place. + (setq statep (core-next first-state) + k 0 + i 0) + (while (< i (shifts-nshifts sp)) + (when (and (> (core-accessing-symbol statep) start-symbol) + (= i k)) + (aset shifts k nstates) + (setq k (1+ k))) + (aset shifts k (aref (shifts-shifts sp) i)) + (setq statep (core-next statep)) + (setq i (1+ i) + k (1+ k))) + (when (= i k) + (aset shifts k nstates) + (setq k (1+ k))) + ;; Patch sp2 into the chain of shifts in place of + ;; sp, at the beginning. + (set-shifts-next sp2 (shifts-next sp)) + (setq first-shift sp2) + (if (eq last-shift sp) + (setq last-shift sp2)) + ;; Create the next-to-final state, with shift to + ;; what will be the final state. + (wisent-insert-start-shift))) + ;; The initial state didn't even have any shifts. Give it + ;; one shift, to the next-to-final state. + (setq sp (make-shifts)) + (set-shifts-nshifts sp 1) + (set-shifts-shifts sp (vector nstates)) + ;; Patch sp into the chain of shifts at the beginning. + (set-shifts-next sp first-shift) + (setq first-shift sp) + ;; Create the next-to-final state, with shift to what will + ;; be the final state. + (wisent-insert-start-shift))) + ;; There are no shifts for any state. Make one shift, from the + ;; initial state to the next-to-final state. + (setq sp (make-shifts)) + (set-shifts-nshifts sp 1) + (set-shifts-shifts sp (vector nstates)) + ;; Initialize the chain of shifts with sp. + (setq first-shift sp + last-shift sp) + ;; Create the next-to-final state, with shift to what will be + ;; the final state. + (wisent-insert-start-shift)) + ;; Make the final state--the one that follows a shift from the + ;; next-to-final state. The symbol for that shift is 0 + ;; (end-of-file). + (setq statep (make-core)) + (set-core-number statep nstates) + (set-core-next last-state statep) + (setq last-state statep) + ;; Make the shift from the final state to the termination state. + (setq sp (make-shifts)) + (set-shifts-number sp nstates) + (setq nstates (1+ nstates)) + (set-shifts-nshifts sp 1) + (set-shifts-shifts sp (vector nstates)) + (set-shifts-next last-shift sp) + (setq last-shift sp) + ;; Note that the variable FINAL-STATE refers to what we sometimes + ;; call the termination state. + (setq final-state nstates) + ;; Make the termination state. + (setq statep (make-core)) + (set-core-number statep nstates) + (setq nstates (1+ nstates)) + (set-core-next last-state statep) + (setq last-state statep))) + +(defun wisent-save-reductions () + "Make a reductions structure. +Find which rules can be used for reduction transitions from the +current state and make a reductions structure for the state to record +their rule numbers." + (let (i item count p rules) + ;; Find and count the active items that represent ends of rules. + (setq count 0 + i 0) + (while (< i nitemset) + (setq item (aref ritem (aref itemset i))) + (when (< item 0) + (aset redset count (- item)) + (setq count (1+ count))) + (setq i (1+ i))) + ;; Make a reductions structure and copy the data into it. + (when (> count 0) + (setq p (make-reductions) + rules (make-vector count 0)) + (set-reductions-number p (core-number this-state)) + (set-reductions-nreds p count) + (set-reductions-rules p rules) + (setq i 0) + (while (< i count) + ;; (p->rules)[i] = redset[i] + (aset rules i (aref redset i)) + (setq i (1+ i))) + (if last-reduction + (set-reductions-next last-reduction p) + (setq first-reduction p)) + (setq last-reduction p)))) + +(defun wisent-generate-states () + "Compute the nondeterministic finite state machine from the grammar." + (wisent-allocate-storage) + (wisent-initialize-closure nitems) + (wisent-initialize-states) + (while this-state + ;; Set up RULESET and ITEMSET for the transitions out of this + ;; state. RULESET gets a 1 bit for each rule that could reduce + ;; now. ITEMSET gets a vector of all the items that could be + ;; accepted next. + (wisent-closure (core-items this-state) (core-nitems this-state)) + ;; Record the reductions allowed out of this state. + (wisent-save-reductions) + ;; Find the itemsets of the states that shifts can reach. + (wisent-new-itemsets) + ;; Find or create the core structures for those states. + (wisent-append-states) + ;; Create the shifts structures for the shifts to those states, + ;; now that the state numbers transitioning to are known. + (if (> nshifts 0) + (wisent-save-shifts)) + ;; States are queued when they are created; process them all. + (setq this-state (core-next this-state))) + ;; Set up initial and final states as parser wants them. + (wisent-augment-automaton)) + +;;;; --------------------------- +;;;; Compute look-ahead criteria +;;;; --------------------------- + +;; Compute how to make the finite state machine deterministic; find +;; which rules need lookahead in each state, and which lookahead +;; tokens they accept. + +;; `wisent-lalr', the entry point, builds these data structures: + +;; GOTO-MAP, FROM-STATE and TO-STATE record each shift transition +;; which accepts a variable (a nonterminal). NGOTOS is the number of +;; such transitions. +;; FROM-STATE[t] is the state number which a transition leads from and +;; TO-STATE[t] is the state number it leads to. +;; All the transitions that accept a particular variable are grouped +;; together and GOTO-MAP[i - NTOKENS] is the index in FROM-STATE and +;; TO-STATE of the first of them. + +;; CONSISTENT[s] is non-nil if no lookahead is needed to decide what +;; to do in state s. + +;; LARULENO is a vector which records the rules that need lookahead in +;; various states. The elements of LARULENO that apply to state s are +;; those from LOOKAHEADS[s] through LOOKAHEADS[s+1]-1. Each element +;; of LARULENO is a rule number. + +;; If LR is the length of LARULENO, then a number from 0 to LR-1 can +;; specify both a rule and a state where the rule might be applied. +;; LA is a LR by NTOKENS matrix of bits. +;; LA[l, i] is 1 if the rule LARULENO[l] is applicable in the +;; appropriate state when the next token is symbol i. +;; If LA[l, i] and LA[l, j] are both 1 for i != j, it is a conflict. + +(wisent-defcontext digraph + INDEX R VERTICES + infinity top) + +(defun wisent-traverse (i) + "Traverse I." + (let (j k height Ri Fi break) + (setq top (1+ top) + height top) + (aset VERTICES top i) ;; VERTICES[++top] = i + (aset INDEX i top) ;; INDEX[i] = height = top + + (setq Ri (aref R i)) + (when Ri + (setq j 0) + (while (>= (aref Ri j) 0) + (if (zerop (aref INDEX (aref Ri j))) + (wisent-traverse (aref Ri j))) + ;; if (INDEX[i] > INDEX[R[i][j]]) + (if (> (aref INDEX i) (aref INDEX (aref Ri j))) + ;; INDEX[i] = INDEX[R[i][j]]; + (aset INDEX i (aref INDEX (aref Ri j)))) + (setq Fi (aref F i) + k 0) + (while (< k tokensetsize) + ;; F (i)[k] |= F (R[i][j])[k]; + (aset Fi k (logior (aref Fi k) + (aref (aref F (aref Ri j)) k))) + (setq k (1+ k))) + (setq j (1+ j)))) + + (when (= (aref INDEX i) height) + (setq break nil) + (while (not break) + (setq j (aref VERTICES top) ;; j = VERTICES[top--] + top (1- top)) + (aset INDEX j infinity) + (if (= i j) + (setq break t) + (setq k 0) + (while (< k tokensetsize) + ;; F (j)[k] = F (i)[k]; + (aset (aref F j) k (aref (aref F i) k)) + (setq k (1+ k)))))) + )) + +(defun wisent-digraph (relation) + "Digraph RELATION." + (wisent-with-context digraph + (setq infinity (+ ngotos 2) + INDEX (make-vector (1+ ngotos) 0) + VERTICES (make-vector (1+ ngotos) 0) + top 0 + R relation) + (let ((i 0)) + (while (< i ngotos) + (if (and (= (aref INDEX i) 0) (aref R i)) + (wisent-traverse i)) + (setq i (1+ i)))))) + +(defun wisent-set-state-table () + "Build state table." + (let (sp) + (setq state-table (make-vector nstates nil) + sp first-state) + (while sp + (aset state-table (core-number sp) sp) + (setq sp (core-next sp))))) + +(defun wisent-set-accessing-symbol () + "Build accessing symbol table." + (let (sp) + (setq accessing-symbol (make-vector nstates 0) + sp first-state) + (while sp + (aset accessing-symbol (core-number sp) (core-accessing-symbol sp)) + (setq sp (core-next sp))))) + +(defun wisent-set-shift-table () + "Build shift table." + (let (sp) + (setq shift-table (make-vector nstates nil) + sp first-shift) + (while sp + (aset shift-table (shifts-number sp) sp) + (setq sp (shifts-next sp))))) + +(defun wisent-set-reduction-table () + "Build reduction table." + (let (rp) + (setq reduction-table (make-vector nstates nil) + rp first-reduction) + (while rp + (aset reduction-table (reductions-number rp) rp) + (setq rp (reductions-next rp))))) + +(defun wisent-set-maxrhs () + "Setup MAXRHS length." + (let (i len max) + (setq len 0 + max 0 + i 0) + (while (aref ritem i) + (if (> (aref ritem i) 0) + (setq len (1+ len)) + (if (> len max) + (setq max len)) + (setq len 0)) + (setq i (1+ i))) + (setq maxrhs max))) + +(defun wisent-initialize-LA () + "Set up LA." + (let (i j k count rp sp np v) + (setq consistent (make-vector nstates nil) + lookaheads (make-vector (1+ nstates) 0) + count 0 + i 0) + (while (< i nstates) + (aset lookaheads i count) + (setq rp (aref reduction-table i) + sp (aref shift-table i)) + ;; if (rp && + ;; (rp->nreds > 1 + ;; || (sp && ! ISVAR(accessing-symbol[sp->shifts[0]])))) + (if (and rp + (or (> (reductions-nreds rp) 1) + (and sp + (not (wisent-ISVAR + (aref accessing-symbol + (aref (shifts-shifts sp) 0))))))) + (setq count (+ count (reductions-nreds rp))) + (aset consistent i t)) + + (when sp + (setq k 0 + j (shifts-nshifts sp) + v (shifts-shifts sp)) + (while (< k j) + (when (= (aref accessing-symbol (aref v k)) + error-token-number) + (aset consistent i nil) + (setq k j)) ;; break + (setq k (1+ k)))) + (setq i (1+ i))) + + (aset lookaheads nstates count) + + (if (zerop count) + (progn + (setq LA (make-vector 1 nil) + LAruleno (make-vector 1 0) + lookback (make-vector 1 nil))) + (setq LA (make-vector count nil) + LAruleno (make-vector count 0) + lookback (make-vector count nil))) + (setq i 0 j (length LA)) + (while (< i j) + (aset LA i (make-vector tokensetsize 0)) + (setq i (1+ i))) + + (setq np 0 + i 0) + (while (< i nstates) + (when (not (aref consistent i)) + (setq rp (aref reduction-table i)) + (when rp + (setq j 0 + k (reductions-nreds rp) + v (reductions-rules rp)) + (while (< j k) + (aset LAruleno np (aref v j)) + (setq np (1+ np) + j (1+ j))))) + (setq i (1+ i))))) + +(defun wisent-set-goto-map () + "Set up GOTO-MAP." + (let (sp i j symbol k temp-map state1 state2 v) + (setq goto-map (make-vector (1+ nvars) 0) + temp-map (make-vector (1+ nvars) 0)) + + (setq ngotos 0 + sp first-shift) + (while sp + (setq i (1- (shifts-nshifts sp)) + v (shifts-shifts sp)) + (while (>= i 0) + (setq symbol (aref accessing-symbol (aref v i))) + (if (wisent-ISTOKEN symbol) + (setq i 0) ;; break + (setq ngotos (1+ ngotos)) + ;; goto-map[symbol]++; + (aset goto-map (- symbol ntokens) + (1+ (aref goto-map (- symbol ntokens))))) + (setq i (1- i))) + (setq sp (shifts-next sp))) + + (setq k 0 + i ntokens + j 0) + (while (< i nsyms) + (aset temp-map j k) + (setq k (+ k (aref goto-map j)) + i (1+ i) + j (1+ j))) + (setq i ntokens + j 0) + (while (< i nsyms) + (aset goto-map j (aref temp-map j)) + (setq i (1+ i) + j (1+ j))) + ;; goto-map[nsyms] = ngotos; + ;; temp-map[nsyms] = ngotos; + (aset goto-map j ngotos) + (aset temp-map j ngotos) + + (setq from-state (make-vector ngotos 0) + to-state (make-vector ngotos 0) + sp first-shift) + (while sp + (setq state1 (shifts-number sp) + v (shifts-shifts sp) + i (1- (shifts-nshifts sp))) + (while (>= i 0) + (setq state2 (aref v i) + symbol (aref accessing-symbol state2)) + (if (wisent-ISTOKEN symbol) + (setq i 0) ;; break + ;; k = temp-map[symbol]++; + (setq k (aref temp-map (- symbol ntokens))) + (aset temp-map (- symbol ntokens) (1+ k)) + (aset from-state k state1) + (aset to-state k state2)) + (setq i (1- i))) + (setq sp (shifts-next sp))) + )) + +(defun wisent-map-goto (state symbol) + "Map a STATE/SYMBOL pair into its numeric representation." + (let (high low middle s result) + ;; low = goto-map[symbol]; + ;; high = goto-map[symbol + 1] - 1; + (setq low (aref goto-map (- symbol ntokens)) + high (1- (aref goto-map (- (1+ symbol) ntokens)))) + (while (and (not result) (<= low high)) + (setq middle (/ (+ low high) 2) + s (aref from-state middle)) + (cond + ((= s state) + (setq result middle)) + ((< s state) + (setq low (1+ middle))) + (t + (setq high (1- middle))))) + (or result + (error "Internal error in `wisent-map-goto'")) + )) + +(defun wisent-initialize-F () + "Set up F." + (let (i j k sp edge rowp rp reads nedges stateno symbol v break) + (setq F (make-vector ngotos nil) + i 0) + (while (< i ngotos) + (aset F i (make-vector tokensetsize 0)) + (setq i (1+ i))) + + (setq reads (make-vector ngotos nil) + edge (make-vector (1+ ngotos) 0) + nedges 0 + rowp 0 ;; rowp = F + i 0) + (while (< i ngotos) + (setq stateno (aref to-state i) + sp (aref shift-table stateno)) + (when sp + (setq k (shifts-nshifts sp) + v (shifts-shifts sp) + j 0 + break nil) + (while (and (not break) (< j k)) + ;; symbol = accessing-symbol[sp->shifts[j]]; + (setq symbol (aref accessing-symbol (aref v j))) + (if (wisent-ISVAR symbol) + (setq break t) ;; break + (wisent-SETBIT (aref F rowp) symbol) + (setq j (1+ j)))) + + (while (< j k) + ;; symbol = accessing-symbol[sp->shifts[j]]; + (setq symbol (aref accessing-symbol (aref v j))) + (when (aref nullable (- symbol ntokens)) + (aset edge nedges (wisent-map-goto stateno symbol)) + (setq nedges (1+ nedges))) + (setq j (1+ j))) + + (when (> nedges 0) + ;; reads[i] = rp = NEW2(nedges + 1, short); + (setq rp (make-vector (1+ nedges) 0) + j 0) + (aset reads i rp) + (while (< j nedges) + ;; rp[j] = edge[j]; + (aset rp j (aref edge j)) + (setq j (1+ j))) + (aset rp nedges -1) + (setq nedges 0))) + (setq rowp (1+ rowp)) + (setq i (1+ i))) + (wisent-digraph reads) + )) + +(defun wisent-add-lookback-edge (stateno ruleno gotono) + "Add a lookback edge. +STATENO, RULENO, GOTONO are self-explanatory." + (let (i k found) + (setq i (aref lookaheads stateno) + k (aref lookaheads (1+ stateno)) + found nil) + (while (and (not found) (< i k)) + (if (= (aref LAruleno i) ruleno) + (setq found t) + (setq i (1+ i)))) + + (or found + (error "Internal error in `wisent-add-lookback-edge'")) + + ;; value . next + ;; lookback[i] = (gotono . lookback[i]) + (aset lookback i (cons gotono (aref lookback i))))) + +(defun wisent-transpose (R-arg n) + "Return the transpose of R-ARG, of size N. +Destroy R-ARG, as it is replaced with the result. R-ARG[I] is nil or +a -1 terminated list of numbers. RESULT[NUM] is nil or the -1 +terminated list of the I such as NUM is in R-ARG[I]." + (let (i j new-R end-R nedges v sp) + (setq new-R (make-vector n nil) + end-R (make-vector n nil) + nedges (make-vector n 0)) + + ;; Count. + (setq i 0) + (while (< i n) + (setq v (aref R-arg i)) + (when v + (setq j 0) + (while (>= (aref v j) 0) + (aset nedges (aref v j) (1+ (aref nedges (aref v j)))) + (setq j (1+ j)))) + (setq i (1+ i))) + + ;; Allocate. + (setq i 0) + (while (< i n) + (when (> (aref nedges i) 0) + (setq sp (make-vector (1+ (aref nedges i)) 0)) + (aset sp (aref nedges i) -1) + (aset new-R i sp) + (aset end-R i 0)) + (setq i (1+ i))) + + ;; Store. + (setq i 0) + (while (< i n) + (setq v (aref R-arg i)) + (when v + (setq j 0) + (while (>= (aref v j) 0) + (aset (aref new-R (aref v j)) (aref end-R (aref v j)) i) + (aset end-R (aref v j) (1+ (aref end-R (aref v j)))) + (setq j (1+ j)))) + (setq i (1+ i))) + + new-R)) + +(defun wisent-build-relations () + "Build relations." + (let (i j k rulep rp sp length nedges done state1 stateno + symbol1 symbol2 edge states v) + (setq includes (make-vector ngotos nil) + edge (make-vector (1+ ngotos) 0) + states (make-vector (1+ maxrhs) 0) + i 0) + + (while (< i ngotos) + (setq nedges 0 + state1 (aref from-state i) + symbol1 (aref accessing-symbol (aref to-state i)) + rulep (aref derives (- symbol1 ntokens))) + + (while (> (car rulep) 0) + (aset states 0 state1) + (setq length 1 + stateno state1 + rp (aref rrhs (car rulep))) ;; rp = ritem + rrhs[*rulep] + (while (> (aref ritem rp) 0) ;; *rp > 0 + (setq symbol2 (aref ritem rp) + sp (aref shift-table stateno) + k (shifts-nshifts sp) + v (shifts-shifts sp) + j 0) + (while (< j k) + (setq stateno (aref v j)) + (if (= (aref accessing-symbol stateno) symbol2) + (setq j k) ;; break + (setq j (1+ j)))) + ;; states[length++] = stateno; + (aset states length stateno) + (setq length (1+ length)) + (setq rp (1+ rp))) + + (if (not (aref consistent stateno)) + (wisent-add-lookback-edge stateno (car rulep) i)) + + (setq length (1- length) + done nil) + (while (not done) + (setq done t + rp (1- rp)) + (when (and (>= rp 0) (wisent-ISVAR (aref ritem rp))) + ;; stateno = states[--length]; + (setq length (1- length) + stateno (aref states length)) + (aset edge nedges (wisent-map-goto stateno (aref ritem rp))) + (setq nedges (1+ nedges)) + (if (aref nullable (- (aref ritem rp) ntokens)) + (setq done nil)))) + (setq rulep (cdr rulep))) + + (when (> nedges 0) + (setq v (make-vector (1+ nedges) 0) + j 0) + (aset includes i v) + (while (< j nedges) + (aset v j (aref edge j)) + (setq j (1+ j))) + (aset v nedges -1)) + (setq i (1+ i))) + + (setq includes (wisent-transpose includes ngotos)) + )) + +(defun wisent-compute-FOLLOWS () + "Compute follows." + (wisent-digraph includes)) + +(defun wisent-compute-lookaheads () + "Compute lookaheads." + (let (i j n v1 v2 sp) + (setq n (aref lookaheads nstates) + i 0) + (while (< i n) + (setq sp (aref lookback i)) + (while sp + (setq v1 (aref LA i) + v2 (aref F (car sp)) + j 0) + (while (< j tokensetsize) + ;; LA (i)[j] |= F (sp->value)[j] + (aset v1 j (logior (aref v1 j) (aref v2 j))) + (setq j (1+ j))) + (setq sp (cdr sp))) + (setq i (1+ i))))) + +(defun wisent-lalr () + "Make the nondeterministic finite state machine deterministic." + (setq tokensetsize (wisent-WORDSIZE ntokens)) + (wisent-set-state-table) + (wisent-set-accessing-symbol) + (wisent-set-shift-table) + (wisent-set-reduction-table) + (wisent-set-maxrhs) + (wisent-initialize-LA) + (wisent-set-goto-map) + (wisent-initialize-F) + (wisent-build-relations) + (wisent-compute-FOLLOWS) + (wisent-compute-lookaheads)) + +;;;; ----------------------------------------------- +;;;; Find and resolve or report look-ahead conflicts +;;;; ----------------------------------------------- + +(defsubst wisent-log-resolution (state LAno token resolution) + "Log a shift-reduce conflict resolution. +In specified STATE between rule pointed by lookahead number LANO and +TOKEN, resolved as RESOLUTION." + (if (or wisent-verbose-flag wisent-debug-flag) + (wisent-log + "Conflict in state %d between rule %d and token %s resolved as %s.\n" + state (aref LAruleno LAno) (wisent-tag token) resolution))) + +(defun wisent-flush-shift (state token) + "Turn off the shift recorded in the specified STATE for TOKEN. +Used when we resolve a shift-reduce conflict in favor of the reduction." + (let (shiftp i k v) + (when (setq shiftp (aref shift-table state)) + (setq k (shifts-nshifts shiftp) + v (shifts-shifts shiftp) + i 0) + (while (< i k) + (if (and (not (zerop (aref v i))) + (= token (aref accessing-symbol (aref v i)))) + (aset v i 0)) + (setq i (1+ i)))))) + +(defun wisent-resolve-sr-conflict (state lookaheadnum) + "Attempt to resolve shift-reduce conflict for one rule. +Resolve by means of precedence declarations. The conflict occurred in +specified STATE for the rule pointed by the lookahead symbol +LOOKAHEADNUM. It has already been checked that the rule has a +precedence. A conflict is resolved by modifying the shift or reduce +tables so that there is no longer a conflict." + (let (i redprec errp errs nerrs token sprec sassoc) + ;; Find the rule to reduce by to get precedence of reduction + (setq token (aref tags (aref rprec (aref LAruleno lookaheadnum))) + redprec (wisent-prec token) + errp (make-errs) + errs (make-vector ntokens 0) + nerrs 0 + i 0) + (set-errs-errs errp errs) + (while (< i ntokens) + (setq token (aref tags i)) + (when (and (wisent-BITISSET (aref LA lookaheadnum) i) + (wisent-BITISSET lookaheadset i) + (setq sprec (wisent-prec token))) + ;; Shift-reduce conflict occurs for token number I and it has + ;; a precedence. The precedence of shifting is that of token + ;; I. + (cond + ((< sprec redprec) + (wisent-log-resolution state lookaheadnum i "reduce") + ;; Flush the shift for this token + (wisent-RESETBIT lookaheadset i) + (wisent-flush-shift state i) + ) + ((> sprec redprec) + (wisent-log-resolution state lookaheadnum i "shift") + ;; Flush the reduce for this token + (wisent-RESETBIT (aref LA lookaheadnum) i) + ) + (t + ;; Matching precedence levels. + ;; For left association, keep only the reduction. + ;; For right association, keep only the shift. + ;; For nonassociation, keep neither. + (setq sassoc (wisent-assoc token)) + (cond + ((eq sassoc 'right) + (wisent-log-resolution state lookaheadnum i "shift")) + ((eq sassoc 'left) + (wisent-log-resolution state lookaheadnum i "reduce")) + ((eq sassoc 'nonassoc) + (wisent-log-resolution state lookaheadnum i "an error")) + ) + (when (not (eq sassoc 'right)) + ;; Flush the shift for this token + (wisent-RESETBIT lookaheadset i) + (wisent-flush-shift state i)) + (when (not (eq sassoc 'left)) + ;; Flush the reduce for this token + (wisent-RESETBIT (aref LA lookaheadnum) i)) + (when (eq sassoc 'nonassoc) + ;; Record an explicit error for this token + (aset errs nerrs i) + (setq nerrs (1+ nerrs))) + ))) + (setq i (1+ i))) + (when (> nerrs 0) + (set-errs-nerrs errp nerrs) + (aset err-table state errp)) + )) + +(defun wisent-set-conflicts (state) + "Find and attempt to resolve conflicts in specified STATE." + (let (i j k v shiftp symbol) + (unless (aref consistent state) + (fillarray lookaheadset 0) + + (when (setq shiftp (aref shift-table state)) + (setq k (shifts-nshifts shiftp) + v (shifts-shifts shiftp) + i 0) + (while (and (< i k) + (wisent-ISTOKEN + (setq symbol (aref accessing-symbol (aref v i))))) + (or (zerop (aref v i)) + (wisent-SETBIT lookaheadset symbol)) + (setq i (1+ i)))) + + ;; Loop over all rules which require lookahead in this state + ;; first check for shift-reduce conflict, and try to resolve + ;; using precedence + (setq i (aref lookaheads state) + k (aref lookaheads (1+ state))) + (while (< i k) + (when (aref rprec (aref LAruleno i)) + (setq v (aref LA i) + j 0) + (while (< j tokensetsize) + (if (zerop (logand (aref v j) (aref lookaheadset j))) + (setq j (1+ j)) + ;; if (LA (i)[j] & lookaheadset[j]) + (wisent-resolve-sr-conflict state i) + (setq j tokensetsize)))) ;; break + (setq i (1+ i))) + + ;; Loop over all rules which require lookahead in this state + ;; Check for conflicts not resolved above. + (setq i (aref lookaheads state)) + (while (< i k) + (setq v (aref LA i) + j 0) + (while (< j tokensetsize) + ;; if (LA (i)[j] & lookaheadset[j]) + (if (not (zerop (logand (aref v j) (aref lookaheadset j)))) + (aset conflicts state t)) + (setq j (1+ j))) + (setq j 0) + (while (< j tokensetsize) + ;; lookaheadset[j] |= LA (i)[j]; + (aset lookaheadset j (logior (aref lookaheadset j) + (aref v j))) + (setq j (1+ j))) + (setq i (1+ i))) + ))) + +(defun wisent-resolve-conflicts () + "Find and resolve conflicts." + (let (i) + (setq conflicts (make-vector nstates nil) + shiftset (make-vector tokensetsize 0) + lookaheadset (make-vector tokensetsize 0) + err-table (make-vector nstates nil) + i 0) + (while (< i nstates) + (wisent-set-conflicts i) + (setq i (1+ i))))) + +(defun wisent-count-sr-conflicts (state) + "Count the number of shift/reduce conflicts in specified STATE." + (let (i j k shiftp symbol v) + (setq src-count 0 + shiftp (aref shift-table state)) + (when shiftp + (fillarray shiftset 0) + (fillarray lookaheadset 0) + (setq k (shifts-nshifts shiftp) + v (shifts-shifts shiftp) + i 0) + (while (< i k) + (when (not (zerop (aref v i))) + (setq symbol (aref accessing-symbol (aref v i))) + (if (wisent-ISVAR symbol) + (setq i k) ;; break + (wisent-SETBIT shiftset symbol))) + (setq i (1+ i))) + + (setq k (aref lookaheads (1+ state)) + i (aref lookaheads state)) + (while (< i k) + (setq v (aref LA i) + j 0) + (while (< j tokensetsize) + ;; lookaheadset[j] |= LA (i)[j] + (aset lookaheadset j (logior (aref lookaheadset j) + (aref v j))) + (setq j (1+ j))) + (setq i (1+ i))) + + (setq k 0) + (while (< k tokensetsize) + ;; lookaheadset[k] &= shiftset[k]; + (aset lookaheadset k (logand (aref lookaheadset k) + (aref shiftset k))) + (setq k (1+ k))) + + (setq i 0) + (while (< i ntokens) + (if (wisent-BITISSET lookaheadset i) + (setq src-count (1+ src-count))) + (setq i (1+ i)))) + src-count)) + +(defun wisent-count-rr-conflicts (state) + "Count the number of reduce/reduce conflicts in specified STATE." + (let (i j count n m) + (setq rrc-count 0 + m (aref lookaheads state) + n (aref lookaheads (1+ state))) + (when (>= (- n m) 2) + (setq i 0) + (while (< i ntokens) + (setq count 0 + j m) + (while (< j n) + (if (wisent-BITISSET (aref LA j) i) + (setq count (1+ count))) + (setq j (1+ j))) + + (if (>= count 2) + (setq rrc-count (1+ rrc-count))) + (setq i (1+ i)))) + rrc-count)) + +(defvar wisent-expected-conflicts nil + "*If non-nil suppress the warning about shift/reduce conflicts. +It is a decimal integer N that says there should be no warning if +there are N shift/reduce conflicts and no reduce/reduce conflicts. A +warning is given if there are either more or fewer conflicts, or if +there are any reduce/reduce conflicts.") + +(defun wisent-total-conflicts () + "Report the total number of conflicts." + (unless (and (zerop rrc-total) + (or (zerop src-total) + (= src-total (or wisent-expected-conflicts 0)))) + (let* ((src (wisent-source)) + (src (if src (concat " in " src) "")) + (msg (format "Grammar%s contains" src))) + (if (> src-total 0) + (setq msg (format "%s %d shift/reduce conflict%s" + msg src-total (if (> src-total 1) + "s" "")))) + (if (and (> src-total 0) (> rrc-total 0)) + (setq msg (format "%s and" msg))) + (if (> rrc-total 0) + (setq msg (format "%s %d reduce/reduce conflict%s" + msg rrc-total (if (> rrc-total 1) + "s" "")))) + (message msg)))) + +(defun wisent-print-conflicts () + "Report conflicts." + (let (i) + (setq src-total 0 + rrc-total 0 + i 0) + (while (< i nstates) + (when (aref conflicts i) + (wisent-count-sr-conflicts i) + (wisent-count-rr-conflicts i) + (setq src-total (+ src-total src-count) + rrc-total (+ rrc-total rrc-count)) + (when (or wisent-verbose-flag wisent-debug-flag) + (wisent-log "State %d contains" i) + (if (> src-count 0) + (wisent-log " %d shift/reduce conflict%s" + src-count (if (> src-count 1) "s" ""))) + + (if (and (> src-count 0) (> rrc-count 0)) + (wisent-log " and")) + + (if (> rrc-count 0) + (wisent-log " %d reduce/reduce conflict%s" + rrc-count (if (> rrc-count 1) "s" ""))) + + (wisent-log ".\n"))) + (setq i (1+ i))) + (wisent-total-conflicts))) + +;;;; -------------------------------------- +;;;; Report information on generated parser +;;;; -------------------------------------- +(defun wisent-print-grammar () + "Print grammar." + (let (i j r break left-count right-count) + + (wisent-log "\n\nGrammar\n\n Number, Rule\n") + (setq i 1) + (while (<= i nrules) + ;; Don't print rules disabled in `wisent-reduce-grammar-tables'. + (when (aref ruseful i) + (wisent-log " %s %s ->" + (wisent-pad-string (number-to-string i) 6) + (wisent-tag (aref rlhs i))) + (setq r (aref rrhs i)) + (if (> (aref ritem r) 0) + (while (> (aref ritem r) 0) + (wisent-log " %s" (wisent-tag (aref ritem r))) + (setq r (1+ r))) + (wisent-log " /* empty */")) + (wisent-log "\n")) + (setq i (1+ i))) + + (wisent-log "\n\nTerminals, with rules where they appear\n\n") + (wisent-log "%s (-1)\n" (wisent-tag 0)) + (setq i 1) + (while (< i ntokens) + (wisent-log "%s (%d)" (wisent-tag i) i) + (setq j 1) + (while (<= j nrules) + (setq r (aref rrhs j) + break nil) + (while (and (not break) (> (aref ritem r) 0)) + (if (setq break (= (aref ritem r) i)) + (wisent-log " %d" j) + (setq r (1+ r)))) + (setq j (1+ j))) + (wisent-log "\n") + (setq i (1+ i))) + + (wisent-log "\n\nNonterminals, with rules where they appear\n\n") + (setq i ntokens) + (while (< i nsyms) + (setq left-count 0 + right-count 0 + j 1) + (while (<= j nrules) + (if (= (aref rlhs j) i) + (setq left-count (1+ left-count))) + (setq r (aref rrhs j) + break nil) + (while (and (not break) (> (aref ritem r) 0)) + (if (= (aref ritem r) i) + (setq right-count (1+ right-count) + break t) + (setq r (1+ r)))) + (setq j (1+ j))) + (wisent-log "%s (%d)\n " (wisent-tag i) i) + (when (> left-count 0) + (wisent-log " on left:") + (setq j 1) + (while (<= j nrules) + (if (= (aref rlhs j) i) + (wisent-log " %d" j)) + (setq j (1+ j)))) + (when (> right-count 0) + (if (> left-count 0) + (wisent-log ",")) + (wisent-log " on right:") + (setq j 1) + (while (<= j nrules) + (setq r (aref rrhs j) + break nil) + (while (and (not break) (> (aref ritem r) 0)) + (if (setq break (= (aref ritem r) i)) + (wisent-log " %d" j) + (setq r (1+ r)))) + (setq j (1+ j)))) + (wisent-log "\n") + (setq i (1+ i))) + )) + +(defun wisent-print-reductions (state) + "Print reductions on STATE." + (let (i j k v symbol m n defaulted + default-LA default-rule cmax count shiftp errp nodefault) + (setq nodefault nil + i 0) + (fillarray shiftset 0) + + (setq shiftp (aref shift-table state)) + (when shiftp + (setq k (shifts-nshifts shiftp) + v (shifts-shifts shiftp) + i 0) + (while (< i k) + (when (not (zerop (aref v i))) + (setq symbol (aref accessing-symbol (aref v i))) + (if (wisent-ISVAR symbol) + (setq i k) ;; break + ;; If this state has a shift for the error token, don't + ;; use a default rule. + (if (= symbol error-token-number) + (setq nodefault t)) + (wisent-SETBIT shiftset symbol))) + (setq i (1+ i)))) + + (setq errp (aref err-table state)) + (when errp + (setq k (errs-nerrs errp) + v (errs-errs errp) + i 0) + (while (< i k) + (if (not (zerop (setq symbol (aref v i)))) + (wisent-SETBIT shiftset symbol)) + (setq i (1+ i)))) + + (setq m (aref lookaheads state) + n (aref lookaheads (1+ state))) + + (cond + ((and (= (- n m) 1) (not nodefault)) + (setq default-rule (aref LAruleno m) + v (aref LA m) + k 0) + (while (< k tokensetsize) + (aset lookaheadset k (logand (aref v k) + (aref shiftset k))) + (setq k (1+ k))) + + (setq i 0) + (while (< i ntokens) + (if (wisent-BITISSET lookaheadset i) + (wisent-log " %s\t[reduce using rule %d (%s)]\n" + (wisent-tag i) default-rule + (wisent-tag (aref rlhs default-rule)))) + (setq i (1+ i))) + (wisent-log " $default\treduce using rule %d (%s)\n\n" + default-rule + (wisent-tag (aref rlhs default-rule))) + ) + ((>= (- n m) 1) + (setq cmax 0 + default-LA -1 + default-rule 0) + (when (not nodefault) + (setq i m) + (while (< i n) + (setq v (aref LA i) + count 0 + k 0) + (while (< k tokensetsize) + ;; lookaheadset[k] = LA (i)[k] & ~shiftset[k] + (aset lookaheadset k + (logand (aref v k) + (lognot (aref shiftset k)))) + (setq k (1+ k))) + (setq j 0) + (while (< j ntokens) + (if (wisent-BITISSET lookaheadset j) + (setq count (1+ count))) + (setq j (1+ j))) + (if (> count cmax) + (setq cmax count + default-LA i + default-rule (aref LAruleno i))) + (setq k 0) + (while (< k tokensetsize) + (aset shiftset k (logior (aref shiftset k) + (aref lookaheadset k))) + (setq k (1+ k))) + (setq i (1+ i)))) + + (fillarray shiftset 0) + + (when shiftp + (setq k (shifts-nshifts shiftp) + v (shifts-shifts shiftp) + i 0) + (while (< i k) + (when (not (zerop (aref v i))) + (setq symbol (aref accessing-symbol (aref v i))) + (if (wisent-ISVAR symbol) + (setq i k) ;; break + (wisent-SETBIT shiftset symbol))) + (setq i (1+ i)))) + + (setq i 0) + (while (< i ntokens) + (setq defaulted nil + count (if (wisent-BITISSET shiftset i) 1 0) + j m) + (while (< j n) + (when (wisent-BITISSET (aref LA j) i) + (if (zerop count) + (progn + (if (not (= j default-LA)) + (wisent-log + " %s\treduce using rule %d (%s)\n" + (wisent-tag i) (aref LAruleno j) + (wisent-tag (aref rlhs (aref LAruleno j)))) + (setq defaulted t)) + (setq count (1+ count))) + (if defaulted + (wisent-log + " %s\treduce using rule %d (%s)\n" + (wisent-tag i) (aref LAruleno default-LA) + (wisent-tag (aref rlhs (aref LAruleno default-LA))))) + (setq defaulted nil) + (wisent-log + " %s\t[reduce using rule %d (%s)]\n" + (wisent-tag i) (aref LAruleno j) + (wisent-tag (aref rlhs (aref LAruleno j)))))) + (setq j (1+ j))) + (setq i (1+ i))) + + (if (>= default-LA 0) + (wisent-log + " $default\treduce using rule %d (%s)\n" + default-rule + (wisent-tag (aref rlhs default-rule)))) + )))) + +(defun wisent-print-actions (state) + "Print actions on STATE." + (let (i j k v state1 symbol shiftp errp redp rule nerrs break) + (setq shiftp (aref shift-table state) + redp (aref reduction-table state) + errp (aref err-table state)) + (if (and (not shiftp) (not redp)) + (if (= final-state state) + (wisent-log " $default\taccept\n") + (wisent-log " NO ACTIONS\n")) + (if (not shiftp) + (setq i 0 + k 0) + (setq k (shifts-nshifts shiftp) + v (shifts-shifts shiftp) + i 0 + break nil) + (while (and (not break) (< i k)) + (if (zerop (setq state1 (aref v i))) + (setq i (1+ i)) + (setq symbol (aref accessing-symbol state1)) + ;; The following line used to be turned off. + (if (wisent-ISVAR symbol) + (setq break t) ;; break + (wisent-log " %s\tshift, and go to state %d\n" + (wisent-tag symbol) state1) + (setq i (1+ i))))) + (if (> i 0) + (wisent-log "\n"))) + + (when errp + (setq nerrs (errs-nerrs errp) + v (errs-errs errp) + j 0) + (while (< j nerrs) + (if (aref v j) + (wisent-log " %s\terror (nonassociative)\n" + (wisent-tag (aref v j)))) + (setq j (1+ j))) + (if (> j 0) + (wisent-log "\n"))) + + (cond + ((and (aref consistent state) redp) + (setq rule (aref (reductions-rules redp) 0) + symbol (aref rlhs rule)) + (wisent-log " $default\treduce using rule %d (%s)\n\n" + rule (wisent-tag symbol)) + ) + (redp + (wisent-print-reductions state) + )) + + (when (< i k) + (setq v (shifts-shifts shiftp)) + (while (< i k) + (when (setq state1 (aref v i)) + (setq symbol (aref accessing-symbol state1)) + (wisent-log " %s\tgo to state %d\n" + (wisent-tag symbol) state1)) + (setq i (1+ i))) + (wisent-log "\n")) + ))) + +(defun wisent-print-core (state) + "Print STATE core." + (let (i k rule statep sp sp1) + (setq statep (aref state-table state) + k (core-nitems statep)) + (when (> k 0) + (setq i 0) + (while (< i k) + ;; sp1 = sp = ritem + statep->items[i]; + (setq sp1 (aref (core-items statep) i) + sp sp1) + (while (> (aref ritem sp) 0) + (setq sp (1+ sp))) + + (setq rule (- (aref ritem sp))) + (wisent-log " %s -> " (wisent-tag (aref rlhs rule))) + + (setq sp (aref rrhs rule)) + (while (< sp sp1) + (wisent-log "%s " (wisent-tag (aref ritem sp))) + (setq sp (1+ sp))) + (wisent-log ".") + (while (> (aref ritem sp) 0) + (wisent-log " %s" (wisent-tag (aref ritem sp))) + (setq sp (1+ sp))) + (wisent-log " (rule %d)\n" rule) + (setq i (1+ i))) + (wisent-log "\n")))) + +(defun wisent-print-state (state) + "Print information on STATE." + (wisent-log "\n\nstate %d\n\n" state) + (wisent-print-core state) + (wisent-print-actions state)) + +(defun wisent-print-states () + "Print information on states." + (let ((i 0)) + (while (< i nstates) + (wisent-print-state i) + (setq i (1+ i))))) + +(defun wisent-print-results () + "Print information on generated parser. +Report detailed informations if `wisent-verbose-flag' or +`wisent-debug-flag' are non-nil." + (when (or wisent-verbose-flag wisent-debug-flag) + (wisent-print-useless)) + (wisent-print-conflicts) + (when (or wisent-verbose-flag wisent-debug-flag) + (wisent-print-grammar) + (wisent-print-states)) + ;; Append output to log file when running in batch mode + (when (wisent-noninteractive) + (wisent-append-to-log-file) + (wisent-clear-log))) + +;;;; --------------------------------- +;;;; Build the generated parser tables +;;;; --------------------------------- + +(defun wisent-action-row (state actrow) + "Figure out the actions for the specified STATE. +Decide what to do for each type of token if seen as the lookahead +token in specified state. The value returned is used as the default +action for the state. In addition, ACTROW is filled with what to do +for each kind of token, index by symbol number, with nil meaning do +the default action. The value 'error, means this situation is an +error. The parser recognizes this value specially. + +This is where conflicts are resolved. The loop over lookahead rules +considered lower-numbered rules last, and the last rule considered +that likes a token gets to handle it." + (let (i j k m n v default-rule nreds rule max count + shift-state symbol redp shiftp errp nodefault) + + (fillarray actrow nil) + + (setq default-rule 0 + nodefault nil ;; nil inhibit having any default reduction + nreds 0 + m 0 + n 0 + redp (aref reduction-table state)) + + (when redp + (setq nreds (reductions-nreds redp)) + (when (>= nreds 1) + ;; loop over all the rules available here which require + ;; lookahead + (setq m (aref lookaheads state) + n (aref lookaheads (1+ state)) + i (1- n)) + (while (>= i m) + ;; and find each token which the rule finds acceptable to + ;; come next + (setq j 0) + (while (< j ntokens) + ;; and record this rule as the rule to use if that token + ;; follows. + (if (wisent-BITISSET (aref LA i) j) + (aset actrow j (- (aref LAruleno i))) + ) + (setq j (1+ j))) + (setq i (1- i))))) + + ;; Now see which tokens are allowed for shifts in this state. For + ;; them, record the shift as the thing to do. So shift is + ;; preferred to reduce. + (setq shiftp (aref shift-table state)) + (when shiftp + (setq k (shifts-nshifts shiftp) + v (shifts-shifts shiftp) + i 0) + (while (< i k) + (setq shift-state (aref v i)) + (if (zerop shift-state) + nil ;; continue + (setq symbol (aref accessing-symbol shift-state)) + (if (wisent-ISVAR symbol) + (setq i k) ;; break + (aset actrow symbol shift-state) + ;; Do not use any default reduction if there is a shift + ;; for error + (if (= symbol error-token-number) + (setq nodefault t)))) + (setq i (1+ i)))) + + ;; See which tokens are an explicit error in this state (due to + ;; %nonassoc). For them, record error as the action. + (setq errp (aref err-table state)) + (when errp + (setq k (errs-nerrs errp) + v (errs-errs errp) + i 0) + (while (< i k) + (aset actrow (aref v i) wisent-error-tag) + (setq i (1+ i)))) + + ;; Now find the most common reduction and make it the default + ;; action for this state. + (when (and (>= nreds 1) (not nodefault)) + (if (aref consistent state) + (setq default-rule (- (aref (reductions-rules redp) 0))) + (setq max 0 + i m) + (while (< i n) + (setq count 0 + rule (- (aref LAruleno i)) + j 0) + (while (< j ntokens) + (if (and (numberp (aref actrow j)) + (= (aref actrow j) rule)) + (setq count (1+ count))) + (setq j (1+ j))) + (if (> count max) + (setq max count + default-rule rule)) + (setq i (1+ i))) + ;; actions which match the default are replaced with zero, + ;; which means "use the default" + (when (> max 0) + (setq j 0) + (while (< j ntokens) + (if (and (numberp (aref actrow j)) + (= (aref actrow j) default-rule)) + (aset actrow j nil)) + (setq j (1+ j))) + ))) + + ;; If have no default rule, if this is the final state the default + ;; is accept else it is an error. So replace any action which + ;; says "error" with "use default". + (when (zerop default-rule) + (if (= final-state state) + (setq default-rule wisent-accept-tag) + (setq j 0) + (while (< j ntokens) + (if (eq (aref actrow j) wisent-error-tag) + (aset actrow j nil)) + (setq j (1+ j))) + (setq default-rule wisent-error-tag))) + default-rule)) + +(defconst wisent-default-tag 'default + "Tag used in an action table to indicate a default action.") + +;; These variables only exist locally in the function +;; `wisent-state-actions' and are shared by all other nested callees. +(wisent-defcontext semantic-actions + ;; Uninterned symbols used in code generation. + stack sp gotos state + ;; Name of the current semantic action + NAME) + +(defun wisent-state-actions () + "Figure out the actions for every state. +Return the action table." + ;; Store the semantic action obarray in (unused) RCODE[0]. + (aset rcode 0 (make-vector 13 0)) + (let (i j action-table actrow action) + (setq action-table (make-vector nstates nil) + actrow (make-vector ntokens nil) + i 0) + (wisent-with-context semantic-actions + (setq stack (make-symbol "stack") + sp (make-symbol "sp") + gotos (make-symbol "gotos") + state (make-symbol "state")) + (while (< i nstates) + (setq action (wisent-action-row i actrow)) + ;; Translate a reduction into semantic action + (and (integerp action) (< action 0) + (setq action (wisent-semantic-action (- action)))) + (aset action-table i (list (cons wisent-default-tag action))) + (setq j 0) + (while (< j ntokens) + (when (setq action (aref actrow j)) + ;; Translate a reduction into semantic action + (and (integerp action) (< action 0) + (setq action (wisent-semantic-action (- action)))) + (aset action-table i (cons (cons (aref tags j) action) + (aref action-table i))) + ) + (setq j (1+ j))) + (aset action-table i (nreverse (aref action-table i))) + (setq i (1+ i))) + action-table))) + +(defun wisent-goto-actions () + "Figure out what to do after reducing with each rule. +Depending on the saved state from before the beginning of parsing the +data that matched this rule. Return the goto table." + (let (i j m n symbol state goto-table) + (setq goto-table (make-vector nstates nil) + i ntokens) + (while (< i nsyms) + (setq symbol (- i ntokens) + m (aref goto-map symbol) + n (aref goto-map (1+ symbol)) + j m) + (while (< j n) + (setq state (aref from-state j)) + (aset goto-table state + (cons (cons (aref tags i) (aref to-state j)) + (aref goto-table state))) + (setq j (1+ j))) + (setq i (1+ i))) + goto-table)) + +(defsubst wisent-quote-p (sym) + "Return non-nil if SYM is bound to the `quote' function." + (condition-case nil + (eq (indirect-function sym) + (indirect-function 'quote)) + (error nil))) + +(defsubst wisent-backquote-p (sym) + "Return non-nil if SYM is bound to the `backquote' function." + (condition-case nil + (eq (indirect-function sym) + (indirect-function 'backquote)) + (error nil))) + +(defun wisent-check-$N (x m) + "Return non-nil if X is a valid $N or $regionN symbol. +That is if X is a $N or $regionN symbol with N >= 1 and N <= M. +Also warn if X is a $N or $regionN symbol with N < 1 or N > M." + (when (symbolp x) + (let* ((n (symbol-name x)) + (i (and (string-match "\\`\\$\\(region\\)?\\([0-9]+\\)\\'" n) + (string-to-number (match-string 2 n))))) + (when i + (if (and (>= i 1) (<= i m)) + t + (message + "*** In %s, %s might be a free variable (rule has %s)" + NAME x (format (cond ((< m 1) "no component") + ((= m 1) "%d component") + ("%d components")) + m)) + nil))))) + +(defun wisent-semantic-action-expand-body (body n &optional found) + "Parse BODY of semantic action. +N is the maximum number of $N variables that can be referenced in +BODY. Warn on references out of permitted range. +Optional argument FOUND is the accumulated list of '$N' references +encountered so far. +Return a cons (FOUND . XBODY), where FOUND is the list of $N +references found in BODY, and XBODY is BODY expression with +`backquote' forms expanded." + (if (not (listp body)) + ;; BODY is an atom, no expansion needed + (progn + (if (wisent-check-$N body n) + ;; Accumulate $i symbol + (add-to-list 'found body)) + (cons found body)) + ;; BODY is a list, expand inside it + (let (xbody sexpr) + ;; If backquote expand it first + (if (wisent-backquote-p (car body)) + (setq body (macroexpand body))) + (while body + (setq sexpr (car body) + body (cdr body)) + (cond + ;; Function call excepted quote expression + ((and (consp sexpr) + (not (wisent-quote-p (car sexpr)))) + (setq sexpr (wisent-semantic-action-expand-body sexpr n found) + found (car sexpr) + sexpr (cdr sexpr))) + ;; $i symbol + ((wisent-check-$N sexpr n) + ;; Accumulate $i symbol + (add-to-list 'found sexpr)) + ) + ;; Accumulate expanded forms + (setq xbody (nconc xbody (list sexpr)))) + (cons found xbody)))) + +(defun wisent-semantic-action (r) + "Set up the Elisp function for semantic action at rule R. +On entry RCODE[R] contains a vector [BODY N (NTERM I)] where BODY is the +body of the semantic action, N is the maximum number of values +available in the parser's stack, NTERM is the nonterminal the semantic +action belongs to, and I is the index of the semantic action inside +NTERM definition. Return the semantic action symbol. +The semantic action function accepts three arguments: + +- the state/value stack +- the top-of-stack index +- the goto table + +And returns the updated top-of-stack index." + (if (not (aref ruseful r)) + (aset rcode r nil) + (let* ((actn (aref rcode r)) + (n (aref actn 1)) ; nb of val avail. in stack + (NAME (apply 'format "%s:%d" (aref actn 2))) + (form (wisent-semantic-action-expand-body (aref actn 0) n)) + ($l (car form)) ; list of $vars used in body + (form (cdr form)) ; expanded form of body + (nt (aref rlhs r)) ; nonterminal item no. + (bl nil) ; `let*' binding list + $v i j) + + ;; Compute $N and $regionN bindings + (setq i n) + (while (> i 0) + (setq j (1+ (* 2 (- n i)))) + ;; Only bind $regionI if used in action + (setq $v (intern (format "$region%d" i))) + (if (memq $v $l) + (setq bl (cons `(,$v (cdr (aref ,stack (- ,sp ,j)))) bl))) + ;; Only bind $I if used in action + (setq $v (intern (format "$%d" i))) + (if (memq $v $l) + (setq bl (cons `(,$v (car (aref ,stack (- ,sp ,j)))) bl))) + (setq i (1- i))) + + ;; Compute J, the length of rule's RHS. It will give the + ;; current parser state at STACK[SP - 2*J], and where to push + ;; the new semantic value and the next state, respectively at: + ;; STACK[SP - 2*J + 1] and STACK[SP - 2*J + 2]. Generally N, + ;; the maximum number of values available in the stack, is equal + ;; to J. But, for mid-rule actions, N is the number of rule + ;; elements before the action and J is always 0 (empty rule). + (setq i (aref rrhs r) + j 0) + (while (> (aref ritem i) 0) + (setq j (1+ j) + i (1+ i))) + + ;; Create the semantic action symbol. + (setq actn (intern NAME (aref rcode 0))) + + ;; Store source code in function cell of the semantic action + ;; symbol. It will be byte-compiled at automaton's compilation + ;; time. Using a byte-compiled automaton can significantly + ;; speed up parsing! + (fset actn + `(lambda (,stack ,sp ,gotos) + (let* (,@bl + ($region + ,(cond + ((= n 1) + (if (assq '$region1 bl) + '$region1 + `(cdr (aref ,stack (1- ,sp))))) + ((> n 1) + `(wisent-production-bounds + ,stack (- ,sp ,(1- (* 2 n))) (1- ,sp))))) + ($action ,NAME) + ($nterm ',(aref tags nt)) + ,@(and (> j 0) `((,sp (- ,sp ,(* j 2))))) + (,state (cdr (assq $nterm + (aref ,gotos + (aref ,stack ,sp)))))) + (setq ,sp (+ ,sp 2)) + ;; push semantic value + (aset ,stack (1- ,sp) (cons ,form $region)) + ;; push next state + (aset ,stack ,sp ,state) + ;; return new top of stack + ,sp))) + + ;; Return the semantic action symbol + actn))) + +;;;; ---------------------------- +;;;; Build parser LALR automaton. +;;;; ---------------------------- + +(defun wisent-parser-automaton () + "Compute and return LALR(1) automaton from GRAMMAR. +GRAMMAR is in internal format. GRAM/ACTS are grammar rules +in internal format. STARTS defines the start symbols." + ;; Check for useless stuff + (wisent-reduce-grammar) + + (wisent-set-derives) + (wisent-set-nullable) + ;; convert to nondeterministic finite state machine. + (wisent-generate-states) + ;; make it deterministic. + (wisent-lalr) + ;; Find and record any conflicts: places where one token of + ;; lookahead is not enough to disambiguate the parsing. Also + ;; resolve s/r conflicts based on precedence declarations. + (wisent-resolve-conflicts) + (wisent-print-results) + + (vector (wisent-state-actions) ; action table + (wisent-goto-actions) ; goto table + start-table ; start symbols + (aref rcode 0) ; sem. action symbol obarray + ) + ) + +;;;; ------------------- +;;;; Parse input grammar +;;;; ------------------- + +(defconst wisent-reserved-symbols (list wisent-error-term) + "The list of reserved symbols. +Also all symbols starting with a character defined in +`wisent-reserved-capitals' are reserved for internal use.") + +(defconst wisent-reserved-capitals '(?\$ ?\@) + "The list of reserved capital letters. +All symbol starting with one of these letters are reserved for +internal use.") + +(defconst wisent-starts-nonterm '$STARTS + "Main start symbol. +It gives the rules for start symbols.") + +(defvar wisent-single-start-flag nil + "Non-nil means allows only one start symbol like in Bison. +That is don't add extra start rules to the grammar. This is +useful to compare the Wisent's generated automaton with the Bison's +one.") + +(defsubst wisent-ISVALID-VAR (x) + "Return non-nil if X is a character or an allowed symbol." + (and x (symbolp x) + (not (memq (aref (symbol-name x) 0) wisent-reserved-capitals)) + (not (memq x wisent-reserved-symbols)))) + +(defsubst wisent-ISVALID-TOKEN (x) + "Return non-nil if X is a character or an allowed symbol." + (or (wisent-char-p x) + (wisent-ISVALID-VAR x))) + +(defun wisent-push-token (symbol &optional nocheck) + "Push a new SYMBOL in the list of tokens. +Bypass checking if NOCHECK is non-nil." + ;; Check + (or nocheck (wisent-ISVALID-TOKEN symbol) + (error "Invalid terminal symbol: %S" symbol)) + (if (memq symbol token-list) + (message "*** duplicate terminal `%s' ignored" symbol) + ;; Set up properties + (wisent-set-prec symbol nil) + (wisent-set-assoc symbol nil) + (wisent-set-item-number symbol ntokens) + ;; Add + (setq ntokens (1+ ntokens) + token-list (cons symbol token-list)))) + +(defun wisent-push-var (symbol &optional nocheck) + "Push a new SYMBOL in the list of nonterminals. +Bypass checking if NOCHECK is non-nil." + ;; Check + (unless nocheck + (or (wisent-ISVALID-VAR symbol) + (error "Invalid nonterminal symbol: %S" symbol)) + (if (memq symbol var-list) + (error "Nonterminal `%s' already defined" symbol))) + ;; Set up properties + (wisent-set-item-number symbol nvars) + ;; Add + (setq nvars (1+ nvars) + var-list (cons symbol var-list))) + +(defun wisent-parse-nonterminals (defs) + "Parse nonterminal definitions in DEFS. +Fill in each element of the global arrays RPREC, RCODE, RUSEFUL with +respectively rule precedence level, semantic action code and +usefulness flag. Return a list of rules of the form (LHS . RHS) where +LHS and RHS are respectively the Left Hand Side and Right Hand Side of +the rule." + (setq rprec nil + rcode nil + nitems 0 + nrules 0) + (let (def nonterm rlist rule rules rhs rest item items + rhl plevel semact @n @count iactn) + (setq @count 0) + (while defs + (setq def (car defs) + defs (cdr defs) + nonterm (car def) + rlist (cdr def) + iactn 0) + (or (consp rlist) + (error "Invalid nonterminal definition syntax: %S" def)) + (while rlist + (setq rule (car rlist) + rlist (cdr rlist) + items (car rule) + rest (cdr rule) + rhl 0 + rhs nil) + + ;; Check & count items + (setq nitems (1+ nitems)) ;; LHS item + (while items + (setq item (car items) + items (cdr items) + nitems (1+ nitems)) ;; RHS items + (if (listp item) + ;; Mid-rule action + (progn + (setq @count (1+ @count) + @n (intern (format "@%d" @count))) + (wisent-push-var @n t) + ;; Push a new empty rule with the mid-rule action + (setq semact (vector item rhl (list nonterm iactn)) + iactn (1+ iactn) + plevel nil + rcode (cons semact rcode) + rprec (cons plevel rprec) + item @n ;; Replace action by @N nonterminal + rules (cons (list item) rules) + nitems (1+ nitems) + nrules (1+ nrules))) + ;; Check terminal or nonterminal symbol + (cond + ((or (memq item token-list) (memq item var-list))) + ;; Create new literal character token + ((wisent-char-p item) (wisent-push-token item t)) + ((error "Symbol `%s' is used, but is not defined as a token and has no rules" + item)))) + (setq rhl (1+ rhl) + rhs (cons item rhs))) + + ;; Check & collect rule precedence level + (setq plevel (when (vectorp (car rest)) + (setq item (car rest) + rest (cdr rest)) + (if (and (= (length item) 1) + (memq (aref item 0) token-list) + (wisent-prec (aref item 0))) + (wisent-item-number (aref item 0)) + (error "Invalid rule precedence level syntax: %S" item))) + rprec (cons plevel rprec)) + + ;; Check & collect semantic action body + (setq semact (vector + (if rest + (if (cdr rest) + (error "Invalid semantic action syntax: %S" rest) + (car rest)) + ;; Give a default semantic action body: nil + ;; for an empty rule or $1, the value of the + ;; first symbol in the rule, otherwise. + (if (> rhl 0) '$1 '())) + rhl + (list nonterm iactn)) + iactn (1+ iactn) + rcode (cons semact rcode)) + (setq rules (cons (cons nonterm (nreverse rhs)) rules) + nrules (1+ nrules)))) + + (setq ruseful (make-vector (1+ nrules) t) + rprec (vconcat (cons nil (nreverse rprec))) + rcode (vconcat (cons nil (nreverse rcode)))) + (nreverse rules) + )) + +(defun wisent-parse-grammar (grammar &optional start-list) + "Parse GRAMMAR and build a suitable internal representation. +Optional argument START-LIST defines the start symbols. +GRAMMAR is a list of form: (TOKENS ASSOCS . NONTERMS) + +TOKENS is a list of terminal symbols (tokens). + +ASSOCS is nil or an alist of (ASSOC-TYPE . ASSOC-VALUE) elements +describing the associativity of TOKENS. ASSOC-TYPE must be one of the +`default-prec' `nonassoc', `left' or `right' symbols. When ASSOC-TYPE +is `default-prec', ASSOC-VALUE must be nil or t (the default). +Otherwise it is a list of tokens which must have been previously +declared in TOKENS. + +NONTERMS is the list of non terminal definitions (see function +`wisent-parse-nonterminals')." + (or (and (consp grammar) (> (length grammar) 2)) + (error "Bad input grammar")) + + (let (i r rhs pre dpre lst start-var assoc rules item + token var def tokens defs ep-token ep-var ep-def) + + ;; Built-in tokens + (setq ntokens 0 nvars 0) + (wisent-push-token wisent-eoi-term t) + (wisent-push-token wisent-error-term t) + + ;; Check/collect terminals + (setq lst (car grammar)) + (while lst + (wisent-push-token (car lst)) + (setq lst (cdr lst))) + + ;; Check/Set up tokens precedence & associativity + (setq lst (nth 1 grammar) + pre 0 + defs nil + dpre nil + default-prec t) + (while lst + (setq def (car lst) + assoc (car def) + tokens (cdr def) + lst (cdr lst)) + (if (eq assoc 'default-prec) + (progn + (or (null (cdr tokens)) + (memq (car tokens) '(t nil)) + (error "Invalid default-prec value: %S" tokens)) + (setq default-prec (car tokens)) + (if dpre + (message "*** redefining default-prec to %s" + default-prec)) + (setq dpre t)) + (or (memq assoc '(left right nonassoc)) + (error "Invalid associativity syntax: %S" assoc)) + (setq pre (1+ pre)) + (while tokens + (setq token (car tokens) + tokens (cdr tokens)) + (if (memq token defs) + (message "*** redefining precedence of `%s'" token)) + (or (memq token token-list) + ;; Define token not previously declared. + (wisent-push-token token)) + (setq defs (cons token defs)) + ;; Record the precedence and associativity of the terminal. + (wisent-set-prec token pre) + (wisent-set-assoc token assoc)))) + + ;; Check/Collect nonterminals + (setq lst (nthcdr 2 grammar) + defs nil) + (while lst + (setq def (car lst) + lst (cdr lst)) + (or (consp def) + (error "Invalid nonterminal definition: %S" def)) + (if (memq (car def) token-list) + (error "Nonterminal `%s' already defined as token" (car def))) + (wisent-push-var (car def)) + (setq defs (cons def defs))) + (or defs + (error "No input grammar")) + (setq defs (nreverse defs)) + + ;; Set up the start symbol. + (setq start-table nil) + (cond + + ;; 1. START-LIST is nil, the start symbol is the first + ;; nonterminal defined in the grammar (Bison like). + ((null start-list) + (setq start-var (caar defs))) + + ;; 2. START-LIST contains only one element, it is the start + ;; symbol (Bison like). + ((or wisent-single-start-flag (null (cdr start-list))) + (setq start-var (car start-list)) + (or (assq start-var defs) + (error "Start symbol `%s' has no rule" start-var))) + + ;; 3. START-LIST contains more than one element. All defines + ;; potential start symbols. One of them (the first one by + ;; default) will be given at parse time to be the parser goal. + ;; If `wisent-single-start-flag' is non-nil that feature is + ;; disabled and the first nonterminal in START-LIST defines + ;; the start symbol, like in case 2 above. + ((not wisent-single-start-flag) + + ;; START-LIST is a list of nonterminals '(nt0 ... ntN). + ;; Build and push ad hoc start rules in the grammar: + + ;; ($STARTS ((nt0) $1) ((nt1) $1) ... ((ntN) $1)) + ;; ($nt1 (($$nt1 nt1) $2)) + ;; ... + ;; ($ntN (($$ntN ntN) $2)) + + ;; Where internal symbols $ntI and $$ntI are respectively + ;; nonterminals and terminals. + + ;; The internal start symbol $STARTS is used to build the + ;; LALR(1) automaton. The true default start symbol used by the + ;; parser is the first nonterminal in START-LIST (nt0). + (setq start-var wisent-starts-nonterm + lst (nreverse start-list)) + (while lst + (setq var (car lst) + lst (cdr lst)) + (or (memq var var-list) + (error "Start symbol `%s' has no rule" var)) + (unless (assq var start-table) ;; Ignore duplicates + ;; For each nt start symbol + (setq ep-var (intern (format "$%s" var)) + ep-token (intern (format "$$%s" var))) + (wisent-push-token ep-token t) + (wisent-push-var ep-var t) + (setq + ;; Add entry (nt . $$nt) to start-table + start-table (cons (cons var ep-token) start-table) + ;; Add rule ($nt (($$nt nt) $2)) + defs (cons (list ep-var (list (list ep-token var) '$2)) defs) + ;; Add start rule (($nt) $1) + ep-def (cons (list (list ep-var) '$1) ep-def)) + )) + (wisent-push-var start-var t) + (setq defs (cons (cons start-var ep-def) defs)))) + + ;; Set up rules main data structure & RPREC, RCODE, RUSEFUL + (setq rules (wisent-parse-nonterminals defs)) + + ;; Set up the terminal & nonterminal lists. + (setq nsyms (+ ntokens nvars) + token-list (nreverse token-list) + lst var-list + var-list nil) + (while lst + (setq var (car lst) + lst (cdr lst) + var-list (cons var var-list)) + (wisent-set-item-number ;; adjust nonterminal item number to + var (+ ntokens (wisent-item-number var)))) ;; I += NTOKENS + + ;; Store special item numbers + (setq error-token-number (wisent-item-number wisent-error-term) + start-symbol (wisent-item-number start-var)) + + ;; Keep symbols in the TAGS vector so that TAGS[I] is the symbol + ;; associated to item number I. + (setq tags (vconcat token-list var-list)) + ;; Set up RLHS RRHS & RITEM data structures from list of rules + ;; (LHS . RHS) received from `wisent-parse-nonterminals'. + (setq rlhs (make-vector (1+ nrules) nil) + rrhs (make-vector (1+ nrules) nil) + ritem (make-vector (1+ nitems) nil) + i 0 + r 1) + (while rules + (aset rlhs r (wisent-item-number (caar rules))) + (aset rrhs r i) + (setq rhs (cdar rules) + pre nil) + (while rhs + (setq item (wisent-item-number (car rhs))) + ;; Get default precedence level of rule, that is the + ;; precedence of the last terminal in it. + (and (wisent-ISTOKEN item) + default-prec + (setq pre item)) + + (aset ritem i item) + (setq i (1+ i) + rhs (cdr rhs))) + ;; Setup the precedence level of the rule, that is the one + ;; specified by %prec or the default one. + (and (not (aref rprec r)) ;; Already set by %prec + pre + (wisent-prec (aref tags pre)) + (aset rprec r pre)) + (aset ritem i (- r)) + (setq i (1+ i) + r (1+ r)) + (setq rules (cdr rules))) + )) + +;;;; --------------------- +;;;; Compile input grammar +;;;; --------------------- + +(defun wisent-compile-grammar (grammar &optional start-list) + "Compile the LALR(1) GRAMMAR. + +GRAMMAR is a list (TOKENS ASSOCS . NONTERMS) where: + +- TOKENS is a list of terminal symbols (tokens). + +- ASSOCS is nil, or an alist of (ASSOC-TYPE . ASSOC-VALUE) elements + describing the associativity of TOKENS. ASSOC-TYPE must be one of + the `default-prec' `nonassoc', `left' or `right' symbols. When + ASSOC-TYPE is `default-prec', ASSOC-VALUE must be nil or t (the + default). Otherwise it is a list of tokens which must have been + previously declared in TOKENS. + +- NONTERMS is a list of nonterminal definitions. + +Optional argument START-LIST specify the possible grammar start +symbols. This is a list of nonterminals which must have been +previously declared in GRAMMAR's NONTERMS form. By default, the start +symbol is the first nonterminal defined. When START-LIST contains +only one element, it is the start symbol. Otherwise, all elements are +possible start symbols, unless `wisent-single-start-flag' is non-nil. +In that case, the first element is the start symbol, and others are +ignored. + +Return an automaton as a vector: [ACTIONS GOTOS STARTS FUNCTIONS] +where: + +- ACTIONS is a state/token matrix telling the parser what to do at + every state based on the current lookahead token. That is shift, + reduce, accept or error. + +- GOTOS is a state/nonterminal matrix telling the parser the next + state to go to after reducing with each rule. + +- STARTS is an alist which maps the allowed start nonterminal symbols + to tokens that will be first shifted into the parser stack. + +- FUNCTIONS is an obarray of semantic action symbols. Each symbol's + function definition is the semantic action lambda expression." + (if (wisent-automaton-p grammar) + grammar ;; Grammar already compiled just return it + (wisent-with-context compile-grammar + (let* ((gc-cons-threshold 1000000) + automaton) + (garbage-collect) + (setq wisent-new-log-flag t) + ;; Parse input grammar + (wisent-parse-grammar grammar start-list) + ;; Generate the LALR(1) automaton + (setq automaton (wisent-parser-automaton)) + automaton)))) + +;;;; -------------------------- +;;;; Byte compile input grammar +;;;; -------------------------- + +(require 'bytecomp) + +(defun wisent-byte-compile-grammar (form) + "Byte compile the `wisent-compile-grammar' FORM. +Automatically called by the Emacs Lisp byte compiler as a +`byte-compile' handler." + ;; Eval the `wisent-compile-grammar' form to obtain an LALR + ;; automaton internal data structure. Then, because the internal + ;; data structure contains an obarray, convert it to a lisp form so + ;; it can be byte-compiled. + (byte-compile-form (wisent-automaton-lisp-form (eval form)))) + +(put 'wisent-compile-grammar 'byte-compile 'wisent-byte-compile-grammar) + +(defun wisent-automaton-lisp-form (automaton) + "Return a Lisp form that produces AUTOMATON. +See also `wisent-compile-grammar' for more details on AUTOMATON." + (or (wisent-automaton-p automaton) + (signal 'wrong-type-argument + (list 'wisent-automaton-p automaton))) + (let ((obn (make-symbol "ob")) ; Generated obarray name + (obv (aref automaton 3)) ; Semantic actions obarray + ) + `(let ((,obn (make-vector 13 0))) + ;; Generate code to initialize the semantic actions obarray, + ;; in local variable OBN. + ,@(let (obcode) + (mapatoms + #'(lambda (s) + (setq obcode + (cons `(fset (intern ,(symbol-name s) ,obn) + #',(symbol-function s)) + obcode))) + obv) + obcode) + ;; Generate code to create the automaton. + (vector + ;; In code generated to initialize the action table, take + ;; care of symbols that are interned in the semantic actions + ;; obarray. + (vector + ,@(mapcar + #'(lambda (state) ;; for each state + `(list + ,@(mapcar + #'(lambda (tr) ;; for each transition + (let ((k (car tr)) ; token + (a (cdr tr))) ; action + (if (and (symbolp a) + (intern-soft (symbol-name a) obv)) + `(cons ,(if (symbolp k) `(quote ,k) k) + (intern-soft ,(symbol-name a) ,obn)) + `(quote ,tr)))) + state))) + (aref automaton 0))) + ;; The code of the goto table is unchanged. + ,(aref automaton 1) + ;; The code of the alist of start symbols is unchanged. + ',(aref automaton 2) + ;; The semantic actions obarray is in the local variable OBN. + ,obn)))) + +(provide 'semantic/wisent/comp) + +;;; semantic/wisent/comp.el ends here diff --git a/lisp/cedet/semantic/wisent/java-tags.el b/lisp/cedet/semantic/wisent/java-tags.el new file mode 100644 index 00000000000..5a9d8c398b4 --- /dev/null +++ b/lisp/cedet/semantic/wisent/java-tags.el @@ -0,0 +1,122 @@ +;;; semantic/wisent/java-tags.el --- Java LALR parser for Emacs + +;;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 +;;; Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> +;; Maintainer: David Ponce <david@dponce.com> +;; Created: 15 Dec 2001 +;; 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: +;; + +;;; History: +;; + +;;; Code: + +(require 'semantic/wisent) +(require 'semantic/wisent/javat-wy) +(require 'semantic/java) + +;;;; +;;;; Simple parser error reporting function +;;;; + +(defun wisent-java-parse-error (msg) + "Error reporting function called when a parse error occurs. +MSG is the message string to report." +;; (let ((error-start (nth 2 wisent-input))) +;; (if (number-or-marker-p error-start) +;; (goto-char error-start))) + (message msg) + ;;(debug) + ) + +;;;; +;;;; Local context +;;;; + +(define-mode-local-override semantic-get-local-variables + java-mode () + "Get local values from a specific context. +Parse the current context for `field_declaration' nonterminals to +collect tags, such as local variables or prototypes. +This function override `get-local-variables'." + (let ((vars nil) + ;; We want nothing to do with funny syntaxing while doing this. + (semantic-unmatched-syntax-hook nil)) + (while (not (semantic-up-context (point) 'function)) + (save-excursion + (forward-char 1) + (setq vars + (append (semantic-parse-region + (point) + (save-excursion (semantic-end-of-context) (point)) + 'field_declaration + 0 t) + vars)))) + vars)) + +;;;; +;;;; Semantic integration of the Java LALR parser +;;;; + +;;;###autoload +(defun wisent-java-default-setup () + "Hook run to setup Semantic in `java-mode'. +Use the alternate LALR(1) parser." + (wisent-java-tags-wy--install-parser) + (setq + ;; Lexical analysis + semantic-lex-number-expression semantic-java-number-regexp + semantic-lex-analyzer 'wisent-java-tags-lexer + ;; Parsing + semantic-tag-expand-function 'semantic-java-expand-tag + ;; Environment + semantic-imenu-summary-function 'semantic-format-tag-prototype + imenu-create-index-function 'semantic-create-imenu-index + semantic-type-relation-separator-character '(".") + semantic-command-separation-character ";" + ;; speedbar and imenu buckets name + semantic-symbol->name-assoc-list-for-type-parts + ;; in type parts + '((type . "Classes") + (variable . "Variables") + (function . "Methods")) + semantic-symbol->name-assoc-list + ;; everywhere + (append semantic-symbol->name-assoc-list-for-type-parts + '((include . "Imports") + (package . "Package"))) + ;; navigation inside 'type children + senator-step-at-tag-classes '(function variable) + ) + ;; Setup javadoc stuff + (semantic-java-doc-setup)) + +(provide 'semantic/wisent/java-tags) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/wisent/java-tags" +;; End: + +;;; semantic/wisent/java-tags.el ends here diff --git a/lisp/cedet/semantic/wisent/javascript.el b/lisp/cedet/semantic/wisent/javascript.el new file mode 100644 index 00000000000..892f76be4f0 --- /dev/null +++ b/lisp/cedet/semantic/wisent/javascript.el @@ -0,0 +1,103 @@ +;;; semantic/wisent/javascript.el --- javascript parser support + +;;; Copyright (C) 2005 Free Software Foundation, Inc. + +;; Author: Eric 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: +;; +;; Parser support for javascript language. + + +;;; Code: +(require 'semantic/java) +(require 'semantic/wisent) +(require 'semantic/wisent/js-wy) + +(defun wisent-javascript-jv-expand-tag (tag) + "Expand TAG into a list of equivalent tags, or nil. +Expand multiple variable declarations in the same statement, that is +tags of class `variable' whose name is equal to a list of elements of +the form (NAME VALUE START . END). NAME is a variable name. VALUE is +an initializer START and END are the bounds in the declaration, related +to this variable NAME." + (let (elts elt value clone start end xpand) + (when (and (eq 'variable (semantic-tag-class tag)) + (consp (setq elts (semantic-tag-name tag)))) + ;; There are multiple names in the same variable declaration. + (while elts + ;; For each name element, clone the initial tag and give it + ;; the name of the element. + (setq elt (car elts) + elts (cdr elts) + clone (semantic-tag-clone tag (car elt)) + value (car (cdr elt)) + start (if elts (caddr elt) (semantic-tag-start tag)) + end (if xpand (cdddr elt) (semantic-tag-end tag)) + xpand (cons clone xpand)) + ;; Set the definition of the cloned tag + (semantic-tag-put-attribute clone :default-value value) + ;; Set the bounds of the cloned tag with those of the name + ;; element. + (semantic-tag-set-bounds clone start end)) + xpand))) + +;;; Override Methods +;; +;; These methods override aspects of how semantic-tools can access +;; the tags created by the javascript parser. +;; Local context +(define-mode-overload-implementation semantic-get-local-variables + javascript-mode () + "Get local values from a specific context. +This function overrides `get-local-variables'." + ;; Does javascript have identifiable local variables? + nil) + + +;;; Setup Function +;; +;; This sets up the javascript parser + +;;;###autoload +(defun wisent-javascript-setup-parser () + "Setup buffer for parse." + (wisent-javascript-jv-wy--install-parser) + (setq + ;; Lexical Analysis + semantic-lex-analyzer 'javascript-lexer-jv + semantic-lex-number-expression semantic-java-number-regexp + ;; semantic-lex-depth nil ;; Full lexical analysis + ;; Parsing + semantic-tag-expand-function 'wisent-javascript-jv-expand-tag + ;; Environment + semantic-imenu-summary-function 'semantic-format-tag-name + imenu-create-index-function 'semantic-create-imenu-index + semantic-command-separation-character ";" + )) + +(provide 'semantic/wisent/javascript-jv) + +;; Local variables: +;; generated-autoload-file: "../loaddefs.el" +;; generated-autoload-feature: semantic/loaddefs +;; generated-autoload-load-name: "semantic/wisent/javascript" +;; End: + +;;; semantic/wisent/javascript-jv.el ends here diff --git a/lisp/cedet/semantic/wisent/javat-wy.el b/lisp/cedet/semantic/wisent/javat-wy.el Binary files differnew file mode 100644 index 00000000000..0cbee2c086b --- /dev/null +++ b/lisp/cedet/semantic/wisent/javat-wy.el diff --git a/lisp/cedet/semantic/wisent/js-wy.el b/lisp/cedet/semantic/wisent/js-wy.el new file mode 100644 index 00000000000..8d25b726605 --- /dev/null +++ b/lisp/cedet/semantic/wisent/js-wy.el @@ -0,0 +1,491 @@ +;;; semantic/wisent/js-wy.el --- Generated parser support file + +;; Copyright (C) 2005 Free Software Foundation, Inc. + +;; 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: +;; +;; This file was generated from the grammar file +;; semantic/wisent/wisent-javascript-jv.wy in the CEDET repository. + +;;; Code: +(require 'semantic/lex) + +;;; Prologue +;; + +;;; Declarations +;; +(defconst wisent-javascript-jv-wy--keyword-table + (semantic-lex-make-keyword-table + '(("if" . IF) + ("break" . BREAK) + ("continue" . CONTINUE) + ("else" . ELSE) + ("for" . FOR) + ("function" . FUNCTION) + ("this" . THIS) + ("return" . RETURN) + ("while" . WHILE) + ("void" . VOID_SYMBOL) + ("new" . NEW) + ("delete" . DELETE) + ("var" . VAR) + ("with" . WITH) + ("typeof" . TYPEOF) + ("in" . IN)) + '(("in" summary "in something") + ("typeof" summary "typeof ") + ("with" summary "with ") + ("var" summary "var <variablename> [= value];") + ("delete" summary "delete(<objectreference>) - Deletes the object.") + ("new" summary "new <objecttype> - Creates a new object.") + ("void" summary "Method return type: void <name> ...") + ("while" summary "while (<expr>) <stmt> | do <stmt> while (<expr>);") + ("return" summary "return [<expr>] ;") + ("this" summary "this") + ("function" summary "function declaration blah blah") + ("for" summary "for ([<init-expr>]; [<expr>]; [<update-expr>]) <stmt>") + ("else" summary "if (<expr>) <stmt> else <stmt>") + ("continue" summary "continue [<label>] ;") + ("break" summary "break [<label>] ;") + ("if" summary "if (<expr>) <stmt> [else <stmt>] (jv)"))) + "Table of language keywords.") + +(defconst wisent-javascript-jv-wy--token-table + (semantic-lex-make-type-table + '(("<no-type>" + (NULL_TOKEN) + (QUERY) + (TRUE) + (FALSE)) + ("number" + (NUMBER)) + ("string" + (STRING)) + ("symbol" + (VARIABLE)) + ("close-paren" + (CLOSE_SQ_BRACKETS . "]") + (END_BLOCK . "}") + (CLOSE_PARENTHESIS . ")")) + ("open-paren" + (OPEN_SQ_BRACKETS . "[") + (START_BLOCK . "{") + (OPEN_PARENTHESIS . "(")) + ("block" + (BRACK_BLOCK . "(OPEN_SQ_BRACKETS CLOSE_SQ_BRACKETS)") + (BRACE_BLOCK . "(START_BLOCK END_BLOCK)") + (PAREN_BLOCK . "(OPEN_PARENTHESIS CLOSE_PARENTHESIS)")) + ("punctuation" + (ONES_COMPLIMENT . "~") + (SEMICOLON . ";") + (LINE_TERMINATOR . "\n") + (LESS_THAN . "<") + (DOT . ".") + (COMMA . ",") + (COLON . ":") + (DIV . "/") + (DECREMENT . "--") + (INCREMENT . "++") + (PLUS_EQUALS . "+=") + (PLUS . "+") + (MULTIPLY_EQUALS . "*=") + (MULTIPLY . "*") + (MOD_EQUALS . "%=") + (MOD . "%") + (MINUS_EQUALS . "-=") + (MINUS . "-") + (LS_EQUAL . "<=") + (LOGICAL_NOT . "!!") + (LOGICAL_OR . "||") + (LOGICAL_AND . "&&") + (GT_EQUAL . ">=") + (GREATER_THAN . ">") + (EQUALS . "==") + (DIV_EQUALS . "/=") + (NOT_EQUAL . "!=") + (BITWISE_SHIFT_RIGHT_ZERO_FILL_EQUALS . ">>>=") + (BITWISE_SHIFT_RIGHT_ZERO_FILL . ">>>") + (BITWISE_SHIFT_RIGHT_EQUALS . ">>=") + (BITWISE_SHIFT_RIGHT . ">>") + (BITWISE_SHIFT_LEFT_EQUALS . "<<=") + (BITWISE_SHIFT_LEFT . "<<") + (BITWISE_OR_EQUALS . "|=") + (BITWISE_OR . "|") + (BITWISE_EXCLUSIVE_OR_EQUALS . "^=") + (BITWISE_EXCLUSIVE_OR . "^") + (BITWISE_AND_EQUALS . "&=") + (BITWISE_AND . "&") + (ASSIGN_SYMBOL . "="))) + '(("number" :declared t) + ("string" :declared t) + ("symbol" :declared t) + ("keyword" :declared t) + ("block" :declared t) + ("punctuation" :declared t))) + "Table of lexical tokens.") + +(defconst wisent-javascript-jv-wy--parse-table + (progn + (eval-when-compile + (require 'semantic/wisent/comp)) + (wisent-compile-grammar + '((ASSIGN_SYMBOL BITWISE_AND BITWISE_AND_EQUALS BITWISE_EXCLUSIVE_OR BITWISE_EXCLUSIVE_OR_EQUALS BITWISE_OR BITWISE_OR_EQUALS BITWISE_SHIFT_LEFT BITWISE_SHIFT_LEFT_EQUALS BITWISE_SHIFT_RIGHT BITWISE_SHIFT_RIGHT_EQUALS BITWISE_SHIFT_RIGHT_ZERO_FILL BITWISE_SHIFT_RIGHT_ZERO_FILL_EQUALS NOT_EQUAL DIV_EQUALS EQUALS GREATER_THAN GT_EQUAL LOGICAL_AND LOGICAL_OR LOGICAL_NOT LS_EQUAL MINUS MINUS_EQUALS MOD MOD_EQUALS MULTIPLY MULTIPLY_EQUALS PLUS PLUS_EQUALS INCREMENT DECREMENT DIV COLON COMMA DOT LESS_THAN LINE_TERMINATOR SEMICOLON ONES_COMPLIMENT PAREN_BLOCK BRACE_BLOCK BRACK_BLOCK OPEN_PARENTHESIS CLOSE_PARENTHESIS START_BLOCK END_BLOCK OPEN_SQ_BRACKETS CLOSE_SQ_BRACKETS IF BREAK CONTINUE ELSE FOR FUNCTION THIS RETURN WHILE VOID_SYMBOL NEW DELETE VAR WITH TYPEOF IN VARIABLE STRING NUMBER FALSE TRUE QUERY NULL_TOKEN) + ((left PLUS MINUS) + (left MULTIPLY DIV MOD) + (nonassoc FALSE) + (nonassoc HIGHER_THAN_FALSE) + (nonassoc ELSE) + (nonassoc LOWER_THAN_CLOSE_PARENTHESIS) + (nonassoc CLOSE_PARENTHESIS)) + (Program + ((SourceElement))) + (SourceElement + ((Statement)) + ((FunctionDeclaration))) + (Statement + ((Block)) + ((VariableStatement)) + ((EmptyStatement)) + ((ExpressionStatement)) + ((IfStatement)) + ((IterationExpression)) + ((ContinueStatement)) + ((BreakStatement)) + ((ReturnStatement)) + ((WithStatement))) + (FunctionDeclaration + ((FUNCTION VARIABLE FormalParameterListBlock Block) + (wisent-raw-tag + (semantic-tag-new-function $2 nil $3)))) + (FormalParameterListBlock + ((PAREN_BLOCK) + (semantic-parse-region + (car $region1) + (cdr $region1) + 'FormalParameterList 1))) + (FormalParameterList + ((OPEN_PARENTHESIS) + nil) + ((VARIABLE) + (wisent-raw-tag + (semantic-tag-new-variable $1 nil nil))) + ((CLOSE_PARENTHESIS) + nil) + ((COMMA) + nil)) + (StatementList + ((Statement)) + ((StatementList Statement))) + (Block + ((BRACE_BLOCK))) + (BlockExpand + ((START_BLOCK StatementList END_BLOCK)) + ((START_BLOCK END_BLOCK))) + (VariableStatement + ((VAR VariableDeclarationList SEMICOLON) + (wisent-raw-tag + (semantic-tag-new-variable $2 nil nil)))) + (VariableDeclarationList + ((VariableDeclaration) + (list $1)) + ((VariableDeclarationList COMMA VariableDeclaration) + (append $1 + (list $3)))) + (VariableDeclaration + ((VARIABLE) + (append + (list $1 nil) + $region)) + ((VARIABLE Initializer) + (append + (cons $1 $2) + $region))) + (Initializer + ((ASSIGN_SYMBOL AssignmentExpression) + (list $2))) + (EmptyStatement + ((SEMICOLON))) + (ExpressionStatement + ((Expression SEMICOLON))) + (IfStatement + ((IF OPEN_PARENTHESIS Expression CLOSE_PARENTHESIS Statement) + [HIGHER_THAN_FALSE]) + ((IF OPEN_PARENTHESIS Expression CLOSE_PARENTHESIS Statement ELSE Statement)) + ((IF OPEN_PARENTHESIS FALSE CLOSE_PARENTHESIS Statement)) + ((IF OPEN_PARENTHESIS LeftHandSideExpression AssignmentOperator AssignmentExpression CLOSE_PARENTHESIS Statement))) + (IterationExpression + ((WHILE OPEN_PARENTHESIS Expression CLOSE_PARENTHESIS Statement) + [HIGHER_THAN_FALSE]) + ((WHILE OPEN_PARENTHESIS FALSE CLOSE_PARENTHESIS Statement)) + ((WHILE OPEN_PARENTHESIS LeftHandSideExpression AssignmentOperator AssignmentExpression CLOSE_PARENTHESIS Statement)) + ((FOR OPEN_PARENTHESIS OptionalExpression SEMICOLON OptionalExpression SEMICOLON OptionalExpression CLOSE_PARENTHESIS Statement)) + ((FOR OPEN_PARENTHESIS VAR VariableDeclarationList SEMICOLON OptionalExpression SEMICOLON OptionalExpression CLOSE_PARENTHESIS Statement)) + ((FOR OPEN_PARENTHESIS LeftHandSideExpression IN Expression CLOSE_PARENTHESIS Statement)) + ((FOR OPEN_PARENTHESIS VAR VARIABLE OptionalInitializer IN Expression CLOSE_PARENTHESIS Statement))) + (ContinueStatement + ((CONTINUE SEMICOLON))) + (BreakStatement + ((BREAK SEMICOLON))) + (ReturnStatement + ((RETURN Expression SEMICOLON)) + ((RETURN SEMICOLON))) + (WithStatement + ((WITH OPEN_PARENTHESIS Expression CLOSE_PARENTHESIS Statement))) + (OptionalInitializer + ((Initializer)) + (nil)) + (PrimaryExpression + ((THIS)) + ((VARIABLE)) + ((NUMBER)) + ((STRING)) + ((NULL_TOKEN)) + ((TRUE)) + ((FALSE)) + ((OPEN_PARENTHESIS Expression CLOSE_PARENTHESIS))) + (MemberExpression + ((PrimaryExpression)) + ((MemberExpression OPEN_SQ_BRACKETS Expression CLOSE_SQ_BRACKETS)) + ((MemberExpression DOT VARIABLE)) + ((NEW MemberExpression Arguments))) + (NewExpression + ((MemberExpression)) + ((NEW NewExpression))) + (CallExpression + ((MemberExpression Arguments)) + ((CallExpression Arguments)) + ((CallExpression OPEN_SQ_BRACKETS Expression CLOSE_SQ_BRACKETS)) + ((CallExpression DOT VARIABLE))) + (Arguments + ((OPEN_PARENTHESIS CLOSE_PARENTHESIS)) + ((OPEN_PARENTHESIS ArgumentList CLOSE_PARENTHESIS))) + (ArgumentList + ((AssignmentExpression)) + ((ArgumentList COMMA AssignmentExpression))) + (LeftHandSideExpression + ((NewExpression)) + ((CallExpression))) + (PostfixExpression + ((LeftHandSideExpression)) + ((LeftHandSideExpression INCREMENT)) + ((LeftHandSideExpression DECREMENT))) + (UnaryExpression + ((PostfixExpression)) + ((DELETE UnaryExpression)) + ((VOID_SYMBOL UnaryExpression)) + ((TYPEOF UnaryExpression)) + ((INCREMENT UnaryExpression)) + ((DECREMENT UnaryExpression)) + ((PLUS UnaryExpression)) + ((MINUS UnaryExpression)) + ((ONES_COMPLIMENT UnaryExpression)) + ((LOGICAL_NOT UnaryExpression))) + (MultiplicativeExpression + ((UnaryExpression)) + ((MultiplicativeExpression MULTIPLY UnaryExpression)) + ((MultiplicativeExpression DIV UnaryExpression)) + ((MultiplicativeExpression MOD UnaryExpression))) + (AdditiveExpression + ((MultiplicativeExpression)) + ((AdditiveExpression PLUS MultiplicativeExpression)) + ((AdditiveExpression MINUS MultiplicativeExpression))) + (ShiftExpression + ((AdditiveExpression)) + ((ShiftExpression BITWISE_SHIFT_LEFT AdditiveExpression)) + ((ShiftExpression BITWISE_SHIFT_RIGHT AdditiveExpression)) + ((ShiftExpression BITWISE_SHIFT_RIGHT_ZERO_FILL AdditiveExpression))) + (RelationalExpression + ((ShiftExpression)) + ((RelationalExpression LESS_THAN ShiftExpression)) + ((RelationalExpression GREATER_THAN ShiftExpression)) + ((RelationalExpression LS_EQUAL ShiftExpression)) + ((RelationalExpression GT_EQUAL ShiftExpression))) + (EqualityExpression + ((RelationalExpression)) + ((EqualityExpression EQUALS RelationalExpression)) + ((EqualityExpression NOT_EQUAL RelationalExpression))) + (BitwiseANDExpression + ((EqualityExpression)) + ((BitwiseANDExpression BITWISE_AND EqualityExpression))) + (BitwiseXORExpression + ((BitwiseANDExpression)) + ((BitwiseXORExpression BITWISE_EXCLUSIVE_OR BitwiseANDExpression))) + (BitwiseORExpression + ((BitwiseXORExpression)) + ((BitwiseORExpression BITWISE_OR BitwiseXORExpression))) + (LogicalANDExpression + ((BitwiseORExpression)) + ((LogicalANDExpression LOGICAL_AND BitwiseORExpression))) + (LogicalORExpression + ((LogicalANDExpression)) + ((LogicalORExpression LOGICAL_OR LogicalANDExpression))) + (ConditionalExpression + ((LogicalORExpression)) + ((LogicalORExpression QUERY AssignmentExpression COLON AssignmentExpression))) + (AssignmentExpression + ((ConditionalExpression)) + ((LeftHandSideExpression AssignmentOperator AssignmentExpression) + [LOWER_THAN_CLOSE_PARENTHESIS])) + (AssignmentOperator + ((ASSIGN_SYMBOL)) + ((MULTIPLY_EQUALS)) + ((DIV_EQUALS)) + ((MOD_EQUALS)) + ((PLUS_EQUALS)) + ((MINUS_EQUALS)) + ((BITWISE_SHIFT_LEFT_EQUALS)) + ((BITWISE_SHIFT_RIGHT_EQUALS)) + ((BITWISE_SHIFT_RIGHT_ZERO_FILL_EQUALS)) + ((BITWISE_AND_EQUALS)) + ((BITWISE_EXCLUSIVE_OR_EQUALS)) + ((BITWISE_OR_EQUALS))) + (Expression + ((AssignmentExpression)) + ((Expression COMMA AssignmentExpression))) + (OptionalExpression + ((Expression)) + (nil))) + '(Program FormalParameterList))) + "Parser table.") + +(defun wisent-javascript-jv-wy--install-parser () + "Setup the Semantic Parser." + (semantic-install-function-overrides + '((parse-stream . wisent-parse-stream))) + (setq semantic-parser-name "LALR" + semantic--parse-table wisent-javascript-jv-wy--parse-table + semantic-debug-parser-source "wisent-javascript-jv.wy" + semantic-flex-keywords-obarray wisent-javascript-jv-wy--keyword-table + semantic-lex-types-obarray wisent-javascript-jv-wy--token-table) + ;; Collect unmatched syntax lexical tokens + (semantic-make-local-hook 'wisent-discarding-token-functions) + (add-hook 'wisent-discarding-token-functions + 'wisent-collect-unmatched-syntax nil t)) + + +;;; Analyzers +;; +(define-lex-keyword-type-analyzer wisent-javascript-jv-wy--<keyword>-keyword-analyzer + "keyword analyzer for <keyword> tokens." + "\\(\\sw\\|\\s_\\)+") + +(define-lex-block-type-analyzer wisent-javascript-jv-wy--<block>-block-analyzer + "block analyzer for <block> tokens." + "\\s(\\|\\s)" + '((("(" OPEN_PARENTHESIS PAREN_BLOCK) + ("{" START_BLOCK BRACE_BLOCK) + ("[" OPEN_SQ_BRACKETS BRACK_BLOCK)) + (")" CLOSE_PARENTHESIS) + ("}" END_BLOCK) + ("]" CLOSE_SQ_BRACKETS)) + ) + +(define-lex-regex-type-analyzer wisent-javascript-jv-wy--<symbol>-regexp-analyzer + "regexp analyzer for <symbol> tokens." + "\\(\\sw\\|\\s_\\)+" + nil + 'VARIABLE) + +(define-lex-sexp-type-analyzer wisent-javascript-jv-wy--<string>-sexp-analyzer + "sexp analyzer for <string> tokens." + "\\s\"" + 'STRING) + +(define-lex-regex-type-analyzer wisent-javascript-jv-wy--<number>-regexp-analyzer + "regexp analyzer for <number> tokens." + semantic-lex-number-expression + nil + 'NUMBER) + +(define-lex-string-type-analyzer wisent-javascript-jv-wy--<punctuation>-string-analyzer + "string analyzer for <punctuation> tokens." + "\\(\\s.\\|\\s$\\|\\s'\\)+" + '((ONES_COMPLIMENT . "~") + (SEMICOLON . ";") + (LINE_TERMINATOR . "\n") + (LESS_THAN . "<") + (DOT . ".") + (COMMA . ",") + (COLON . ":") + (DIV . "/") + (DECREMENT . "--") + (INCREMENT . "++") + (PLUS_EQUALS . "+=") + (PLUS . "+") + (MULTIPLY_EQUALS . "*=") + (MULTIPLY . "*") + (MOD_EQUALS . "%=") + (MOD . "%") + (MINUS_EQUALS . "-=") + (MINUS . "-") + (LS_EQUAL . "<=") + (LOGICAL_NOT . "!!") + (LOGICAL_OR . "||") + (LOGICAL_AND . "&&") + (GT_EQUAL . ">=") + (GREATER_THAN . ">") + (EQUALS . "==") + (DIV_EQUALS . "/=") + (NOT_EQUAL . "!=") + (BITWISE_SHIFT_RIGHT_ZERO_FILL_EQUALS . ">>>=") + (BITWISE_SHIFT_RIGHT_ZERO_FILL . ">>>") + (BITWISE_SHIFT_RIGHT_EQUALS . ">>=") + (BITWISE_SHIFT_RIGHT . ">>") + (BITWISE_SHIFT_LEFT_EQUALS . "<<=") + (BITWISE_SHIFT_LEFT . "<<") + (BITWISE_OR_EQUALS . "|=") + (BITWISE_OR . "|") + (BITWISE_EXCLUSIVE_OR_EQUALS . "^=") + (BITWISE_EXCLUSIVE_OR . "^") + (BITWISE_AND_EQUALS . "&=") + (BITWISE_AND . "&") + (ASSIGN_SYMBOL . "=")) + 'punctuation) + + +;;; Epilogue +;; +;;here something like: +;;(define-lex wisent-java-tags-lexer +;; should go +(define-lex javascript-lexer-jv +"javascript thingy" +;;std stuff + semantic-lex-ignore-whitespace + semantic-lex-ignore-newline + semantic-lex-ignore-comments + + ;;stuff generated from the wy file(one for each "type" declaration) + wisent-javascript-jv-wy--<number>-regexp-analyzer + wisent-javascript-jv-wy--<string>-sexp-analyzer + + wisent-javascript-jv-wy--<keyword>-keyword-analyzer + + wisent-javascript-jv-wy--<symbol>-regexp-analyzer + wisent-javascript-jv-wy--<punctuation>-string-analyzer + wisent-javascript-jv-wy--<block>-block-analyzer + + + ;;;;more std stuff + semantic-lex-default-action + ) + +(provide 'semantic/wisent/js-wy) + +;;; semantic/wisent/js-wy.el ends here diff --git a/lisp/cedet/semantic/wisent/wisent.el b/lisp/cedet/semantic/wisent/wisent.el new file mode 100644 index 00000000000..e3614d8b591 --- /dev/null +++ b/lisp/cedet/semantic/wisent/wisent.el @@ -0,0 +1,479 @@ +;;; semantic/wisent/wisent.el --- GNU Bison for Emacs - Runtime + +;;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009 +;;; Free Software Foundation, Inc. + +;; Author: David Ponce <david@dponce.com> +;; Maintainer: David Ponce <david@dponce.com> +;; Created: 30 January 2002 +;; 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: +;; +;; Parser engine and runtime of Wisent. +;; +;; Wisent (the European Bison ;-) is an Elisp implementation of the +;; GNU Compiler Compiler Bison. The Elisp code is a port of the C +;; code of GNU Bison 1.28 & 1.31. +;; +;; For more details on the basic concepts for understanding Wisent, +;; read the Bison manual ;) +;; +;; For more details on Wisent itself read the Wisent manual. + +;;; History: +;; + +;;; Code: + +(defgroup wisent nil + " + /\\_.-^^^-._/\\ The GNU + \\_ _/ + ( `o ` (European ;-) Bison + \\ ` / + ( D ,¨ for Emacs! + ` ~ ,¨ + `\"\"" + :group 'semantic) + + +;;;; ------------- +;;;; Runtime stuff +;;;; ------------- + +;;; Compatibility +(eval-and-compile + (if (fboundp 'char-valid-p) + (defalias 'wisent-char-p 'char-valid-p) + (defalias 'wisent-char-p 'char-or-char-int-p))) + +;;; Printed representation of terminals and nonterminals +(defconst wisent-escape-sequence-strings + '( + (?\a . "'\\a'") ; C-g + (?\b . "'\\b'") ; backspace, BS, C-h + (?\t . "'\\t'") ; tab, TAB, C-i + (?\n . "'\\n'") ; newline, C-j + (?\v . "'\\v'") ; vertical tab, C-k + (?\f . "'\\f'") ; formfeed character, C-l + (?\r . "'\\r'") ; carriage return, RET, C-m + (?\e . "'\\e'") ; escape character, ESC, C-[ + (?\\ . "'\\'") ; backslash character, \ + (?\d . "'\\d'") ; delete character, DEL + ) + "Printed representation of usual escape sequences.") + +(defsubst wisent-item-to-string (item) + "Return a printed representation of ITEM. +ITEM can be a nonterminal or terminal symbol, or a character literal." + (if (wisent-char-p item) + (or (cdr (assq item wisent-escape-sequence-strings)) + (format "'%c'" item)) + (symbol-name item))) + +(defsubst wisent-token-to-string (token) + "Return a printed representation of lexical token TOKEN." + (format "%s%s(%S)" (wisent-item-to-string (car token)) + (if (nth 2 token) (format "@%s" (nth 2 token)) "") + (nth 1 token))) + +;;; Special symbols +(defconst wisent-eoi-term '$EOI + "End Of Input token.") + +(defconst wisent-error-term 'error + "Error recovery token.") + +(defconst wisent-accept-tag 'accept + "Accept result after input successfully parsed.") + +(defconst wisent-error-tag 'error + "Process a syntax error.") + +;;; Special functions +(defun wisent-automaton-p (obj) + "Return non-nil if OBJ is a LALR automaton. +If OBJ is a symbol check its value." + (and obj (symbolp obj) (boundp obj) + (setq obj (symbol-value obj))) + (and (vectorp obj) (= 4 (length obj)) + (vectorp (aref obj 0)) (vectorp (aref obj 1)) + (= (length (aref obj 0)) (length (aref obj 1))) + (listp (aref obj 2)) (vectorp (aref obj 3)))) + +(defsubst wisent-region (&rest positions) + "Return the start/end positions of the region including POSITIONS. +Each element of POSITIONS is a pair (START-POS . END-POS) or nil. The +returned value is the pair (MIN-START-POS . MAX-END-POS) or nil if no +POSITIONS are available." + (let ((pl (delq nil positions))) + (if pl + (cons (apply #'min (mapcar #'car pl)) + (apply #'max (mapcar #'cdr pl)))))) + +;;; Reporting +(defvar wisent-parse-verbose-flag nil + "*Non-nil means to issue more messages while parsing.") + +(defun wisent-parse-toggle-verbose-flag () + "Toggle whether to issue more messages while parsing." + (interactive) + (setq wisent-parse-verbose-flag (not wisent-parse-verbose-flag)) + (when (interactive-p) + (message "More messages while parsing %sabled" + (if wisent-parse-verbose-flag "en" "dis")))) + +(defsubst wisent-message (string &rest args) + "Print a one-line message if `wisent-parse-verbose-flag' is set. +Pass STRING and ARGS arguments to `message'." + (and wisent-parse-verbose-flag + (apply 'message string args))) + +;;;; -------------------- +;;;; The LR parser engine +;;;; -------------------- + +(defcustom wisent-parse-max-stack-size 500 + "The parser stack size." + :type 'integer + :group 'wisent) + +(defcustom wisent-parse-max-recover 3 + "Number of tokens to shift before turning off error status." + :type 'integer + :group 'wisent) + +(defvar wisent-discarding-token-functions nil + "List of functions to be called when discarding a lexical token. +These functions receive the lexical token discarded. +When the parser encounters unexpected tokens, it can discards them, +based on what directed by error recovery rules. Either when the +parser reads tokens until one is found that can be shifted, or when an +semantic action calls the function `wisent-skip-token' or +`wisent-skip-block'. +For language specific hooks, make sure you define this as a local +hook.") + +(defvar wisent-pre-parse-hook nil + "Normal hook run just before entering the LR parser engine.") + +(defvar wisent-post-parse-hook nil + "Normal hook run just after the LR parser engine terminated.") + +(defvar wisent-loop nil + "The current parser action. +Stop parsing when set to nil. +This variable only has meaning in the scope of `wisent-parse'.") + +(defvar wisent-nerrs nil + "The number of parse errors encountered so far.") + +(defvar wisent-lookahead nil + "The lookahead lexical token. +This value is non-nil if the parser terminated because of an +unrecoverable error.") + +;; Variables and macros that are useful in semantic actions. +(defvar wisent-parse-lexer-function nil + "The user supplied lexer function. +This function don't have arguments. +This variable only has meaning in the scope of `wisent-parse'.") + +(defvar wisent-parse-error-function nil + "The user supplied error function. +This function must accept one argument, a message string. +This variable only has meaning in the scope of `wisent-parse'.") + +(defvar wisent-input nil + "The last token read. +This variable only has meaning in the scope of `wisent-parse'.") + +(defvar wisent-recovering nil + "Non-nil means that the parser is recovering. +This variable only has meaning in the scope of `wisent-parse'.") + +;; Variables that only have meaning in the scope of a semantic action. +;; These global definitions avoid byte-compiler warnings. +(defvar $region nil) +(defvar $nterm nil) +(defvar $action nil) + +(defmacro wisent-lexer () + "Obtain the next terminal in input." + '(funcall wisent-parse-lexer-function)) + +(defmacro wisent-error (msg) + "Call the user supplied error reporting function with message MSG." + `(funcall wisent-parse-error-function ,msg)) + +(defmacro wisent-errok () + "Resume generating error messages immediately for subsequent syntax errors. +This is useful primarily in error recovery semantic actions." + '(setq wisent-recovering nil)) + +(defmacro wisent-clearin () + "Discard the current lookahead token. +This will cause a new lexical token to be read. +This is useful primarily in error recovery semantic actions." + '(setq wisent-input nil)) + +(defmacro wisent-abort () + "Abort parsing and save the lookahead token. +This is useful primarily in error recovery semantic actions." + '(setq wisent-lookahead wisent-input + wisent-loop nil)) + +(defmacro wisent-set-region (start end) + "Change the region of text matched by the current nonterminal. +START and END are respectively the beginning and end positions of the +region. If START or END values are not a valid positions the region +is set to nil." + `(setq $region (and (number-or-marker-p ,start) + (number-or-marker-p ,end) + (cons ,start ,end)))) + +(defun wisent-skip-token () + "Skip the lookahead token in order to resume parsing. +Return nil. +Must be used in error recovery semantic actions." + (if (eq (car wisent-input) wisent-eoi-term) + ;; Does nothing at EOI to avoid infinite recovery loop. + nil + (wisent-message "%s: skip %s" $action + (wisent-token-to-string wisent-input)) + (run-hook-with-args + 'wisent-discarding-token-functions wisent-input) + (wisent-clearin) + (wisent-errok))) + +(defun wisent-skip-block (&optional bounds) + "Safely skip a parenthesized block in order to resume parsing. +Return nil. +Must be used in error recovery semantic actions. +Optional argument BOUNDS is a pair (START . END) which indicates where +the parenthesized block starts. Typically the value of a `$regionN' +variable, where `N' is the the Nth element of the current rule +components that match the block beginning. It defaults to the value +of the `$region' variable." + (let ((start (car (or bounds $region))) + end input) + (if (not (number-or-marker-p start)) + ;; No nonterminal region available, skip the lookahead token. + (wisent-skip-token) + ;; Try to skip a block. + (if (not (setq end (save-excursion + (goto-char start) + (and (looking-at "\\s(") + (condition-case nil + (1- (scan-lists (point) 1 0)) + (error nil)))))) + ;; Not actually a block, skip the lookahead token. + (wisent-skip-token) + ;; OK to safely skip the block, so read input until a matching + ;; close paren or EOI is encountered. + (setq input wisent-input) + (while (and (not (eq (car input) wisent-eoi-term)) + (< (nth 2 input) end)) + (run-hook-with-args + 'wisent-discarding-token-functions input) + (setq input (wisent-lexer))) + (wisent-message "%s: in enclosing block, skip from %s to %s" + $action + (wisent-token-to-string wisent-input) + (wisent-token-to-string input)) + (if (eq (car wisent-input) wisent-eoi-term) + ;; Does nothing at EOI to avoid infinite recovery loop. + nil + (wisent-clearin) + (wisent-errok)) + ;; Set end of $region to end of block. + (wisent-set-region (car $region) (1+ end)) + nil)))) + +;;; Core parser engine +(defsubst wisent-production-bounds (stack i j) + "Determine the start and end locations of a production value. +Return a pair (START . END), where START is the first available start +location, and END the last available end location, in components +values of the rule currently reduced. +Return nil when no component location is available. +STACK is the parser stack. +I and J are the indices in STACK of respectively the value of the +first and last components of the current rule. +This function is for internal use by semantic actions' generated +lambda-expression." + (let ((f (cadr (aref stack i))) + (l (cddr (aref stack j)))) + (while (/= i j) + (cond + ((not f) (setq f (cadr (aref stack (setq i (+ i 2)))))) + ((not l) (setq l (cddr (aref stack (setq j (- j 2)))))) + ((setq i j)))) + (and f l (cons f l)))) + +(defmacro wisent-parse-action (i al) + "Return the next parser action. +I is a token item number and AL is the list of (item . action) +available at current state. The first element of AL contains the +default action for this state." + `(cdr (or (assq ,i ,al) (car ,al)))) + +(defsubst wisent-parse-start (start starts) + "Return the first lexical token to shift for START symbol. +STARTS is the table of allowed start symbols or nil if the LALR +automaton has only one entry point." + (if (null starts) + ;; Only one entry point, return the first lexical token + ;; available in input. + (wisent-lexer) + ;; Multiple start symbols defined, return the internal lexical + ;; token associated to START. By default START is the first + ;; nonterminal defined in STARTS. + (let ((token (cdr (if start (assq start starts) (car starts))))) + (if token + (list token (symbol-name token)) + (error "Invalid start symbol %s" start))))) + +(defun wisent-parse (automaton lexer &optional error start) + "Parse input using the automaton specified in AUTOMATON. + +- AUTOMATON is an LALR(1) automaton generated by + `wisent-compile-grammar'. + +- LEXER is a function with no argument called by the parser to obtain + the next terminal (token) in input. + +- ERROR is an optional reporting function called when a parse error + occurs. It receives a message string to report. It defaults to the + function `wisent-message'. + +- START specify the start symbol (nonterminal) used by the parser as + its goal. It defaults to the start symbol defined in the grammar + \(see also `wisent-compile-grammar')." + (run-hooks 'wisent-pre-parse-hook) + (let* ((actions (aref automaton 0)) + (gotos (aref automaton 1)) + (starts (aref automaton 2)) + (stack (make-vector wisent-parse-max-stack-size nil)) + (sp 0) + (wisent-loop t) + (wisent-parse-error-function (or error 'wisent-message)) + (wisent-parse-lexer-function lexer) + (wisent-recovering nil) + (wisent-input (wisent-parse-start start starts)) + state tokid choices choice) + (setq wisent-nerrs 0 ;; Reset parse error counter + wisent-lookahead nil) ;; and lookahead token + (aset stack 0 0) ;; Initial state + (while wisent-loop + (setq state (aref stack sp) + tokid (car wisent-input) + wisent-loop (wisent-parse-action tokid (aref actions state))) + (cond + + ;; Input successfully parsed + ;; ------------------------- + ((eq wisent-loop wisent-accept-tag) + (setq wisent-loop nil)) + + ;; Syntax error in input + ;; --------------------- + ((eq wisent-loop wisent-error-tag) + ;; Report this error if not already recovering from an error. + (setq choices (aref actions state)) + (or wisent-recovering + (wisent-error + (format "Syntax error, unexpected %s, expecting %s" + (wisent-token-to-string wisent-input) + (mapconcat 'wisent-item-to-string + (delq wisent-error-term + (mapcar 'car (cdr choices))) + ", ")))) + ;; Increment the error counter + (setq wisent-nerrs (1+ wisent-nerrs)) + ;; If just tried and failed to reuse lookahead token after an + ;; error, discard it. + (if (eq wisent-recovering wisent-parse-max-recover) + (if (eq tokid wisent-eoi-term) + (wisent-abort) ;; Terminate if at end of input. + (wisent-message "Error recovery: skip %s" + (wisent-token-to-string wisent-input)) + (run-hook-with-args + 'wisent-discarding-token-functions wisent-input) + (setq wisent-input (wisent-lexer))) + + ;; Else will try to reuse lookahead token after shifting the + ;; error token. + + ;; Each real token shifted decrements this. + (setq wisent-recovering wisent-parse-max-recover) + ;; Pop the value/state stack to see if an action associated + ;; to special terminal symbol 'error exists. + (while (and (>= sp 0) + (not (and (setq state (aref stack sp) + choices (aref actions state) + choice (assq wisent-error-term choices)) + (natnump (cdr choice))))) + (setq sp (- sp 2))) + + (if (not choice) + ;; No 'error terminal was found. Just terminate. + (wisent-abort) + ;; Try to recover and continue parsing. + ;; Shift the error terminal. + (setq state (cdr choice) ; new state + sp (+ sp 2)) + (aset stack (1- sp) nil) ; push value + (aset stack sp state) ; push new state + ;; Adjust input to error recovery state. Unless 'error + ;; triggers a reduction, eat the input stream until an + ;; expected terminal symbol is found, or EOI is reached. + (if (cdr (setq choices (aref actions state))) + (while (not (or (eq (car wisent-input) wisent-eoi-term) + (assq (car wisent-input) choices))) + (wisent-message "Error recovery: skip %s" + (wisent-token-to-string wisent-input)) + (run-hook-with-args + 'wisent-discarding-token-functions wisent-input) + (setq wisent-input (wisent-lexer))))))) + + ;; Shift current token on top of the stack + ;; --------------------------------------- + ((natnump wisent-loop) + ;; Count tokens shifted since error; after + ;; `wisent-parse-max-recover', turn off error status. + (setq wisent-recovering (and (natnump wisent-recovering) + (> wisent-recovering 1) + (1- wisent-recovering))) + (setq sp (+ sp 2)) + (aset stack (1- sp) (cdr wisent-input)) + (aset stack sp wisent-loop) + (setq wisent-input (wisent-lexer))) + + ;; Reduce by rule (call semantic action) + ;; ------------------------------------- + (t + (setq sp (funcall wisent-loop stack sp gotos)) + (or wisent-input (setq wisent-input (wisent-lexer)))))) + (run-hooks 'wisent-post-parse-hook) + (car (aref stack 1)))) + +(provide 'semantic/wisent/wisent) + +;;; semantic/wisent/wisent.el ends here |