summaryrefslogtreecommitdiff
path: root/lisp/cedet/semantic/ctxt.el
diff options
context:
space:
mode:
authorChong Yidong <cyd@stupidchicken.com>2009-08-28 19:18:35 +0000
committerChong Yidong <cyd@stupidchicken.com>2009-08-28 19:18:35 +0000
commit1bd955357097f15170e159d24b4e20b3173b8335 (patch)
tree78dad743284d2f2daee6a139196e32bc98180d5f /lisp/cedet/semantic/ctxt.el
parent994e5ceab00ab6f3127ca3b2f5eef1dda375e1de (diff)
downloademacs-1bd955357097f15170e159d24b4e20b3173b8335.tar.gz
emacs-1bd955357097f15170e159d24b4e20b3173b8335.tar.bz2
emacs-1bd955357097f15170e159d24b4e20b3173b8335.zip
cedet/semantic/ctxt.el, cedet/semantic/db-find.el,
cedet/semantic/db-ref.el, cedet/semantic/find.el, cedet/semantic/format.el, cedet/semantic/sort.el: New files.
Diffstat (limited to 'lisp/cedet/semantic/ctxt.el')
-rw-r--r--lisp/cedet/semantic/ctxt.el613
1 files changed, 613 insertions, 0 deletions
diff --git a/lisp/cedet/semantic/ctxt.el b/lisp/cedet/semantic/ctxt.el
new file mode 100644
index 00000000000..270b9964031
--- /dev/null
+++ b/lisp/cedet/semantic/ctxt.el
@@ -0,0 +1,613 @@
+;;; 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)
+(eval-when-compile (require 'semantic/db))
+
+;;; 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
+(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."
+ (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
+ (working-status-forms
+ (semantic-parser-working-message "Local")
+ "done"
+ (save-excursion
+ (if point (goto-char point))
+ (let* ((semantic-working-type nil)
+ ;; Disable parsing messages
+ (working-status-dynamic-type nil)
+ (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)
+ ))))
+
+(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)
+
+;;; semantic-ctxt.el ends here