mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-01 11:14:55 +00:00
846 lines
32 KiB
EmacsLisp
846 lines
32 KiB
EmacsLisp
|
;;; semantic.el --- Semantic buffer evaluator.
|
|||
|
|
|||
|
;;; 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:
|
|||
|
;;
|
|||
|
;; API for providing the semantic content of a buffer.
|
|||
|
;;
|
|||
|
;; The semantic API provides an interface to a series of different parser
|
|||
|
;; implementations. Each parser outputs a parse tree in a similar format
|
|||
|
;; designed to handle typical functional and object oriented languages.
|
|||
|
|
|||
|
(eval-and-compile
|
|||
|
;; Other package depend on this value at compile time via inversion.
|
|||
|
(defvar semantic-version "2.0pre7"
|
|||
|
"Current version of Semantic."))
|
|||
|
|
|||
|
;; (require 'working)
|
|||
|
(require 'assoc)
|
|||
|
(require 'semantic-tag)
|
|||
|
(require 'semantic-lex)
|
|||
|
|
|||
|
(declare-function inversion-test "inversion")
|
|||
|
|
|||
|
(defun semantic-require-version (major minor &optional beta)
|
|||
|
"Non-nil if this version of semantic does not satisfy a specific version.
|
|||
|
Arguments can be:
|
|||
|
|
|||
|
(MAJOR MINOR &optional BETA)
|
|||
|
|
|||
|
Values MAJOR and MINOR must be integers. BETA can be an integer, or
|
|||
|
excluded if a released version is required.
|
|||
|
|
|||
|
It is assumed that if the current version is newer than that specified,
|
|||
|
everything passes. Exceptions occur when known incompatibilities are
|
|||
|
introduced."
|
|||
|
(require 'inversion)
|
|||
|
(inversion-test 'semantic
|
|||
|
(concat major "." minor
|
|||
|
(when beta (concat "beta" beta)))))
|
|||
|
|
|||
|
(defgroup semantic nil
|
|||
|
"Parser Generator and parser framework."
|
|||
|
:group 'lisp)
|
|||
|
|
|||
|
(defgroup semantic-faces nil
|
|||
|
"Faces used for Semantic enabled tools."
|
|||
|
:group 'semantic)
|
|||
|
|
|||
|
(require 'semantic-fw)
|
|||
|
|
|||
|
;;; Code:
|
|||
|
;;
|
|||
|
|
|||
|
;;; Variables and Configuration
|
|||
|
;;
|
|||
|
(defvar semantic--parse-table nil
|
|||
|
"Variable that defines how to parse top level items in a buffer.
|
|||
|
This variable is for internal use only, and its content depends on the
|
|||
|
external parser used.")
|
|||
|
(make-variable-buffer-local 'semantic--parse-table)
|
|||
|
(semantic-varalias-obsolete 'semantic-toplevel-bovine-table
|
|||
|
'semantic--parse-table)
|
|||
|
|
|||
|
(defvar semantic-symbol->name-assoc-list
|
|||
|
'((type . "Types")
|
|||
|
(variable . "Variables")
|
|||
|
(function . "Functions")
|
|||
|
(include . "Dependencies")
|
|||
|
(package . "Provides"))
|
|||
|
"Association between symbols returned, and a string.
|
|||
|
The string is used to represent a group of objects of the given type.
|
|||
|
It is sometimes useful for a language to use a different string
|
|||
|
in place of the default, even though that language will still
|
|||
|
return a symbol. For example, Java return's includes, but the
|
|||
|
string can be replaced with `Imports'.")
|
|||
|
(make-variable-buffer-local 'semantic-symbol->name-assoc-list)
|
|||
|
|
|||
|
(defvar semantic-symbol->name-assoc-list-for-type-parts nil
|
|||
|
"Like `semantic-symbol->name-assoc-list' for type parts.
|
|||
|
Some tags that have children (see `semantic-tag-children-compatibility')
|
|||
|
will want to define the names of classes of tags differently than at
|
|||
|
the top level. For example, in C++, a Function may be called a
|
|||
|
Method. In addition, there may be new types of tags that exist only
|
|||
|
in classes, such as protection labels.")
|
|||
|
(make-variable-buffer-local 'semantic-symbol->name-assoc-list-for-type-parts)
|
|||
|
|
|||
|
(defvar semantic-case-fold nil
|
|||
|
"Value for `case-fold-search' when parsing.")
|
|||
|
(make-variable-buffer-local 'semantic-case-fold)
|
|||
|
|
|||
|
(defvar semantic-expand-nonterminal nil
|
|||
|
"Function to call for each nonterminal production.
|
|||
|
Return a list of non-terminals derived from the first argument, 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 the definition
|
|||
|
int a, b;
|
|||
|
is easily parsed into one tag. 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-expand-nonterminal)
|
|||
|
|
|||
|
(defvar semantic--buffer-cache nil
|
|||
|
"A cache of the fully parsed buffer.
|
|||
|
If no significant changes have been made (based on the state) then
|
|||
|
this is returned instead of re-parsing the buffer.
|
|||
|
|
|||
|
DO NOT USE THIS VARIABLE IN PROGRAMS.
|
|||
|
|
|||
|
If you need a tag list, use `semantic-fetch-tags'. If you need the
|
|||
|
cached values for some reason, chances are you can, add a hook to
|
|||
|
`semantic-after-toplevel-cache-change-hook'.")
|
|||
|
(make-variable-buffer-local 'semantic--buffer-cache)
|
|||
|
(semantic-varalias-obsolete 'semantic-toplevel-bovine-cache
|
|||
|
'semantic--buffer-cache)
|
|||
|
|
|||
|
(defvar semantic-unmatched-syntax-cache nil
|
|||
|
"A cached copy of unmatched syntax tokens.")
|
|||
|
(make-variable-buffer-local 'semantic-unmatched-syntax-cache)
|
|||
|
|
|||
|
(defvar semantic-unmatched-syntax-cache-check nil
|
|||
|
"Non nil if the unmatched syntax cache is out of date.
|
|||
|
This is tracked with `semantic-change-function'.")
|
|||
|
(make-variable-buffer-local 'semantic-unmatched-syntax-cache-check)
|
|||
|
|
|||
|
(defvar semantic-edits-are-safe nil
|
|||
|
"When non-nil, modifications do not require a reparse.
|
|||
|
This prevents tags from being marked dirty, and it prevents top level
|
|||
|
edits from causing a cache check.
|
|||
|
Use this when writing programs that could cause a full reparse, but
|
|||
|
will not change the tag structure, such as adding or updating
|
|||
|
`top-level' comments.")
|
|||
|
|
|||
|
(defvar semantic-unmatched-syntax-hook nil
|
|||
|
"Hooks run when semantic detects syntax not matched in a grammar.
|
|||
|
Each individual piece of syntax (such as a symbol or punctuation
|
|||
|
character) is called with this hook when it doesn't match in the
|
|||
|
grammar, and multiple unmatched syntax elements are not grouped
|
|||
|
together. Each hook is called with one argument, which is a list of
|
|||
|
syntax tokens created by the semantic lexer. Use the functions
|
|||
|
`semantic-lex-token-start', `semantic-lex-token-end' and
|
|||
|
`semantic-lex-token-text' to get information about these tokens. The
|
|||
|
current buffer is the buffer these tokens are derived from.")
|
|||
|
|
|||
|
(defvar semantic--before-fetch-tags-hook nil
|
|||
|
"Hooks run before a buffer is parses for tags.
|
|||
|
It is called before any request for tags is made via the function
|
|||
|
`semantic-fetch-tags' by an application.
|
|||
|
If any hook returns a nil value, the cached value is returned
|
|||
|
immediately, even if it is empty.")
|
|||
|
(semantic-varalias-obsolete 'semantic-before-toplevel-bovination-hook
|
|||
|
'semantic--before-fetch-tags-hook)
|
|||
|
|
|||
|
(defvar semantic-after-toplevel-bovinate-hook nil
|
|||
|
"Hooks run after a toplevel parse.
|
|||
|
It is not run if the toplevel parse command is called, and buffer does
|
|||
|
not need to be fully reparsed.
|
|||
|
For language specific hooks, make sure you define this as a local hook.
|
|||
|
|
|||
|
This hook should not be used any more.
|
|||
|
Use `semantic-after-toplevel-cache-change-hook' instead.")
|
|||
|
(make-obsolete-variable 'semantic-after-toplevel-bovinate-hook nil)
|
|||
|
|
|||
|
(defvar semantic-after-toplevel-cache-change-hook nil
|
|||
|
"Hooks run after the buffer tag list has changed.
|
|||
|
This list will change when a buffer is reparsed, or when the tag list
|
|||
|
in a buffer is cleared. It is *NOT* called if the current tag list is
|
|||
|
partially reparsed.
|
|||
|
|
|||
|
Hook functions must take one argument, which is the new list of tags
|
|||
|
associated with this buffer.
|
|||
|
|
|||
|
For language specific hooks, make sure you define this as a local hook.")
|
|||
|
|
|||
|
(defvar semantic-before-toplevel-cache-flush-hook nil
|
|||
|
"Hooks run before the toplevel tag cache is flushed.
|
|||
|
For language specific hooks, make sure you define this as a local
|
|||
|
hook. This hook is called before a corresponding
|
|||
|
`semantic-after-toplevel-cache-change-hook' which is also called
|
|||
|
during a flush when the cache is given a new value of nil.")
|
|||
|
|
|||
|
(defcustom semantic-dump-parse nil
|
|||
|
"When non-nil, dump parsing information."
|
|||
|
:group 'semantic
|
|||
|
:type 'boolean)
|
|||
|
|
|||
|
(defvar semantic-parser-name "LL"
|
|||
|
"Optional name of the parser used to parse input stream.")
|
|||
|
(make-variable-buffer-local 'semantic-parser-name)
|
|||
|
|
|||
|
;;; Parse tree state management API
|
|||
|
;;
|
|||
|
(defvar semantic-parse-tree-state 'needs-rebuild
|
|||
|
"State of the current parse tree.")
|
|||
|
(make-variable-buffer-local 'semantic-parse-tree-state)
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-unparseable ()
|
|||
|
"Indicate that the current buffer is unparseable.
|
|||
|
It is also true that the parse tree will need either updating or
|
|||
|
a rebuild. This state will be changed when the user edits the buffer."
|
|||
|
`(setq semantic-parse-tree-state 'unparseable))
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-unparseable-p ()
|
|||
|
"Return non-nil if the current buffer has been marked unparseable."
|
|||
|
`(eq semantic-parse-tree-state 'unparseable))
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-set-needs-update ()
|
|||
|
"Indicate that the current parse tree needs to be updated.
|
|||
|
The parse tree can be updated by `semantic-parse-changes'."
|
|||
|
`(setq semantic-parse-tree-state 'needs-update))
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-needs-update-p ()
|
|||
|
"Return non-nil if the current parse tree needs to be updated."
|
|||
|
`(eq semantic-parse-tree-state 'needs-update))
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-set-needs-rebuild ()
|
|||
|
"Indicate that the current parse tree needs to be rebuilt.
|
|||
|
The parse tree must be rebuilt by `semantic-parse-region'."
|
|||
|
`(setq semantic-parse-tree-state 'needs-rebuild))
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-needs-rebuild-p ()
|
|||
|
"Return non-nil if the current parse tree needs to be rebuilt."
|
|||
|
`(eq semantic-parse-tree-state 'needs-rebuild))
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-set-up-to-date ()
|
|||
|
"Indicate that the current parse tree is up to date."
|
|||
|
`(setq semantic-parse-tree-state nil))
|
|||
|
|
|||
|
(defmacro semantic-parse-tree-up-to-date-p ()
|
|||
|
"Return non-nil if the current parse tree is up to date."
|
|||
|
`(null semantic-parse-tree-state))
|
|||
|
|
|||
|
;;; Interfacing with the system
|
|||
|
;;
|
|||
|
(defcustom semantic-inhibit-functions nil
|
|||
|
"List of functions to call with no arguments before Semantic is setup.
|
|||
|
If any of these functions returns non-nil, the current buffer is not
|
|||
|
setup to use Semantic."
|
|||
|
:group 'semantic
|
|||
|
:type 'hook)
|
|||
|
|
|||
|
(defvar semantic-init-hooks nil
|
|||
|
"*Hooks run when a buffer is initialized with a parsing table.")
|
|||
|
|
|||
|
(defvar semantic-init-mode-hooks nil
|
|||
|
"*Hooks run when a buffer of a particular mode is initialized.")
|
|||
|
(make-variable-buffer-local 'semantic-init-mode-hooks)
|
|||
|
|
|||
|
(defvar semantic-init-db-hooks nil
|
|||
|
"Hooks run when a buffer is initialized with a parsing table for DBs.
|
|||
|
This hook is for database functions which intend to swap in a tag table.
|
|||
|
This guarantees that the DB will go before other modes that require
|
|||
|
a parse of the buffer.")
|
|||
|
|
|||
|
(defvar semantic-new-buffer-fcn-was-run nil
|
|||
|
"Non nil after `semantic-new-buffer-fcn' has been executed.")
|
|||
|
(make-variable-buffer-local 'semantic-new-buffer-fcn-was-run)
|
|||
|
|
|||
|
(defsubst semantic-active-p ()
|
|||
|
"Return non-nil if the current buffer was set up for parsing."
|
|||
|
semantic-new-buffer-fcn-was-run)
|
|||
|
|
|||
|
(defsubst semantic--umatched-syntax-needs-refresh-p ()
|
|||
|
"Return non-nil if the unmatched syntax cache needs a refresh.
|
|||
|
That is if it is dirty or if the current parse tree isn't up to date."
|
|||
|
(or semantic-unmatched-syntax-cache-check
|
|||
|
(not (semantic-parse-tree-up-to-date-p))))
|
|||
|
|
|||
|
(defun semantic-new-buffer-fcn ()
|
|||
|
"Setup the current buffer to use Semantic.
|
|||
|
If the major mode is ready for Semantic, and no
|
|||
|
`semantic-inhibit-functions' disabled it, the current buffer is setup
|
|||
|
to use Semantic, and `semantic-init-hook' is run."
|
|||
|
;; Do stuff if semantic was activated by a mode hook in this buffer,
|
|||
|
;; and not afterwards disabled.
|
|||
|
(when (and semantic--parse-table
|
|||
|
(not (semantic-active-p))
|
|||
|
(not (run-hook-with-args-until-success
|
|||
|
'semantic-inhibit-functions)))
|
|||
|
;; Make sure that if this buffer is cloned, our tags and overlays
|
|||
|
;; don't go along for the ride.
|
|||
|
(add-hook 'clone-indirect-buffer-hook 'semantic-clear-toplevel-cache
|
|||
|
nil t)
|
|||
|
;; Specify that this function has done it's work. At this point
|
|||
|
;; we can consider that semantic is active in this buffer.
|
|||
|
(setq semantic-new-buffer-fcn-was-run t)
|
|||
|
;; Here are some buffer local variables we can initialize ourselves
|
|||
|
;; of a mode does not choose to do so.
|
|||
|
(semantic-lex-init)
|
|||
|
;; Force this buffer to have its cache refreshed.
|
|||
|
(semantic-clear-toplevel-cache)
|
|||
|
;; Call DB hooks before regular init hooks
|
|||
|
(run-hooks 'semantic-init-db-hooks)
|
|||
|
;; Set up semantic modes
|
|||
|
(run-hooks 'semantic-init-hooks)
|
|||
|
;; Set up major-mode specific semantic modes
|
|||
|
(run-hooks 'semantic-init-mode-hooks)
|
|||
|
))
|
|||
|
|
|||
|
(add-hook 'mode-local-init-hook 'semantic-new-buffer-fcn)
|
|||
|
|
|||
|
;; Test the above hook.
|
|||
|
;;(add-hook 'semantic-init-hooks (lambda () (message "init for semantic")))
|
|||
|
|
|||
|
(defun semantic-fetch-tags-fast ()
|
|||
|
"For use in a hook. When only a partial reparse is needed, reparse."
|
|||
|
(condition-case nil
|
|||
|
(if (semantic-parse-tree-needs-update-p)
|
|||
|
(semantic-fetch-tags))
|
|||
|
(error nil))
|
|||
|
semantic--buffer-cache)
|
|||
|
|
|||
|
(if (boundp 'eval-defun-hooks)
|
|||
|
(add-hook 'eval-defun-hooks 'semantic-fetch-tags-fast))
|
|||
|
|
|||
|
;;; Parsing Commands
|
|||
|
;;
|
|||
|
(eval-when-compile
|
|||
|
(condition-case nil (require 'pp) (error nil)))
|
|||
|
|
|||
|
(defvar semantic-edebug nil
|
|||
|
"When non-nil, activate the interactive parsing debugger.
|
|||
|
Do not set this yourself. Call `semantic-debug'.")
|
|||
|
|
|||
|
(defun semantic-elapsed-time (start end)
|
|||
|
"Copied from elp.el. Was elp-elapsed-time.
|
|||
|
Argument START and END bound the time being calculated."
|
|||
|
(+ (* (- (car end) (car start)) 65536.0)
|
|||
|
(- (car (cdr end)) (car (cdr start)))
|
|||
|
(/ (- (car (cdr (cdr end))) (car (cdr (cdr start)))) 1000000.0)))
|
|||
|
|
|||
|
(defun bovinate (&optional clear)
|
|||
|
"Parse the current buffer. Show output in a temp buffer.
|
|||
|
Optional argument CLEAR will clear the cache before parsing.
|
|||
|
If CLEAR is negative, it will do a full reparse, and also not display
|
|||
|
the output buffer."
|
|||
|
(interactive "P")
|
|||
|
(if clear (semantic-clear-toplevel-cache))
|
|||
|
(if (eq clear '-) (setq clear -1))
|
|||
|
(let* ((start (current-time))
|
|||
|
(out (semantic-fetch-tags))
|
|||
|
(end (current-time)))
|
|||
|
(message "Retrieving tags took %.2f seconds."
|
|||
|
(semantic-elapsed-time start end))
|
|||
|
(when (or (null clear) (not (listp clear)))
|
|||
|
(pop-to-buffer "*Parser Output*")
|
|||
|
(require 'pp)
|
|||
|
(erase-buffer)
|
|||
|
(insert (pp-to-string out))
|
|||
|
(goto-char (point-min)))))
|
|||
|
|
|||
|
;;; Functions of the parser plug-in API
|
|||
|
;;
|
|||
|
;; Overload these functions to create new types of parsers.
|
|||
|
;;
|
|||
|
(define-overloadable-function semantic-parse-stream (stream nonterminal)
|
|||
|
"Parse STREAM, starting at the first NONTERMINAL rule.
|
|||
|
For bovine and wisent based parsers, STREAM is from the output of
|
|||
|
`semantic-lex', and NONTERMINAL is a rule in the apropriate language
|
|||
|
specific rules file.
|
|||
|
The default parser table used for bovine or wisent based parsers is
|
|||
|
`semantic--parse-table'.
|
|||
|
|
|||
|
Must return a list: (STREAM TAGS) where STREAM is the unused elements
|
|||
|
from STREAM, and TAGS is the list of semantic tags found, usually only
|
|||
|
one tag is returned with the exception of compound statements")
|
|||
|
|
|||
|
(define-overloadable-function semantic-parse-changes ()
|
|||
|
"Reparse changes in the current buffer.
|
|||
|
The list of changes are tracked as a series of overlays in the buffer.
|
|||
|
When overloading this function, use `semantic-changes-in-region' to
|
|||
|
analyze.")
|
|||
|
|
|||
|
(define-overloadable-function semantic-parse-region
|
|||
|
(start end &optional nonterminal depth returnonerror)
|
|||
|
"Parse the area between START and END, and return any tags found.
|
|||
|
If END needs to be extended due to a lexical token being too large, it
|
|||
|
will be silently ignored.
|
|||
|
|
|||
|
Optional arguments:
|
|||
|
NONTERMINAL is the rule to start parsing at.
|
|||
|
DEPTH specifies the lexical depth to decend for parser that use
|
|||
|
lexical analysis as their first step.
|
|||
|
RETURNONERROR specifies that parsing should stop on the first
|
|||
|
unmatched syntax encountered. When nil, parsing skips the syntax,
|
|||
|
adding it to the unmatched syntax cache.
|
|||
|
|
|||
|
Must return a list of semantic tags wich have been cooked
|
|||
|
\(repositioned properly) but which DO NOT HAVE OVERLAYS associated
|
|||
|
with them. When overloading this function, use `semantic--tag-expand'
|
|||
|
to cook raw tags.")
|
|||
|
|
|||
|
(defun semantic-parse-region-default
|
|||
|
(start end &optional nonterminal depth returnonerror)
|
|||
|
"Parse the area between START and END, and return any tags found.
|
|||
|
If END needs to be extended due to a lexical token being too large, it
|
|||
|
will be silently ignored.
|
|||
|
Optional arguments:
|
|||
|
NONTERMINAL is the rule to start parsing at if it is known.
|
|||
|
DEPTH specifies the lexical depth to scan.
|
|||
|
RETURNONERROR specifies that parsing should end when encountering
|
|||
|
unterminated syntax."
|
|||
|
(when (or (null semantic--parse-table) (eq semantic--parse-table t))
|
|||
|
;; If there is no table, or it was set to t, then we are here by
|
|||
|
;; some other mistake. Do not throw an error deep in the parser.
|
|||
|
(error "No support found to parse buffer %S" (buffer-name)))
|
|||
|
(save-restriction
|
|||
|
(widen)
|
|||
|
(when (or (< end start) (> end (point-max)))
|
|||
|
(error "Invalid parse region bounds %S, %S" start end))
|
|||
|
(nreverse
|
|||
|
(semantic-repeat-parse-whole-stream
|
|||
|
(or (cdr (assq start semantic-lex-block-streams))
|
|||
|
(semantic-lex start end depth))
|
|||
|
nonterminal returnonerror))))
|
|||
|
|
|||
|
;;; Parsing functions
|
|||
|
;;
|
|||
|
(defun semantic-set-unmatched-syntax-cache (unmatched-syntax)
|
|||
|
"Set the unmatched syntax cache.
|
|||
|
Argument UNMATCHED-SYNTAX is the syntax to set into the cache."
|
|||
|
;; This function is not actually called by the main parse loop.
|
|||
|
;; This is intended for use by semanticdb.
|
|||
|
(setq semantic-unmatched-syntax-cache unmatched-syntax
|
|||
|
semantic-unmatched-syntax-cache-check nil)
|
|||
|
;; Refresh the display of unmatched syntax tokens if enabled
|
|||
|
(run-hook-with-args 'semantic-unmatched-syntax-hook
|
|||
|
semantic-unmatched-syntax-cache))
|
|||
|
|
|||
|
(defun semantic-clear-unmatched-syntax-cache ()
|
|||
|
"Clear the cache of unmatched syntax tokens."
|
|||
|
(setq semantic-unmatched-syntax-cache nil
|
|||
|
semantic-unmatched-syntax-cache-check t))
|
|||
|
|
|||
|
(defun semantic-unmatched-syntax-tokens ()
|
|||
|
"Return the list of unmatched syntax tokens."
|
|||
|
;; If the cache need refresh then do a full re-parse.
|
|||
|
(if (semantic--umatched-syntax-needs-refresh-p)
|
|||
|
;; To avoid a recursive call, temporarily disable
|
|||
|
;; `semantic-unmatched-syntax-hook'.
|
|||
|
(let (semantic-unmatched-syntax-hook)
|
|||
|
(condition-case nil
|
|||
|
(progn
|
|||
|
(semantic-clear-toplevel-cache)
|
|||
|
(semantic-fetch-tags))
|
|||
|
(quit
|
|||
|
(message "semantic-unmatched-syntax-tokens:\
|
|||
|
parsing of buffer canceled"))
|
|||
|
)))
|
|||
|
semantic-unmatched-syntax-cache)
|
|||
|
|
|||
|
(defun semantic-clear-toplevel-cache ()
|
|||
|
"Clear the toplevel tag cache for the current buffer.
|
|||
|
Clearing the cache will force a complete reparse next time a tag list
|
|||
|
is requested."
|
|||
|
(interactive)
|
|||
|
(run-hooks 'semantic-before-toplevel-cache-flush-hook)
|
|||
|
(setq semantic--buffer-cache nil)
|
|||
|
(semantic-clear-unmatched-syntax-cache)
|
|||
|
(semantic-clear-parser-warnings)
|
|||
|
;; Nuke all semantic overlays. This is faster than deleting based
|
|||
|
;; on our data structure.
|
|||
|
(let ((l (semantic-overlay-lists)))
|
|||
|
(mapc 'semantic-delete-overlay-maybe (car l))
|
|||
|
(mapc 'semantic-delete-overlay-maybe (cdr l))
|
|||
|
)
|
|||
|
(semantic-parse-tree-set-needs-rebuild)
|
|||
|
;; Remove this hook which tracks if a buffer is up to date or not.
|
|||
|
(remove-hook 'after-change-functions 'semantic-change-function t)
|
|||
|
;; Old model. Delete someday.
|
|||
|
;;(run-hooks 'semantic-after-toplevel-bovinate-hook)
|
|||
|
|
|||
|
(run-hook-with-args 'semantic-after-toplevel-cache-change-hook
|
|||
|
semantic--buffer-cache)
|
|||
|
)
|
|||
|
|
|||
|
(defvar semantic-bovinate-nonterminal-check-obarray)
|
|||
|
|
|||
|
(defun semantic--set-buffer-cache (tagtable)
|
|||
|
"Set the toplevel cache cache to TAGTABLE."
|
|||
|
(setq semantic--buffer-cache tagtable
|
|||
|
semantic-unmatched-syntax-cache-check nil
|
|||
|
;; This is specific to the bovine parser.
|
|||
|
semantic-bovinate-nonterminal-check-obarray nil)
|
|||
|
(semantic-parse-tree-set-up-to-date)
|
|||
|
(semantic-make-local-hook 'after-change-functions)
|
|||
|
(add-hook 'after-change-functions 'semantic-change-function nil t)
|
|||
|
(run-hook-with-args 'semantic-after-toplevel-cache-change-hook
|
|||
|
semantic--buffer-cache)
|
|||
|
;; Refresh the display of unmatched syntax tokens if enabled
|
|||
|
(run-hook-with-args 'semantic-unmatched-syntax-hook
|
|||
|
semantic-unmatched-syntax-cache)
|
|||
|
;; Old Semantic 1.3 hook API. Maybe useful forever?
|
|||
|
(run-hooks 'semantic-after-toplevel-bovinate-hook)
|
|||
|
)
|
|||
|
|
|||
|
(defvar semantic-working-type 'percent
|
|||
|
"*The type of working message to use when parsing.
|
|||
|
'percent means we are doing a linear parse through the buffer.
|
|||
|
'dynamic means we are reparsing specific tags.")
|
|||
|
(semantic-varalias-obsolete 'semantic-bovination-working-type
|
|||
|
'semantic-working-type)
|
|||
|
|
|||
|
(defvar semantic-minimum-working-buffer-size (* 1024 5)
|
|||
|
"*The minimum size of a buffer before working messages are displayed.
|
|||
|
Buffers smaller than will parse silently.
|
|||
|
Bufferse larger than this will display the working progress bar.")
|
|||
|
|
|||
|
(defsubst semantic-parser-working-message (&optional arg)
|
|||
|
"Return the message string displayed while parsing.
|
|||
|
If optional argument ARG is non-nil it is appended to the message
|
|||
|
string."
|
|||
|
(if semantic-parser-name
|
|||
|
(format "%s/%s" semantic-parser-name (or arg ""))
|
|||
|
(format "%s" (or arg ""))))
|
|||
|
|
|||
|
;;; Application Parser Entry Points
|
|||
|
;;
|
|||
|
;; The best way to call the parser from programs is via
|
|||
|
;; `semantic-fetch-tags'. This, in turn, uses other internal
|
|||
|
;; API functions which plug-in parsers can take advantage of.
|
|||
|
|
|||
|
(defun semantic-fetch-tags ()
|
|||
|
"Fetch semantic tags from the current buffer.
|
|||
|
If the buffer cache is up to date, return that.
|
|||
|
If the buffer cache is out of date, attempt an incremental reparse.
|
|||
|
If the buffer has not been parsed before, or if the incremental reparse
|
|||
|
fails, then parse the entire buffer.
|
|||
|
If a lexcial error had been previously discovered and the buffer
|
|||
|
was marked unparseable, then do nothing, and return the cache."
|
|||
|
(and
|
|||
|
;; Is this a semantic enabled buffer?
|
|||
|
(semantic-active-p)
|
|||
|
;; Application hooks say the buffer is safe for parsing
|
|||
|
(run-hook-with-args-until-failure
|
|||
|
'semantic-before-toplevel-bovination-hook)
|
|||
|
(run-hook-with-args-until-failure
|
|||
|
'semantic--before-fetch-tags-hook)
|
|||
|
;; If the buffer was previously marked unparseable,
|
|||
|
;; then don't waste our time.
|
|||
|
(not (semantic-parse-tree-unparseable-p))
|
|||
|
;; The parse tree actually needs to be refreshed
|
|||
|
(not (semantic-parse-tree-up-to-date-p))
|
|||
|
;; So do it!
|
|||
|
(let* ((gc-cons-threshold (max gc-cons-threshold 10000000))
|
|||
|
(semantic-lex-block-streams nil)
|
|||
|
(res nil))
|
|||
|
(garbage-collect)
|
|||
|
(cond
|
|||
|
|
|||
|
;;;; Try the incremental parser to do a fast update.
|
|||
|
((semantic-parse-tree-needs-update-p)
|
|||
|
(setq res (semantic-parse-changes))
|
|||
|
(if (semantic-parse-tree-needs-rebuild-p)
|
|||
|
;; If the partial reparse fails, jump to a full reparse.
|
|||
|
(semantic-fetch-tags)
|
|||
|
;; Clear the cache of unmatched syntax tokens
|
|||
|
;;
|
|||
|
;; NOTE TO SELF:
|
|||
|
;;
|
|||
|
;; Move this into the incremental parser. This is a bug.
|
|||
|
;;
|
|||
|
(semantic-clear-unmatched-syntax-cache)
|
|||
|
(run-hook-with-args ;; Let hooks know the updated tags
|
|||
|
'semantic-after-partial-cache-change-hook res))
|
|||
|
)
|
|||
|
|
|||
|
;;;; Parse the whole system.
|
|||
|
((semantic-parse-tree-needs-rebuild-p)
|
|||
|
;; (let ((working-status-dynamic-type
|
|||
|
;; (if (< (point-max) semantic-minimum-working-buffer-size)
|
|||
|
;; nil
|
|||
|
;; working-status-dynamic-type))
|
|||
|
;; (working-status-percentage-type
|
|||
|
;; (if (< (point-max) semantic-minimum-working-buffer-size)
|
|||
|
;; nil
|
|||
|
;; working-status-percentage-type)))
|
|||
|
;; (working-status-forms
|
|||
|
;; (semantic-parser-working-message (buffer-name)) "done"
|
|||
|
;; (setq res (semantic-parse-region (point-min) (point-max)))
|
|||
|
;; (working-status t)))
|
|||
|
|
|||
|
;; Use Emacs' built-in progress-reporter
|
|||
|
(let ((semantic--progress-reporter
|
|||
|
(and (>= (point-max) semantic-minimum-working-buffer-size)
|
|||
|
(eq semantic-working-type 'percent)
|
|||
|
(make-progress-reporter
|
|||
|
(semantic-parser-working-message (buffer-name))
|
|||
|
0 100))))
|
|||
|
(setq res (semantic-parse-region (point-min) (point-max)))
|
|||
|
(progress-reporter-done semantic--progress-reporter))
|
|||
|
|
|||
|
;; Clear the caches when we see there were no errors.
|
|||
|
;; But preserve the unmatched syntax cache and warnings!
|
|||
|
(let (semantic-unmatched-syntax-cache
|
|||
|
semantic-unmatched-syntax-cache-check
|
|||
|
semantic-parser-warnings)
|
|||
|
(semantic-clear-toplevel-cache))
|
|||
|
;; Set up the new overlays
|
|||
|
(semantic--tag-link-list-to-buffer res)
|
|||
|
;; Set up the cache with the new results
|
|||
|
(semantic--set-buffer-cache res)
|
|||
|
))))
|
|||
|
|
|||
|
;; Always return the current parse tree.
|
|||
|
semantic--buffer-cache)
|
|||
|
|
|||
|
(defun semantic-refresh-tags-safe ()
|
|||
|
"Refreshes the current buffer's tags safely.
|
|||
|
|
|||
|
Return non-nil if the refresh was successful.
|
|||
|
Return nil if there is some sort of syntax error preventing a reparse.
|
|||
|
|
|||
|
Does nothing if the current buffer doesn't need reparsing."
|
|||
|
|
|||
|
;; 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
|
|||
|
(let* ((inhibit-quit nil)
|
|||
|
(lexically-safe t)
|
|||
|
)
|
|||
|
|
|||
|
(unwind-protect
|
|||
|
;; Perform the parsing.
|
|||
|
(progn
|
|||
|
(when (semantic-lex-catch-errors safe-refresh
|
|||
|
(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))
|
|||
|
)
|
|||
|
)
|
|||
|
;; Return if we are lexically safe
|
|||
|
lexically-safe))))
|
|||
|
|
|||
|
(defun semantic-bovinate-toplevel (&optional ignored)
|
|||
|
"Backward Compatibility Function."
|
|||
|
(semantic-fetch-tags))
|
|||
|
(make-obsolete 'semantic-bovinate-toplevel 'semantic-fetch-tags)
|
|||
|
|
|||
|
;; Another approach is to let Emacs call the parser on idle time, when
|
|||
|
;; needed, use `semantic-fetch-available-tags' to only retrieve
|
|||
|
;; available tags, and setup the `semantic-after-*-hook' hooks to
|
|||
|
;; synchronize with new tags when they become available.
|
|||
|
|
|||
|
(defsubst semantic-fetch-available-tags ()
|
|||
|
"Fetch available semantic tags from the current buffer.
|
|||
|
That is, return tags currently in the cache without parsing the
|
|||
|
current buffer.
|
|||
|
Parse operations happen asynchronously when needed on Emacs idle time.
|
|||
|
Use the `semantic-after-toplevel-cache-change-hook' and
|
|||
|
`semantic-after-partial-cache-change-hook' hooks to synchronize with
|
|||
|
new tags when they become available."
|
|||
|
semantic--buffer-cache)
|
|||
|
|
|||
|
;;; Iterative parser helper function
|
|||
|
;;
|
|||
|
;; Iterative parsers are better than rule-based iterative functions
|
|||
|
;; in that they can handle obscure errors more cleanly.
|
|||
|
;;
|
|||
|
;; `semantic-repeat-parse-whole-stream' abstracts this action for
|
|||
|
;; other parser centric routines.
|
|||
|
;;
|
|||
|
(defun semantic-repeat-parse-whole-stream
|
|||
|
(stream nonterm &optional returnonerror)
|
|||
|
"Iteratively parse the entire stream STREAM starting with NONTERM.
|
|||
|
Optional argument RETURNONERROR indicates that the parser should exit
|
|||
|
with the current results on a parse error.
|
|||
|
This function returns semantic tags without overlays."
|
|||
|
(let ((result nil)
|
|||
|
(case-fold-search semantic-case-fold)
|
|||
|
nontermsym tag)
|
|||
|
(while stream
|
|||
|
(setq nontermsym (semantic-parse-stream stream nonterm)
|
|||
|
tag (car (cdr nontermsym)))
|
|||
|
(if (not nontermsym)
|
|||
|
(error "Parse error @ %d" (car (cdr (car stream)))))
|
|||
|
(if (eq (car nontermsym) stream)
|
|||
|
(error "Parser error: Infinite loop?"))
|
|||
|
(if tag
|
|||
|
(if (car tag)
|
|||
|
(setq tag (mapcar
|
|||
|
#'(lambda (tag)
|
|||
|
;; Set the 'reparse-symbol property to
|
|||
|
;; NONTERM unless it was already setup
|
|||
|
;; by a tag expander
|
|||
|
(or (semantic--tag-get-property
|
|||
|
tag 'reparse-symbol)
|
|||
|
(semantic--tag-put-property
|
|||
|
tag 'reparse-symbol nonterm))
|
|||
|
tag)
|
|||
|
(semantic--tag-expand tag))
|
|||
|
result (append tag result))
|
|||
|
;; No error in this case, a purposeful nil means don't
|
|||
|
;; store anything.
|
|||
|
)
|
|||
|
(if returnonerror
|
|||
|
(setq stream nil)
|
|||
|
;; The current item in the stream didn't match, so add it to
|
|||
|
;; the list of syntax items which didn't match.
|
|||
|
(setq semantic-unmatched-syntax-cache
|
|||
|
(cons (car stream) semantic-unmatched-syntax-cache))
|
|||
|
))
|
|||
|
;; Designated to ignore.
|
|||
|
(setq stream (car nontermsym))
|
|||
|
(if stream
|
|||
|
;; (if (eq semantic-working-type 'percent)
|
|||
|
;; (working-status
|
|||
|
;; (/ (* 100 (semantic-lex-token-start (car stream)))
|
|||
|
;; (point-max)))
|
|||
|
;; (working-dynamic-status))
|
|||
|
|
|||
|
;; Use Emacs' built-in progress reporter:
|
|||
|
(and (boundp 'semantic--progress-reporter)
|
|||
|
semantic--progress-reporter
|
|||
|
(progress-reporter-update
|
|||
|
semantic--progress-reporter
|
|||
|
(/ (* 100 (semantic-lex-token-start (car stream)))
|
|||
|
(point-max))))))
|
|||
|
result))
|
|||
|
|
|||
|
;;; Parsing Warnings:
|
|||
|
;;
|
|||
|
;; Parsing a buffer may result in non-critical things that we should
|
|||
|
;; alert the user to without interrupting the normal flow.
|
|||
|
;;
|
|||
|
;; Any parser can use this API to provide a list of warnings during a
|
|||
|
;; parse which a user may want to investigate.
|
|||
|
(defvar semantic-parser-warnings nil
|
|||
|
"A list of parser warnings since the last full reparse.")
|
|||
|
(make-variable-buffer-local 'semantic-parser-warnings)
|
|||
|
|
|||
|
(defun semantic-clear-parser-warnings ()
|
|||
|
"Clear the current list of parser warnings for this buffer."
|
|||
|
(setq semantic-parser-warnings nil))
|
|||
|
|
|||
|
(defun semantic-push-parser-warning (warning start end)
|
|||
|
"Add a parser WARNING that covers text from START to END."
|
|||
|
(setq semantic-parser-warnings
|
|||
|
(cons (cons warning (cons start end))
|
|||
|
semantic-parser-warnings)))
|
|||
|
|
|||
|
(defun semantic-dump-parser-warnings ()
|
|||
|
"Dump any parser warnings."
|
|||
|
(interactive)
|
|||
|
(if semantic-parser-warnings
|
|||
|
(let ((pw semantic-parser-warnings))
|
|||
|
(pop-to-buffer "*Parser Warnings*")
|
|||
|
(require 'pp)
|
|||
|
(erase-buffer)
|
|||
|
(insert (pp-to-string pw))
|
|||
|
(goto-char (point-min)))
|
|||
|
(message "No parser warnings.")))
|
|||
|
|
|||
|
|
|||
|
|
|||
|
;;; Compatibility:
|
|||
|
;;
|
|||
|
;; Semantic 1.x parser action helper functions, used by some parsers.
|
|||
|
;; Please move away from these functions, and try using semantic 2.x
|
|||
|
;; interfaces instead.
|
|||
|
;;
|
|||
|
(defsubst semantic-bovinate-region-until-error
|
|||
|
(start end nonterm &optional depth)
|
|||
|
"NOTE: Use `semantic-parse-region' instead.
|
|||
|
|
|||
|
Bovinate between START and END starting with NONTERM.
|
|||
|
Optional DEPTH specifies how many levels of parenthesis to enter.
|
|||
|
This command will parse until an error is encountered, and return
|
|||
|
the list of everything found until that moment.
|
|||
|
This is meant for finding variable definitions at the beginning of
|
|||
|
code blocks in methods. If `bovine-inner-scope' can also support
|
|||
|
commands, use `semantic-bovinate-from-nonterminal-full'."
|
|||
|
(semantic-parse-region start end nonterm depth t))
|
|||
|
(make-obsolete 'semantic-bovinate-region-until-error
|
|||
|
'semantic-parse-region)
|
|||
|
|
|||
|
(defsubst semantic-bovinate-from-nonterminal
|
|||
|
(start end nonterm &optional depth length)
|
|||
|
"Bovinate from within a nonterminal lambda from START to END.
|
|||
|
Argument NONTERM is the nonterminal symbol to start with.
|
|||
|
Optional argument DEPTH is the depth of lists to dive into. When used
|
|||
|
in a `lambda' of a MATCH-LIST, there is no need to include a START and
|
|||
|
END part.
|
|||
|
Optional argument LENGTH specifies we are only interested in LENGTH
|
|||
|
tokens."
|
|||
|
(car-safe (cdr (semantic-parse-stream
|
|||
|
(semantic-lex start end (or depth 1) length)
|
|||
|
nonterm))))
|
|||
|
|
|||
|
(defsubst semantic-bovinate-from-nonterminal-full
|
|||
|
(start end nonterm &optional depth)
|
|||
|
"NOTE: Use `semantic-parse-region' instead.
|
|||
|
|
|||
|
Bovinate from within a nonterminal lambda from START to END.
|
|||
|
Iterates until all the space between START and END is exhausted.
|
|||
|
Argument NONTERM is the nonterminal symbol to start with.
|
|||
|
If NONTERM is nil, use `bovine-block-toplevel'.
|
|||
|
Optional argument DEPTH is the depth of lists to dive into.
|
|||
|
When used in a `lambda' of a MATCH-LIST, there is no need to include
|
|||
|
a START and END part."
|
|||
|
(semantic-parse-region start end nonterm (or depth 1)))
|
|||
|
(make-obsolete 'semantic-bovinate-from-nonterminal-full
|
|||
|
'semantic-parse-region)
|
|||
|
|
|||
|
(provide 'semantic)
|
|||
|
|
|||
|
;;; semantic.el ends here
|
|||
|
|
|||
|
;; Semantic-util is a part of the semantic API. Include it last
|
|||
|
;; because it depends on semantic.
|
|||
|
(require 'semantic-util)
|