diff options
| author | Chong Yidong | 2009-08-28 15:19:20 +0000 |
|---|---|---|
| committer | Chong Yidong | 2009-08-28 15:19:20 +0000 |
| commit | 7a0e7d3387b5b675042878e5e2e5878b94b2487a (patch) | |
| tree | 48f1d7c4af09bbc4abfb968332cfc74c5cc1087f | |
| parent | 57e622d92b9538b2302c51ef993766276dfc7569 (diff) | |
| download | emacs-7a0e7d3387b5b675042878e5e2e5878b94b2487a.tar.gz emacs-7a0e7d3387b5b675042878e5e2e5878b94b2487a.zip | |
cedet/semantic/db.el, cedet/semantic/decorate.el,
cedet/semantic/lex-spp.el, cedet/semantic/util-modes.el: New files.
| -rw-r--r-- | lisp/cedet/semantic/db.el | 989 | ||||
| -rw-r--r-- | lisp/cedet/semantic/decorate.el | 320 | ||||
| -rw-r--r-- | lisp/cedet/semantic/lex-spp.el | 1187 | ||||
| -rw-r--r-- | lisp/cedet/semantic/util-modes.el | 1228 |
4 files changed, 3724 insertions, 0 deletions
diff --git a/lisp/cedet/semantic/db.el b/lisp/cedet/semantic/db.el new file mode 100644 index 00000000000..3b32a9a8123 --- /dev/null +++ b/lisp/cedet/semantic/db.el | |||
| @@ -0,0 +1,989 @@ | |||
| 1 | ;;; semanticdb.el --- Semantic tag database manager | ||
| 2 | |||
| 3 | ;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, | ||
| 4 | ;;; 2008, 2009 Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | ||
| 7 | ;; Keywords: tags | ||
| 8 | |||
| 9 | ;; This file is part of GNU Emacs. | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 14 | ;; (at your option) any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 23 | |||
| 24 | ;;; Commentary: | ||
| 25 | ;; | ||
| 26 | ;; Maintain a database of tags for a group of files and enable | ||
| 27 | ;; queries into the database. | ||
| 28 | ;; | ||
| 29 | ;; By default, assume one database per directory. | ||
| 30 | ;; | ||
| 31 | |||
| 32 | (require 'eieio) | ||
| 33 | ;; (require 'inversion) | ||
| 34 | ;; (eval-and-compile | ||
| 35 | ;; (inversion-require 'eieio "1.0")) | ||
| 36 | (require 'eieio-base) | ||
| 37 | (require 'semantic) | ||
| 38 | (eval-when-compile | ||
| 39 | (require 'semantic/lex-spp)) | ||
| 40 | |||
| 41 | ;;; Variables: | ||
| 42 | (defgroup semanticdb nil | ||
| 43 | "Parser Generator Persistent Database interface." | ||
| 44 | :group 'semantic | ||
| 45 | ) | ||
| 46 | ;;; Code: | ||
| 47 | (defvar semanticdb-database-list nil | ||
| 48 | "List of all active databases.") | ||
| 49 | |||
| 50 | (defvar semanticdb-new-database-class 'semanticdb-project-database-file | ||
| 51 | "The default type of database created for new files. | ||
| 52 | This can be changed on a per file basis, so that some directories | ||
| 53 | are saved using one mechanism, and some directories via a different | ||
| 54 | mechanism.") | ||
| 55 | (make-variable-buffer-local 'semanticdb-new-database-class) | ||
| 56 | |||
| 57 | (defvar semanticdb-default-find-index-class 'semanticdb-find-search-index | ||
| 58 | "The default type of search index to use for a `semanticdb-table's. | ||
| 59 | This can be changed to try out new types of search indicies.") | ||
| 60 | (make-variable-buffer-local 'semanticdb-default-find=index-class) | ||
| 61 | |||
| 62 | |||
| 63 | ;;; ABSTRACT CLASSES | ||
| 64 | ;; | ||
| 65 | (defclass semanticdb-abstract-table () | ||
| 66 | ((parent-db ;; :initarg :parent-db | ||
| 67 | ;; Do not set an initarg, or you get circular writes to disk. | ||
| 68 | :documentation "Database Object containing this table.") | ||
| 69 | (major-mode :initarg :major-mode | ||
| 70 | :initform nil | ||
| 71 | :documentation "Major mode this table belongs to. | ||
| 72 | Sometimes it is important for a program to know if a given table has the | ||
| 73 | same major mode as the current buffer.") | ||
| 74 | (tags :initarg :tags | ||
| 75 | :accessor semanticdb-get-tags | ||
| 76 | :printer semantic-tag-write-list-slot-value | ||
| 77 | :documentation "The tags belonging to this table.") | ||
| 78 | (index :type semanticdb-abstract-search-index | ||
| 79 | :documentation "The search index. | ||
| 80 | Used by semanticdb-find to store additional information about | ||
| 81 | this table for searching purposes. | ||
| 82 | |||
| 83 | Note: This index will not be saved in a persistent file.") | ||
| 84 | (cache :type list | ||
| 85 | :initform nil | ||
| 86 | :documentation "List of cache information for tools. | ||
| 87 | Any particular tool can cache data to a database at runtime | ||
| 88 | with `semanticdb-cache-get'. | ||
| 89 | |||
| 90 | Using a semanticdb cache does not save any information to a file, | ||
| 91 | so your cache will need to be recalculated at runtime. Caches can be | ||
| 92 | referenced even when the file is not in a buffer. | ||
| 93 | |||
| 94 | Note: This index will not be saved in a persistent file.") | ||
| 95 | ) | ||
| 96 | "A simple table for semantic tags. | ||
| 97 | This table is the root of tables, and contains the minimum needed | ||
| 98 | for a new table not associated with a buffer." | ||
| 99 | :abstract t) | ||
| 100 | |||
| 101 | (defmethod semanticdb-in-buffer-p ((obj semanticdb-abstract-table)) | ||
| 102 | "Return a nil, meaning abstract table OBJ is not in a buffer." | ||
| 103 | nil) | ||
| 104 | |||
| 105 | (defmethod semanticdb-get-buffer ((obj semanticdb-abstract-table)) | ||
| 106 | "Return a buffer associated with OBJ. | ||
| 107 | If the buffer is not in memory, load it with `find-file-noselect'." | ||
| 108 | nil) | ||
| 109 | |||
| 110 | (defmethod semanticdb-full-filename ((obj semanticdb-abstract-table)) | ||
| 111 | "Fetch the full filename that OBJ refers to. | ||
| 112 | Abstract tables do not have file names associated with them." | ||
| 113 | nil) | ||
| 114 | |||
| 115 | (defmethod semanticdb-dirty-p ((obj semanticdb-abstract-table)) | ||
| 116 | "Return non-nil if OBJ is 'dirty'." | ||
| 117 | nil) | ||
| 118 | |||
| 119 | (defmethod semanticdb-set-dirty ((obj semanticdb-abstract-table)) | ||
| 120 | "Mark the abstract table OBJ dirty. | ||
| 121 | Abstract tables can not be marked dirty, as there is nothing | ||
| 122 | for them to synchronize against." | ||
| 123 | ;; The abstract table can not be dirty. | ||
| 124 | nil) | ||
| 125 | |||
| 126 | (defmethod semanticdb-normalize-tags ((obj semanticdb-abstract-table) tags) | ||
| 127 | "For the table OBJ, convert a list of TAGS, into standardized form. | ||
| 128 | The default is to return TAGS. | ||
| 129 | Some databases may default to searching and providing simplified tags | ||
| 130 | based on whichever technique used. This method provides a hook for | ||
| 131 | them to convert TAG into a more complete form." | ||
| 132 | tags) | ||
| 133 | |||
| 134 | (defmethod semanticdb-normalize-one-tag ((obj semanticdb-abstract-table) tag) | ||
| 135 | "For the table OBJ, convert a TAG, into standardized form. | ||
| 136 | This method returns a list of the form (DATABASE . NEWTAG). | ||
| 137 | |||
| 138 | The default is to just return (OBJ TAG). | ||
| 139 | |||
| 140 | Some databases may default to searching and providing simplified tags | ||
| 141 | based on whichever technique used. This method provides a hook for | ||
| 142 | them to convert TAG into a more complete form." | ||
| 143 | (cons obj tag)) | ||
| 144 | |||
| 145 | (defmethod object-print ((obj semanticdb-abstract-table) &rest strings) | ||
| 146 | "Pretty printer extension for `semanticdb-table'. | ||
| 147 | Adds the number of tags in this file to the object print name." | ||
| 148 | (apply 'call-next-method obj | ||
| 149 | (cons (format " (%d tags)" | ||
| 150 | (length (semanticdb-get-tags obj)) | ||
| 151 | ) | ||
| 152 | strings))) | ||
| 153 | |||
| 154 | ;;; Index Cache | ||
| 155 | ;; | ||
| 156 | (defclass semanticdb-abstract-search-index () | ||
| 157 | ((table :initarg :table | ||
| 158 | :type semanticdb-abstract-table | ||
| 159 | :documentation "XRef to the table this belongs to.") | ||
| 160 | ) | ||
| 161 | "A place where semanticdb-find can store search index information. | ||
| 162 | The search index will store data about which other tables might be | ||
| 163 | needed, or perhaps create hash or index tables for the current buffer." | ||
| 164 | :abstract t) | ||
| 165 | |||
| 166 | (defmethod semanticdb-get-table-index ((obj semanticdb-abstract-table)) | ||
| 167 | "Return the search index for the table OBJ. | ||
| 168 | If one doesn't exist, create it." | ||
| 169 | (if (slot-boundp obj 'index) | ||
| 170 | (oref obj index) | ||
| 171 | (let ((idx nil)) | ||
| 172 | (setq idx (funcall semanticdb-default-find-index-class | ||
| 173 | (concat (object-name obj) " index") | ||
| 174 | ;; Fill in the defaults | ||
| 175 | :table obj | ||
| 176 | )) | ||
| 177 | (oset obj index idx) | ||
| 178 | idx))) | ||
| 179 | |||
| 180 | (defmethod semanticdb-synchronize ((idx semanticdb-abstract-search-index) | ||
| 181 | new-tags) | ||
| 182 | "Synchronize the search index IDX with some NEW-TAGS." | ||
| 183 | ;; The abstract class will do... NOTHING! | ||
| 184 | ) | ||
| 185 | |||
| 186 | (defmethod semanticdb-partial-synchronize ((idx semanticdb-abstract-search-index) | ||
| 187 | new-tags) | ||
| 188 | "Synchronize the search index IDX with some changed NEW-TAGS." | ||
| 189 | ;; The abstract class will do... NOTHING! | ||
| 190 | ) | ||
| 191 | |||
| 192 | |||
| 193 | ;;; CONCRETE TABLE CLASSES | ||
| 194 | ;; | ||
| 195 | (defclass semanticdb-table (semanticdb-abstract-table) | ||
| 196 | ((file :initarg :file | ||
| 197 | :documentation "File name relative to the parent database. | ||
| 198 | This is for the file whose tags are stored in this TABLE object.") | ||
| 199 | (buffer :initform nil | ||
| 200 | :documentation "The buffer associated with this table. | ||
| 201 | If nil, the table's buffer is no in Emacs. If it has a value, then | ||
| 202 | it is in Emacs.") | ||
| 203 | (dirty :initform nil | ||
| 204 | :documentation | ||
| 205 | "Non nil if this table needs to be `Saved'.") | ||
| 206 | (db-refs :initform nil | ||
| 207 | :documentation | ||
| 208 | "List of `semanticdb-table' objects refering to this one. | ||
| 209 | These aren't saved, but are instead recalculated after load. | ||
| 210 | See the file semanticdb-ref.el for how this slot is used.") | ||
| 211 | (pointmax :initarg :pointmax | ||
| 212 | :initform nil | ||
| 213 | :documentation "Size of buffer when written to disk. | ||
| 214 | Checked on retrieval to make sure the file is the same.") | ||
| 215 | (fsize :initarg :fsize | ||
| 216 | :initform nil | ||
| 217 | :documentation "Size of the file when it was last referenced. | ||
| 218 | Checked when deciding if a loaded table needs updating from changes | ||
| 219 | outside of Semantic's control.") | ||
| 220 | (lastmodtime :initarg :lastmodtime | ||
| 221 | :initform nil | ||
| 222 | :documentation "Last modification time of the file referenced. | ||
| 223 | Checked when deciding if a loaded table needs updating from changes outside of | ||
| 224 | Semantic's control.") | ||
| 225 | ;; @todo - need to add `last parsed time', so we can also have | ||
| 226 | ;; refresh checks if spp tables or the parser gets rebuilt. | ||
| 227 | (unmatched-syntax :initarg :unmatched-syntax | ||
| 228 | :documentation | ||
| 229 | "List of vectors specifying unmatched syntax.") | ||
| 230 | |||
| 231 | (lexical-table :initarg :lexical-table | ||
| 232 | :initform nil | ||
| 233 | :printer semantic-lex-spp-table-write-slot-value | ||
| 234 | :documentation | ||
| 235 | "Table that might be needed by the lexical analyzer. | ||
| 236 | For C/C++, the C preprocessor macros can be saved here.") | ||
| 237 | ) | ||
| 238 | "A single table of tags derived from file.") | ||
| 239 | |||
| 240 | (defmethod semanticdb-in-buffer-p ((obj semanticdb-table)) | ||
| 241 | "Return a buffer associated with OBJ. | ||
| 242 | If the buffer is in memory, return that buffer." | ||
| 243 | (let ((buff (oref obj buffer))) | ||
| 244 | (if (buffer-live-p buff) | ||
| 245 | buff | ||
| 246 | (oset obj buffer nil)))) | ||
| 247 | |||
| 248 | (defmethod semanticdb-get-buffer ((obj semanticdb-table)) | ||
| 249 | "Return a buffer associated with OBJ. | ||
| 250 | If the buffer is in memory, return that buffer. | ||
| 251 | If the buffer is not in memory, load it with `find-file-noselect'." | ||
| 252 | (or (semanticdb-in-buffer-p obj) | ||
| 253 | (find-file-noselect (semanticdb-full-filename obj) t))) | ||
| 254 | |||
| 255 | (defmethod semanticdb-set-buffer ((obj semanticdb-table)) | ||
| 256 | "Set the current buffer to be a buffer owned by OBJ. | ||
| 257 | If OBJ's file is not loaded, read it in first." | ||
| 258 | (set-buffer (semanticdb-get-buffer obj))) | ||
| 259 | |||
| 260 | (defmethod semanticdb-full-filename ((obj semanticdb-table)) | ||
| 261 | "Fetch the full filename that OBJ refers to." | ||
| 262 | (expand-file-name (oref obj file) | ||
| 263 | (oref (oref obj parent-db) reference-directory))) | ||
| 264 | |||
| 265 | (defmethod semanticdb-dirty-p ((obj semanticdb-table)) | ||
| 266 | "Return non-nil if OBJ is 'dirty'." | ||
| 267 | (oref obj dirty)) | ||
| 268 | |||
| 269 | (defmethod semanticdb-set-dirty ((obj semanticdb-table)) | ||
| 270 | "Mark the abstract table OBJ dirty." | ||
| 271 | (oset obj dirty t) | ||
| 272 | ) | ||
| 273 | |||
| 274 | (defmethod object-print ((obj semanticdb-table) &rest strings) | ||
| 275 | "Pretty printer extension for `semanticdb-table'. | ||
| 276 | Adds the number of tags in this file to the object print name." | ||
| 277 | (apply 'call-next-method obj | ||
| 278 | (cons (if (oref obj dirty) ", DIRTY" "") strings))) | ||
| 279 | |||
| 280 | ;;; DATABASE BASE CLASS | ||
| 281 | ;; | ||
| 282 | (defclass semanticdb-project-database (eieio-instance-tracker) | ||
| 283 | ((tracking-symbol :initform semanticdb-database-list) | ||
| 284 | (reference-directory :type string | ||
| 285 | :documentation "Directory this database refers to. | ||
| 286 | When a cache directory is specified, then this refers to the directory | ||
| 287 | this database contains symbols for.") | ||
| 288 | (new-table-class :initform semanticdb-table | ||
| 289 | :type class | ||
| 290 | :documentation | ||
| 291 | "New tables created for this database are of this class.") | ||
| 292 | (cache :type list | ||
| 293 | :initform nil | ||
| 294 | :documentation "List of cache information for tools. | ||
| 295 | Any particular tool can cache data to a database at runtime | ||
| 296 | with `semanticdb-cache-get'. | ||
| 297 | |||
| 298 | Using a semanticdb cache does not save any information to a file, | ||
| 299 | so your cache will need to be recalculated at runtime. | ||
| 300 | |||
| 301 | Note: This index will not be saved in a persistent file.") | ||
| 302 | (tables :initarg :tables | ||
| 303 | :type list | ||
| 304 | ;; Need this protection so apps don't try to access | ||
| 305 | ;; the tables without using the accessor. | ||
| 306 | :accessor semanticdb-get-database-tables | ||
| 307 | :protection :protected | ||
| 308 | :documentation "List of `semantic-db-table' objects.")) | ||
| 309 | "Database of file tables.") | ||
| 310 | |||
| 311 | (defmethod semanticdb-full-filename ((obj semanticdb-project-database)) | ||
| 312 | "Fetch the full filename that OBJ refers to. | ||
| 313 | Abstract tables do not have file names associated with them." | ||
| 314 | nil) | ||
| 315 | |||
| 316 | (defmethod semanticdb-dirty-p ((DB semanticdb-project-database)) | ||
| 317 | "Return non-nil if DB is 'dirty'. | ||
| 318 | A database is dirty if the state of the database changed in a way | ||
| 319 | where it may need to resynchronize with some persistent storage." | ||
| 320 | (let ((dirty nil) | ||
| 321 | (tabs (oref DB tables))) | ||
| 322 | (while (and (not dirty) tabs) | ||
| 323 | (setq dirty (semanticdb-dirty-p (car tabs))) | ||
| 324 | (setq tabs (cdr tabs))) | ||
| 325 | dirty)) | ||
| 326 | |||
| 327 | (defmethod object-print ((obj semanticdb-project-database) &rest strings) | ||
| 328 | "Pretty printer extension for `semanticdb-project-database'. | ||
| 329 | Adds the number of tables in this file to the object print name." | ||
| 330 | (apply 'call-next-method obj | ||
| 331 | (cons (format " (%d tables%s)" | ||
| 332 | (length (semanticdb-get-database-tables obj)) | ||
| 333 | (if (semanticdb-dirty-p obj) | ||
| 334 | " DIRTY" "") | ||
| 335 | ) | ||
| 336 | strings))) | ||
| 337 | |||
| 338 | (defmethod semanticdb-create-database :STATIC ((dbc semanticdb-project-database) directory) | ||
| 339 | "Create a new semantic database of class DBC for DIRECTORY and return it. | ||
| 340 | If a database for DIRECTORY has already been created, return it. | ||
| 341 | If DIRECTORY doesn't exist, create a new one." | ||
| 342 | (let ((db (semanticdb-directory-loaded-p directory))) | ||
| 343 | (unless db | ||
| 344 | (setq db (semanticdb-project-database | ||
| 345 | (file-name-nondirectory directory) | ||
| 346 | :tables nil)) | ||
| 347 | ;; Set this up here. We can't put it in the constructor because it | ||
| 348 | ;; would be saved, and we want DB files to be portable. | ||
| 349 | (oset db reference-directory (file-truename directory))) | ||
| 350 | db)) | ||
| 351 | |||
| 352 | (defmethod semanticdb-flush-database-tables ((db semanticdb-project-database)) | ||
| 353 | "Reset the tables in DB to be empty." | ||
| 354 | (oset db tables nil)) | ||
| 355 | |||
| 356 | (defmethod semanticdb-create-table ((db semanticdb-project-database) file) | ||
| 357 | "Create a new table in DB for FILE and return it. | ||
| 358 | The class of DB contains the class name for the type of table to create. | ||
| 359 | If the table for FILE exists, return it. | ||
| 360 | If the table for FILE does not exist, create one." | ||
| 361 | (let ((newtab (semanticdb-file-table db file))) | ||
| 362 | (unless newtab | ||
| 363 | ;; This implementation will satisfy autoloaded classes | ||
| 364 | ;; for tables. | ||
| 365 | (setq newtab (funcall (oref db new-table-class) | ||
| 366 | (file-name-nondirectory file) | ||
| 367 | :file (file-name-nondirectory file) | ||
| 368 | )) | ||
| 369 | (oset newtab parent-db db) | ||
| 370 | (object-add-to-list db 'tables newtab t)) | ||
| 371 | newtab)) | ||
| 372 | |||
| 373 | (defmethod semanticdb-file-table ((obj semanticdb-project-database) filename) | ||
| 374 | "From OBJ, return FILENAME's associated table object." | ||
| 375 | (object-assoc (file-relative-name (file-truename filename) | ||
| 376 | (oref obj reference-directory)) | ||
| 377 | 'file (oref obj tables))) | ||
| 378 | |||
| 379 | ;; DATABASE FUNCTIONS | ||
| 380 | (defun semanticdb-get-database (filename) | ||
| 381 | "Get a database for FILENAME. | ||
| 382 | If one isn't found, create one." | ||
| 383 | (semanticdb-create-database semanticdb-new-database-class (file-truename filename))) | ||
| 384 | |||
| 385 | (defun semanticdb-directory-loaded-p (path) | ||
| 386 | "Return the project belonging to PATH if it was already loaded." | ||
| 387 | (eieio-instance-tracker-find path 'reference-directory 'semanticdb-database-list)) | ||
| 388 | |||
| 389 | (defun semanticdb-create-table-for-file (filename) | ||
| 390 | "Initialize a database table for FILENAME, and return it. | ||
| 391 | If FILENAME exists in the database already, return that. | ||
| 392 | If there is no database for the table to live in, create one." | ||
| 393 | (let ((cdb nil) | ||
| 394 | (tbl nil) | ||
| 395 | (dd (file-name-directory filename)) | ||
| 396 | ) | ||
| 397 | ;; Allow a database override function | ||
| 398 | (setq cdb (semanticdb-create-database semanticdb-new-database-class | ||
| 399 | dd)) | ||
| 400 | ;; Get a table for this file. | ||
| 401 | (setq tbl (semanticdb-create-table cdb filename)) | ||
| 402 | |||
| 403 | ;; Return the pair. | ||
| 404 | (cons cdb tbl) | ||
| 405 | )) | ||
| 406 | |||
| 407 | ;;; Cache Cache. | ||
| 408 | ;; | ||
| 409 | (defclass semanticdb-abstract-cache () | ||
| 410 | ((table :initarg :table | ||
| 411 | :type semanticdb-abstract-table | ||
| 412 | :documentation | ||
| 413 | "Cross reference to the table this belongs to.") | ||
| 414 | ) | ||
| 415 | "Abstract baseclass for tools to use to cache information in semanticdb. | ||
| 416 | Tools needing a per-file cache must subclass this, and then get one as | ||
| 417 | needed. Cache objects are identified in semanticdb by subclass. | ||
| 418 | In order to keep your cache up to date, be sure to implement | ||
| 419 | `semanticdb-synchronize', and `semanticdb-partial-synchronize'. | ||
| 420 | See the file semantic-scope.el for an example." | ||
| 421 | :abstract t) | ||
| 422 | |||
| 423 | (defmethod semanticdb-cache-get ((table semanticdb-abstract-table) | ||
| 424 | desired-class) | ||
| 425 | "Get a cache object on TABLE of class DESIRED-CLASS. | ||
| 426 | This method will create one if none exists with no init arguments | ||
| 427 | other than :table." | ||
| 428 | (assert (child-of-class-p desired-class 'semanticdb-abstract-cache)) | ||
| 429 | (let ((cache (oref table cache)) | ||
| 430 | (obj nil)) | ||
| 431 | (while (and (not obj) cache) | ||
| 432 | (if (eq (object-class-fast (car cache)) desired-class) | ||
| 433 | (setq obj (car cache))) | ||
| 434 | (setq cache (cdr cache))) | ||
| 435 | (if obj | ||
| 436 | obj ;; Just return it. | ||
| 437 | ;; No object, lets create a new one and return that. | ||
| 438 | (setq obj (funcall desired-class "Cache" :table table)) | ||
| 439 | (object-add-to-list table 'cache obj) | ||
| 440 | obj))) | ||
| 441 | |||
| 442 | (defmethod semanticdb-cache-remove ((table semanticdb-abstract-table) | ||
| 443 | cache) | ||
| 444 | "Remove from TABLE the cache object CACHE." | ||
| 445 | (object-remove-from-list table 'cache cache)) | ||
| 446 | |||
| 447 | (defmethod semanticdb-synchronize ((cache semanticdb-abstract-cache) | ||
| 448 | new-tags) | ||
| 449 | "Synchronize a CACHE with some NEW-TAGS." | ||
| 450 | ;; The abstract class will do... NOTHING! | ||
| 451 | ) | ||
| 452 | |||
| 453 | (defmethod semanticdb-partial-synchronize ((cache semanticdb-abstract-cache) | ||
| 454 | new-tags) | ||
| 455 | "Synchronize a CACHE with some changed NEW-TAGS." | ||
| 456 | ;; The abstract class will do... NOTHING! | ||
| 457 | ) | ||
| 458 | |||
| 459 | (defclass semanticdb-abstract-db-cache () | ||
| 460 | ((db :initarg :db | ||
| 461 | :type semanticdb-project-database | ||
| 462 | :documentation | ||
| 463 | "Cross reference to the database this belongs to.") | ||
| 464 | ) | ||
| 465 | "Abstract baseclass for tools to use to cache information in semanticdb. | ||
| 466 | Tools needing a database cache must subclass this, and then get one as | ||
| 467 | needed. Cache objects are identified in semanticdb by subclass. | ||
| 468 | In order to keep your cache up to date, be sure to implement | ||
| 469 | `semanticdb-synchronize', and `semanticdb-partial-synchronize'. | ||
| 470 | See the file semantic-scope.el for an example." | ||
| 471 | :abstract t) | ||
| 472 | |||
| 473 | (defmethod semanticdb-cache-get ((db semanticdb-project-database) | ||
| 474 | desired-class) | ||
| 475 | "Get a cache object on DB of class DESIRED-CLASS. | ||
| 476 | This method will create one if none exists with no init arguments | ||
| 477 | other than :table." | ||
| 478 | (assert (child-of-class-p desired-class 'semanticdb-abstract-db-cache)) | ||
| 479 | (let ((cache (oref db cache)) | ||
| 480 | (obj nil)) | ||
| 481 | (while (and (not obj) cache) | ||
| 482 | (if (eq (object-class-fast (car cache)) desired-class) | ||
| 483 | (setq obj (car cache))) | ||
| 484 | (setq cache (cdr cache))) | ||
| 485 | (if obj | ||
| 486 | obj ;; Just return it. | ||
| 487 | ;; No object, lets create a new one and return that. | ||
| 488 | (setq obj (funcall desired-class "Cache" :db db)) | ||
| 489 | (object-add-to-list db 'cache obj) | ||
| 490 | obj))) | ||
| 491 | |||
| 492 | (defmethod semanticdb-cache-remove ((db semanticdb-project-database) | ||
| 493 | cache) | ||
| 494 | "Remove from TABLE the cache object CACHE." | ||
| 495 | (object-remove-from-list db 'cache cache)) | ||
| 496 | |||
| 497 | |||
| 498 | (defmethod semanticdb-synchronize ((cache semanticdb-abstract-db-cache) | ||
| 499 | new-tags) | ||
| 500 | "Synchronize a CACHE with some NEW-TAGS." | ||
| 501 | ;; The abstract class will do... NOTHING! | ||
| 502 | ) | ||
| 503 | |||
| 504 | (defmethod semanticdb-partial-synchronize ((cache semanticdb-abstract-db-cache) | ||
| 505 | new-tags) | ||
| 506 | "Synchronize a CACHE with some changed NEW-TAGS." | ||
| 507 | ;; The abstract class will do... NOTHING! | ||
| 508 | ) | ||
| 509 | |||
| 510 | ;;; REFRESH | ||
| 511 | |||
| 512 | (defmethod semanticdb-refresh-table ((obj semanticdb-table) &optional force) | ||
| 513 | "If the tag list associated with OBJ is loaded, refresh it. | ||
| 514 | Optional argument FORCE will force a refresh even if the file in question | ||
| 515 | is not in a buffer. Avoid using FORCE for most uses, as an old cache | ||
| 516 | may be sufficient for the general case. Forced updates can be slow. | ||
| 517 | This will call `semantic-fetch-tags' if that file is in memory." | ||
| 518 | (when (or (semanticdb-in-buffer-p obj) force) | ||
| 519 | (save-excursion | ||
| 520 | (semanticdb-set-buffer obj) | ||
| 521 | (semantic-fetch-tags)))) | ||
| 522 | |||
| 523 | (defmethod semanticdb-needs-refresh-p ((obj semanticdb-table)) | ||
| 524 | "Return non-nil of OBJ's tag list is out of date. | ||
| 525 | The file associated with OBJ does not need to be in a buffer." | ||
| 526 | (let* ((ff (semanticdb-full-filename obj)) | ||
| 527 | (buff (semanticdb-in-buffer-p obj)) | ||
| 528 | ) | ||
| 529 | (if buff | ||
| 530 | (save-excursion | ||
| 531 | (set-buffer buff) | ||
| 532 | ;; Use semantic's magic tracker to determine of the buffer is up | ||
| 533 | ;; to date or not. | ||
| 534 | (not (semantic-parse-tree-up-to-date-p)) | ||
| 535 | ;; We assume that semanticdb is keeping itself up to date. | ||
| 536 | ;; via all the clever hooks | ||
| 537 | ) | ||
| 538 | ;; Buffer isn't loaded. The only clue we have is if the file | ||
| 539 | ;; is somehow different from our mark in the semanticdb table. | ||
| 540 | (let* ((stats (file-attributes ff)) | ||
| 541 | (actualsize (nth 7 stats)) | ||
| 542 | (actualmod (nth 5 stats)) | ||
| 543 | ) | ||
| 544 | |||
| 545 | (or (not (slot-boundp obj 'tags)) | ||
| 546 | ;; (not (oref obj tags)) --> not needed anymore? | ||
| 547 | (/= (or (oref obj fsize) 0) actualsize) | ||
| 548 | (not (equal (oref obj lastmodtime) actualmod)) | ||
| 549 | ) | ||
| 550 | )))) | ||
| 551 | |||
| 552 | |||
| 553 | ;;; Synchronization | ||
| 554 | ;; | ||
| 555 | (defmethod semanticdb-synchronize ((table semanticdb-abstract-table) | ||
| 556 | new-tags) | ||
| 557 | "Synchronize the table TABLE with some NEW-TAGS." | ||
| 558 | (oset table tags new-tags) | ||
| 559 | (oset table pointmax (point-max)) | ||
| 560 | (let ((fattr (file-attributes (semanticdb-full-filename table)))) | ||
| 561 | (oset table fsize (nth 7 fattr)) | ||
| 562 | (oset table lastmodtime (nth 5 fattr)) | ||
| 563 | ) | ||
| 564 | ;; Assume it is now up to date. | ||
| 565 | (oset table unmatched-syntax semantic-unmatched-syntax-cache) | ||
| 566 | ;; The lexical table should be good too. | ||
| 567 | (when (featurep 'semantic-lex-spp) | ||
| 568 | (oset table lexical-table (semantic-lex-spp-save-table))) | ||
| 569 | ;; this implies dirtyness | ||
| 570 | (semanticdb-set-dirty table) | ||
| 571 | |||
| 572 | ;; Synchronize the index | ||
| 573 | (when (slot-boundp table 'index) | ||
| 574 | (let ((idx (oref table index))) | ||
| 575 | (when idx (semanticdb-synchronize idx new-tags)))) | ||
| 576 | |||
| 577 | ;; Synchronize application caches. | ||
| 578 | (dolist (C (oref table cache)) | ||
| 579 | (semanticdb-synchronize C new-tags) | ||
| 580 | ) | ||
| 581 | |||
| 582 | ;; Update cross references | ||
| 583 | ;; (semanticdb-refresh-references table) | ||
| 584 | ) | ||
| 585 | |||
| 586 | (defmethod semanticdb-partial-synchronize ((table semanticdb-abstract-table) | ||
| 587 | new-tags) | ||
| 588 | "Synchronize the table TABLE where some NEW-TAGS changed." | ||
| 589 | ;; You might think we need to reset the tags, but since the partial | ||
| 590 | ;; parser splices the lists, we don't need to do anything | ||
| 591 | ;;(oset table tags new-tags) | ||
| 592 | ;; We do need to mark ourselves dirty. | ||
| 593 | (semanticdb-set-dirty table) | ||
| 594 | |||
| 595 | ;; The lexical table may be modified. | ||
| 596 | (when (featurep 'semantic-lex-spp) | ||
| 597 | (oset table lexical-table (semantic-lex-spp-save-table))) | ||
| 598 | |||
| 599 | ;; Incremental parser doesn't mokey around with this. | ||
| 600 | (oset table unmatched-syntax semantic-unmatched-syntax-cache) | ||
| 601 | |||
| 602 | ;; Synchronize the index | ||
| 603 | (when (slot-boundp table 'index) | ||
| 604 | (let ((idx (oref table index))) | ||
| 605 | (when idx (semanticdb-partial-synchronize idx new-tags)))) | ||
| 606 | |||
| 607 | ;; Synchronize application caches. | ||
| 608 | (dolist (C (oref table cache)) | ||
| 609 | (semanticdb-synchronize C new-tags) | ||
| 610 | ) | ||
| 611 | |||
| 612 | ;; Update cross references | ||
| 613 | ;;(when (semantic-find-tags-by-class 'include new-tags) | ||
| 614 | ;; (semanticdb-refresh-references table)) | ||
| 615 | ) | ||
| 616 | |||
| 617 | ;;; SAVE/LOAD | ||
| 618 | ;; | ||
| 619 | (defmethod semanticdb-save-db ((DB semanticdb-project-database) | ||
| 620 | &optional supress-questions) | ||
| 621 | "Cause a database to save itself. | ||
| 622 | The database base class does not save itself persistently. | ||
| 623 | Subclasses could save themselves to a file, or to a database, or other | ||
| 624 | form." | ||
| 625 | nil) | ||
| 626 | |||
| 627 | (defun semanticdb-save-current-db () | ||
| 628 | "Save the current tag database." | ||
| 629 | (interactive) | ||
| 630 | (message "Saving current tag summaries...") | ||
| 631 | (semanticdb-save-db semanticdb-current-database) | ||
| 632 | (message "Saving current tag summaries...done")) | ||
| 633 | |||
| 634 | (defun semanticdb-save-all-db () | ||
| 635 | "Save all semantic tag databases." | ||
| 636 | (interactive) | ||
| 637 | (message "Saving tag summaries...") | ||
| 638 | (mapc 'semanticdb-save-db semanticdb-database-list) | ||
| 639 | (message "Saving tag summaries...done")) | ||
| 640 | |||
| 641 | (defun semanticdb-save-all-db-idle () | ||
| 642 | "Save all semantic tag databases from idle time. | ||
| 643 | Exit the save between databases if there is user input." | ||
| 644 | (semantic-safe "Auto-DB Save: %S" | ||
| 645 | (semantic-exit-on-input 'semanticdb-idle-save | ||
| 646 | (mapc (lambda (db) | ||
| 647 | (semantic-throw-on-input 'semanticdb-idle-save) | ||
| 648 | (semanticdb-save-db db t)) | ||
| 649 | semanticdb-database-list)) | ||
| 650 | )) | ||
| 651 | |||
| 652 | ;;; Directory Project support | ||
| 653 | ;; | ||
| 654 | (defvar semanticdb-project-predicate-functions nil | ||
| 655 | "List of predicates to try that indicate a directory belongs to a project. | ||
| 656 | This list is used when `semanticdb-persistent-path' contains the value | ||
| 657 | 'project. If the predicate list is nil, then presume all paths are valid. | ||
| 658 | |||
| 659 | Project Management software (such as EDE and JDE) should add their own | ||
| 660 | predicates with `add-hook' to this variable, and semanticdb will save tag | ||
| 661 | caches in directories controlled by them.") | ||
| 662 | |||
| 663 | (defmethod semanticdb-write-directory-p ((obj semanticdb-project-database)) | ||
| 664 | "Return non-nil if OBJ should be written to disk. | ||
| 665 | Uses `semanticdb-persistent-path' to determine the return value." | ||
| 666 | nil) | ||
| 667 | |||
| 668 | ;;; Utilities | ||
| 669 | ;; | ||
| 670 | ;; What is the current database, are two tables of an equivalent mode, | ||
| 671 | ;; and what databases are a part of the same project. | ||
| 672 | (defun semanticdb-current-database () | ||
| 673 | "Return the currently active database." | ||
| 674 | (or semanticdb-current-database | ||
| 675 | (and default-directory | ||
| 676 | (semanticdb-create-database semanticdb-new-database-class | ||
| 677 | default-directory) | ||
| 678 | ) | ||
| 679 | nil)) | ||
| 680 | |||
| 681 | (defvar semanticdb-match-any-mode nil | ||
| 682 | "Non-nil to temporarilly search any major mode for a tag. | ||
| 683 | If a particular major mode wants to search any mode, put the | ||
| 684 | `semantic-match-any-mode' symbol onto the symbol of that major mode. | ||
| 685 | Do not set the value of this variable permanently.") | ||
| 686 | |||
| 687 | (defmacro semanticdb-with-match-any-mode (&rest body) | ||
| 688 | "A Semanticdb search occuring withing BODY will search tags in all modes. | ||
| 689 | This temporarilly sets `semanticdb-match-any-mode' while executing BODY." | ||
| 690 | `(let ((semanticdb-match-any-mode t)) | ||
| 691 | ,@body)) | ||
| 692 | (put 'semanticdb-with-match-any-mode 'lisp-indent-function 0) | ||
| 693 | |||
| 694 | (defmethod semanticdb-equivalent-mode-for-search (table &optional buffer) | ||
| 695 | "Return non-nil if TABLE's mode is equivalent to BUFFER. | ||
| 696 | See `semanticdb-equivalent-mode' for details. | ||
| 697 | This version is used during searches. Major-modes that opt | ||
| 698 | to set the `semantic-match-any-mode' property will be able to search | ||
| 699 | all files of any type." | ||
| 700 | (or (get major-mode 'semantic-match-any-mode) | ||
| 701 | semanticdb-match-any-mode | ||
| 702 | (semanticdb-equivalent-mode table buffer)) | ||
| 703 | ) | ||
| 704 | |||
| 705 | (defmethod semanticdb-equivalent-mode ((table semanticdb-abstract-table) &optional buffer) | ||
| 706 | "Return non-nil if TABLE's mode is equivalent to BUFFER. | ||
| 707 | Equivalent modes are specified by by `semantic-equivalent-major-modes' | ||
| 708 | local variable." | ||
| 709 | nil) | ||
| 710 | |||
| 711 | (defmethod semanticdb-equivalent-mode ((table semanticdb-table) &optional buffer) | ||
| 712 | "Return non-nil if TABLE's mode is equivalent to BUFFER. | ||
| 713 | Equivalent modes are specified by by `semantic-equivalent-major-modes' | ||
| 714 | local variable." | ||
| 715 | (save-excursion | ||
| 716 | (if buffer (set-buffer buffer)) | ||
| 717 | (or | ||
| 718 | ;; nil major mode in table means we don't know yet. Assume yes for now? | ||
| 719 | (null (oref table major-mode)) | ||
| 720 | ;; nil means the same as major-mode | ||
| 721 | (and (not semantic-equivalent-major-modes) | ||
| 722 | (mode-local-use-bindings-p major-mode (oref table major-mode))) | ||
| 723 | (and semantic-equivalent-major-modes | ||
| 724 | (member (oref table major-mode) semantic-equivalent-major-modes)) | ||
| 725 | ) | ||
| 726 | )) | ||
| 727 | |||
| 728 | |||
| 729 | ;;; Associations | ||
| 730 | ;; | ||
| 731 | ;; These routines determine associations between a file, and multiple | ||
| 732 | ;; associated databases. | ||
| 733 | |||
| 734 | (defcustom semanticdb-project-roots nil | ||
| 735 | "*List of directories, where each directory is the root of some project. | ||
| 736 | All subdirectories of a root project are considered a part of one project. | ||
| 737 | Values in this string can be overriden by project management programs | ||
| 738 | via the `semanticdb-project-root-functions' variable." | ||
| 739 | :group 'semanticdb | ||
| 740 | :type '(repeat string)) | ||
| 741 | |||
| 742 | (defvar semanticdb-project-root-functions nil | ||
| 743 | "List of functions used to determine a given directories project root. | ||
| 744 | Functions in this variable can override `semanticdb-project-roots'. | ||
| 745 | Functions set in the variable are given one argument (a directory) and | ||
| 746 | must return a string, (the root directory) or a list of strings (multiple | ||
| 747 | root directories in a more complex system). This variable should be used | ||
| 748 | by project management programs like EDE or JDE.") | ||
| 749 | |||
| 750 | (defvar semanticdb-project-system-databases nil | ||
| 751 | "List of databases containing system library information. | ||
| 752 | Mode authors can create their own system databases which know | ||
| 753 | detailed information about the system libraries for querying purposes. | ||
| 754 | Put those into this variable as a buffer-local, or mode-local | ||
| 755 | value.") | ||
| 756 | (make-variable-buffer-local 'semanticdb-project-system-databases) | ||
| 757 | |||
| 758 | (defvar semanticdb-search-system-databases t | ||
| 759 | "Non nil if search routines are to include a system database.") | ||
| 760 | |||
| 761 | (defun semanticdb-current-database-list (&optional dir) | ||
| 762 | "Return a list of databases associated with the current buffer. | ||
| 763 | If optional argument DIR is non-nil, then use DIR as the starting directory. | ||
| 764 | If this buffer has a database, but doesn't have a project associated | ||
| 765 | with it, return nil. | ||
| 766 | First, it checks `semanticdb-project-root-functions', and if that | ||
| 767 | has no results, it checks `semanticdb-project-roots'. If that fails, | ||
| 768 | it returns the results of function `semanticdb-current-database'. | ||
| 769 | Always append `semanticdb-project-system-databases' if | ||
| 770 | `semanticdb-search-system' is non-nil." | ||
| 771 | (let ((root nil) ; found root directory | ||
| 772 | (dbs nil) ; collected databases | ||
| 773 | (roots semanticdb-project-roots) ;all user roots | ||
| 774 | (dir (file-truename (or dir default-directory))) | ||
| 775 | ) | ||
| 776 | ;; Find the root based on project functions. | ||
| 777 | (setq root (run-hook-with-args-until-success | ||
| 778 | 'semanticdb-project-root-functions | ||
| 779 | dir)) | ||
| 780 | ;; Find roots based on strings | ||
| 781 | (while (and roots (not root)) | ||
| 782 | (let ((r (file-truename (car roots)))) | ||
| 783 | (if (string-match (concat "^" (regexp-quote r)) dir) | ||
| 784 | (setq root r))) | ||
| 785 | (setq roots (cdr roots))) | ||
| 786 | |||
| 787 | ;; If no roots are found, use this directory. | ||
| 788 | (unless root (setq root dir)) | ||
| 789 | |||
| 790 | ;; Find databases based on the root directory. | ||
| 791 | (when root | ||
| 792 | ;; The rootlist allows the root functions to possibly | ||
| 793 | ;; return several roots which are in different areas but | ||
| 794 | ;; all apart of the same system. | ||
| 795 | (let ((regexp (concat "^" (regexp-quote root))) | ||
| 796 | (adb semanticdb-database-list) ; all databases | ||
| 797 | ) | ||
| 798 | (while adb | ||
| 799 | ;; I don't like this part, but close enough. | ||
| 800 | (if (and (slot-boundp (car adb) 'reference-directory) | ||
| 801 | (string-match regexp (oref (car adb) reference-directory))) | ||
| 802 | (setq dbs (cons (car adb) dbs))) | ||
| 803 | (setq adb (cdr adb)))) | ||
| 804 | ) | ||
| 805 | ;; Add in system databases | ||
| 806 | (when semanticdb-search-system-databases | ||
| 807 | (setq dbs (nconc dbs semanticdb-project-system-databases))) | ||
| 808 | ;; Return | ||
| 809 | dbs)) | ||
| 810 | |||
| 811 | |||
| 812 | ;;; Generic Accessor Routines | ||
| 813 | ;; | ||
| 814 | ;; These routines can be used to get at tags in files w/out | ||
| 815 | ;; having to know a lot about semanticDB. | ||
| 816 | (defvar semanticdb-file-table-hash (make-hash-table :test 'equal) | ||
| 817 | "Hash table mapping file names to database tables.") | ||
| 818 | |||
| 819 | (defun semanticdb-file-table-object-from-hash (file) | ||
| 820 | "Retrieve a DB table from the hash for FILE. | ||
| 821 | Does not use `file-truename'." | ||
| 822 | (gethash file semanticdb-file-table-hash 'no-hit)) | ||
| 823 | |||
| 824 | (defun semanticdb-file-table-object-put-hash (file dbtable) | ||
| 825 | "For FILE, associate DBTABLE in the hash table." | ||
| 826 | (puthash file dbtable semanticdb-file-table-hash)) | ||
| 827 | |||
| 828 | (defun semanticdb-file-table-object (file &optional dontload) | ||
| 829 | "Return a semanticdb table belonging to FILE, make it up to date. | ||
| 830 | If file has database tags available in the database, return it. | ||
| 831 | If file does not have tags available, and DONTLOAD is nil, | ||
| 832 | then load the tags for FILE, and create a new table object for it. | ||
| 833 | DONTLOAD does not affect the creation of new database objects." | ||
| 834 | ;; (message "Object Translate: %s" file) | ||
| 835 | (when (file-exists-p file) | ||
| 836 | (let* ((default-directory (file-name-directory file)) | ||
| 837 | (tab (semanticdb-file-table-object-from-hash file)) | ||
| 838 | (fullfile nil)) | ||
| 839 | |||
| 840 | ;; If it is not in the cache, then extract the more traditional | ||
| 841 | ;; way by getting the database, and finding a table in that database. | ||
| 842 | ;; Once we have a table, add it to the hash. | ||
| 843 | (when (eq tab 'no-hit) | ||
| 844 | (setq fullfile (file-truename file)) | ||
| 845 | (let ((db (or ;; This line will pick up system databases. | ||
| 846 | (semanticdb-directory-loaded-p default-directory) | ||
| 847 | ;; this line will make a new one if needed. | ||
| 848 | (semanticdb-get-database default-directory)))) | ||
| 849 | (setq tab (semanticdb-file-table db fullfile)) | ||
| 850 | (when tab | ||
| 851 | (semanticdb-file-table-object-put-hash file tab) | ||
| 852 | (when (not (string= fullfile file)) | ||
| 853 | (semanticdb-file-table-object-put-hash fullfile tab) | ||
| 854 | )) | ||
| 855 | )) | ||
| 856 | |||
| 857 | (cond | ||
| 858 | ((and tab | ||
| 859 | ;; Is this in a buffer? | ||
| 860 | ;;(find-buffer-visiting (semanticdb-full-filename tab)) | ||
| 861 | (semanticdb-in-buffer-p tab) | ||
| 862 | ) | ||
| 863 | (save-excursion | ||
| 864 | ;;(set-buffer (find-buffer-visiting (semanticdb-full-filename tab))) | ||
| 865 | (semanticdb-set-buffer tab) | ||
| 866 | (semantic-fetch-tags) | ||
| 867 | ;; Return the table. | ||
| 868 | tab)) | ||
| 869 | ((and tab dontload) | ||
| 870 | ;; If we have table, and we don't want to load it, just return it. | ||
| 871 | tab) | ||
| 872 | ((and tab | ||
| 873 | ;; Is table fully loaded, or just a proxy? | ||
| 874 | (number-or-marker-p (oref tab pointmax)) | ||
| 875 | ;; Is this table up to date with the file? | ||
| 876 | (not (semanticdb-needs-refresh-p tab))) | ||
| 877 | ;; A-ok! | ||
| 878 | tab) | ||
| 879 | ((or (and fullfile (get-file-buffer fullfile)) | ||
| 880 | (get-file-buffer file)) | ||
| 881 | ;; are these two calls this faster than `find-buffer-visiting'? | ||
| 882 | |||
| 883 | ;; If FILE is being visited, but none of the above state is | ||
| 884 | ;; true (meaning, there is no table object associated with it) | ||
| 885 | ;; then it is a file not supported by Semantic, and can be safely | ||
| 886 | ;; ignored. | ||
| 887 | nil) | ||
| 888 | ((not dontload) ;; We must load the file. | ||
| 889 | ;; Full file should have been set by now. Debug why not? | ||
| 890 | (when (and (not tab) (not fullfile)) | ||
| 891 | ;; This case is if a 'nil is erroneously put into the hash table. This | ||
| 892 | ;; would need fixing | ||
| 893 | (setq fullfile (file-truename file)) | ||
| 894 | ) | ||
| 895 | |||
| 896 | ;; If we have a table, but no fullfile, that's ok. Lets get the filename | ||
| 897 | ;; from the table which is pre-truenamed. | ||
| 898 | (when (and (not fullfile) tab) | ||
| 899 | (setq fullfile (semanticdb-full-filename tab))) | ||
| 900 | |||
| 901 | (setq tab (semanticdb-create-table-for-file-not-in-buffer fullfile)) | ||
| 902 | |||
| 903 | ;; Save the new table. | ||
| 904 | (semanticdb-file-table-object-put-hash file tab) | ||
| 905 | (when (not (string= fullfile file)) | ||
| 906 | (semanticdb-file-table-object-put-hash fullfile tab) | ||
| 907 | ) | ||
| 908 | ;; Done! | ||
| 909 | tab) | ||
| 910 | (t | ||
| 911 | ;; Full file should have been set by now. Debug why not? | ||
| 912 | ;; One person found this. Is it a file that failed to parse | ||
| 913 | ;; in the past? | ||
| 914 | (when (not fullfile) | ||
| 915 | (setq fullfile (file-truename file))) | ||
| 916 | |||
| 917 | ;; We were asked not to load the file in and parse it. | ||
| 918 | ;; Instead just create a database table with no tags | ||
| 919 | ;; and a claim of being empty. | ||
| 920 | ;; | ||
| 921 | ;; This will give us a starting point for storing | ||
| 922 | ;; database cross-references so when it is loaded, | ||
| 923 | ;; the cross-references will fire and caches will | ||
| 924 | ;; be cleaned. | ||
| 925 | (let ((ans (semanticdb-create-table-for-file file))) | ||
| 926 | (setq tab (cdr ans)) | ||
| 927 | |||
| 928 | ;; Save the new table. | ||
| 929 | (semanticdb-file-table-object-put-hash file tab) | ||
| 930 | (when (not (string= fullfile file)) | ||
| 931 | (semanticdb-file-table-object-put-hash fullfile tab) | ||
| 932 | ) | ||
| 933 | ;; Done! | ||
| 934 | tab)) | ||
| 935 | ) | ||
| 936 | ))) | ||
| 937 | |||
| 938 | (defvar semanticdb-out-of-buffer-create-table-fcn nil | ||
| 939 | "When non-nil, a function for creating a semanticdb table. | ||
| 940 | This should take a filename to be parsed.") | ||
| 941 | (make-variable-buffer-local 'semanticdb-out-of-buffer-create-table-fcn) | ||
| 942 | |||
| 943 | (defun semanticdb-create-table-for-file-not-in-buffer (filename) | ||
| 944 | "Create a table for the file FILENAME. | ||
| 945 | If there are no language specific configurations, this | ||
| 946 | function will read in the buffer, parse it, and kill the buffer." | ||
| 947 | (if (and semanticdb-out-of-buffer-create-table-fcn | ||
| 948 | (not (file-remote-p filename))) | ||
| 949 | ;; Use external parser only of the file is accessible to the | ||
| 950 | ;; local file system. | ||
| 951 | (funcall semanticdb-out-of-buffer-create-table-fcn filename) | ||
| 952 | (save-excursion | ||
| 953 | (let* ( ;; Remember the buffer to kill | ||
| 954 | (kill-buffer-flag (find-buffer-visiting filename)) | ||
| 955 | (buffer-to-kill (or kill-buffer-flag | ||
| 956 | (semantic-find-file-noselect filename t)))) | ||
| 957 | |||
| 958 | ;; This shouldn't ever be set. Debug some issue here? | ||
| 959 | ;; (when kill-buffer-flag (debug)) | ||
| 960 | |||
| 961 | (set-buffer buffer-to-kill) | ||
| 962 | ;; Find file should automatically do this for us. | ||
| 963 | ;; Sometimes the DB table doesn't contains tags and needs | ||
| 964 | ;; a refresh. For example, when the file is loaded for | ||
| 965 | ;; the first time, and the idle scheduler didn't get a | ||
| 966 | ;; chance to trigger a parse before the file buffer is | ||
| 967 | ;; killed. | ||
| 968 | (when semanticdb-current-table | ||
| 969 | (semantic-fetch-tags)) | ||
| 970 | (prog1 | ||
| 971 | semanticdb-current-table | ||
| 972 | (when (not kill-buffer-flag) | ||
| 973 | ;; If we had to find the file, then we should kill it | ||
| 974 | ;; to keep the master buffer list clean. | ||
| 975 | (kill-buffer buffer-to-kill) | ||
| 976 | ))))) | ||
| 977 | ) | ||
| 978 | |||
| 979 | (defun semanticdb-file-stream (file) | ||
| 980 | "Return a list of tags belonging to FILE. | ||
| 981 | If file has database tags available in the database, return them. | ||
| 982 | If file does not have tags available, then load the file, and create them." | ||
| 983 | (let ((table (semanticdb-file-table-object file))) | ||
| 984 | (when table | ||
| 985 | (semanticdb-get-tags table)))) | ||
| 986 | |||
| 987 | (provide 'semantic/db) | ||
| 988 | |||
| 989 | ;;; semanticdb.el ends here | ||
diff --git a/lisp/cedet/semantic/decorate.el b/lisp/cedet/semantic/decorate.el new file mode 100644 index 00000000000..cde0b25d03b --- /dev/null +++ b/lisp/cedet/semantic/decorate.el | |||
| @@ -0,0 +1,320 @@ | |||
| 1 | ;;; semantic-decorate.el --- Utilities for decorating/highlighting tokens. | ||
| 2 | |||
| 3 | ;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2009 | ||
| 4 | ;;; Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | ||
| 7 | ;; Keywords: syntax | ||
| 8 | |||
| 9 | ;; This file is part of GNU Emacs. | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 14 | ;; (at your option) any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 23 | |||
| 24 | ;;; Commentary: | ||
| 25 | ;; | ||
| 26 | ;; Text representing a semantic tag is wrapped in an overlay. | ||
| 27 | ;; This overlay can be used for highlighting, or setting other | ||
| 28 | ;; editing properties on a tag, such as "read only." | ||
| 29 | ;; | ||
| 30 | |||
| 31 | (require 'semantic) | ||
| 32 | (require 'pulse) | ||
| 33 | |||
| 34 | ;;; Code: | ||
| 35 | |||
| 36 | ;;; Highlighting Basics | ||
| 37 | (defun semantic-highlight-tag (tag &optional face) | ||
| 38 | "Specify that TAG should be highlighted. | ||
| 39 | Optional FACE specifies the face to use." | ||
| 40 | (let ((o (semantic-tag-overlay tag))) | ||
| 41 | (semantic-overlay-put o 'old-face | ||
| 42 | (cons (semantic-overlay-get o 'face) | ||
| 43 | (semantic-overlay-get o 'old-face))) | ||
| 44 | (semantic-overlay-put o 'face (or face 'semantic-tag-highlight-face)) | ||
| 45 | )) | ||
| 46 | |||
| 47 | (defun semantic-unhighlight-tag (tag) | ||
| 48 | "Unhighlight TAG, restoring it's previous face." | ||
| 49 | (let ((o (semantic-tag-overlay tag))) | ||
| 50 | (semantic-overlay-put o 'face (car (semantic-overlay-get o 'old-face))) | ||
| 51 | (semantic-overlay-put o 'old-face (cdr (semantic-overlay-get o 'old-face))) | ||
| 52 | )) | ||
| 53 | |||
| 54 | ;;; Momentary Highlighting - One line | ||
| 55 | (defun semantic-momentary-highlight-one-tag-line (tag &optional face) | ||
| 56 | "Highlight the first line of TAG, unhighlighting before next command. | ||
| 57 | Optional argument FACE specifies the face to do the highlighting." | ||
| 58 | (save-excursion | ||
| 59 | ;; Go to first line in tag | ||
| 60 | (semantic-go-to-tag tag) | ||
| 61 | (pulse-momentary-highlight-one-line (point)))) | ||
| 62 | |||
| 63 | ;;; Momentary Highlighting - Whole Tag | ||
| 64 | (defun semantic-momentary-highlight-tag (tag &optional face) | ||
| 65 | "Highlight TAG, removing highlighting when the user hits a key. | ||
| 66 | Optional argument FACE is the face to use for highlighting. | ||
| 67 | If FACE is not specified, then `highlight' will be used." | ||
| 68 | (when (semantic-tag-with-position-p tag) | ||
| 69 | (if (not (semantic-overlay-p (semantic-tag-overlay tag))) | ||
| 70 | ;; No overlay, but a position. Highlight the first line only. | ||
| 71 | (semantic-momentary-highlight-one-tag-line tag face) | ||
| 72 | ;; The tag has an overlay, highlight the whole thing | ||
| 73 | (pulse-momentary-highlight-overlay (semantic-tag-overlay tag) | ||
| 74 | face) | ||
| 75 | ))) | ||
| 76 | |||
| 77 | (defun semantic-set-tag-face (tag face) | ||
| 78 | "Specify that TAG should use FACE for display." | ||
| 79 | (semantic-overlay-put (semantic-tag-overlay tag) 'face face)) | ||
| 80 | |||
| 81 | (defun semantic-set-tag-invisible (tag &optional visible) | ||
| 82 | "Enable the text in TAG to be made invisible. | ||
| 83 | If VISIBLE is non-nil, make the text visible." | ||
| 84 | (semantic-overlay-put (semantic-tag-overlay tag) 'invisible | ||
| 85 | (not visible))) | ||
| 86 | |||
| 87 | (defun semantic-tag-invisible-p (tag) | ||
| 88 | "Return non-nil if TAG is invisible." | ||
| 89 | (semantic-overlay-get (semantic-tag-overlay tag) 'invisible)) | ||
| 90 | |||
| 91 | (defun semantic-set-tag-intangible (tag &optional tangible) | ||
| 92 | "Enable the text in TAG to be made intangible. | ||
| 93 | If TANGIBLE is non-nil, make the text visible. | ||
| 94 | This function does not have meaning in XEmacs because it seems that | ||
| 95 | the extent 'intangible' property does not exist." | ||
| 96 | (semantic-overlay-put (semantic-tag-overlay tag) 'intangible | ||
| 97 | (not tangible))) | ||
| 98 | |||
| 99 | (defun semantic-tag-intangible-p (tag) | ||
| 100 | "Return non-nil if TAG is intangible. | ||
| 101 | This function does not have meaning in XEmacs because it seems that | ||
| 102 | the extent 'intangible' property does not exist." | ||
| 103 | (semantic-overlay-get (semantic-tag-overlay tag) 'intangible)) | ||
| 104 | |||
| 105 | (defun semantic-overlay-signal-read-only | ||
| 106 | (overlay after start end &optional len) | ||
| 107 | "Hook used in modification hooks to prevent modification. | ||
| 108 | Allows deletion of the entire text. | ||
| 109 | Argument OVERLAY, AFTER, START, END, and LEN are passed in by the system." | ||
| 110 | ;; Stolen blithly from cpp.el in Emacs 21.1 | ||
| 111 | (if (and (not after) | ||
| 112 | (or (< (semantic-overlay-start overlay) start) | ||
| 113 | (> (semantic-overlay-end overlay) end))) | ||
| 114 | (error "This text is read only"))) | ||
| 115 | |||
| 116 | (defun semantic-set-tag-read-only (tag &optional writable) | ||
| 117 | "Enable the text in TAG to be made read-only. | ||
| 118 | Optional argument WRITABLE should be non-nil to make the text writable | ||
| 119 | instead of read-only." | ||
| 120 | (let ((o (semantic-tag-overlay tag)) | ||
| 121 | (hook (if writable nil '(semantic-overlay-signal-read-only)))) | ||
| 122 | (if (featurep 'xemacs) | ||
| 123 | ;; XEmacs extents have a 'read-only' property. | ||
| 124 | (semantic-overlay-put o 'read-only (not writable)) | ||
| 125 | (semantic-overlay-put o 'modification-hooks hook) | ||
| 126 | (semantic-overlay-put o 'insert-in-front-hooks hook) | ||
| 127 | (semantic-overlay-put o 'insert-behind-hooks hook)))) | ||
| 128 | |||
| 129 | (defun semantic-tag-read-only-p (tag) | ||
| 130 | "Return non-nil if the current TAG is marked read only." | ||
| 131 | (let ((o (semantic-tag-overlay tag))) | ||
| 132 | (if (featurep 'xemacs) | ||
| 133 | ;; XEmacs extents have a 'read-only' property. | ||
| 134 | (semantic-overlay-get o 'read-only) | ||
| 135 | (member 'semantic-overlay-signal-read-only | ||
| 136 | (semantic-overlay-get o 'modification-hooks))))) | ||
| 137 | |||
| 138 | ;;; backwards compatability | ||
| 139 | |||
| 140 | (semantic-alias-obsolete 'semantic-highlight-token | ||
| 141 | 'semantic-highlight-tag) | ||
| 142 | (semantic-alias-obsolete 'semantic-unhighlight-token | ||
| 143 | 'semantic-unhighlight-tag) | ||
| 144 | (semantic-alias-obsolete 'semantic-momentary-highlight-token | ||
| 145 | 'semantic-momentary-highlight-tag) | ||
| 146 | (semantic-alias-obsolete 'semantic-set-token-face | ||
| 147 | 'semantic-set-tag-face) | ||
| 148 | (semantic-alias-obsolete 'semantic-set-token-invisible | ||
| 149 | 'semantic-set-tag-invisible) | ||
| 150 | (semantic-alias-obsolete 'semantic-token-invisible-p | ||
| 151 | 'semantic-tag-invisible-p) | ||
| 152 | (semantic-alias-obsolete 'semantic-set-token-intangible | ||
| 153 | 'semantic-set-tag-intangible) | ||
| 154 | (semantic-alias-obsolete 'semantic-token-intangible-p | ||
| 155 | 'semantic-tag-intangible-p) | ||
| 156 | (semantic-alias-obsolete 'semantic-set-token-read-only | ||
| 157 | 'semantic-set-tag-read-only) | ||
| 158 | (semantic-alias-obsolete 'semantic-token-read-only-p | ||
| 159 | 'semantic-tag-read-only-p) | ||
| 160 | |||
| 161 | ;;; Secondary overlays | ||
| 162 | ;; | ||
| 163 | ;; Some types of decoration require a second overlay to be made. | ||
| 164 | ;; It could be for images, arrows, or whatever. | ||
| 165 | ;; We need a way to create such an overlay, and make sure it | ||
| 166 | ;; gets whacked, but doesn't show up in the master list | ||
| 167 | ;; of overlays used for searching. | ||
| 168 | (defun semantic-tag-secondary-overlays (tag) | ||
| 169 | "Return a list of secondary overlays active on TAG." | ||
| 170 | (semantic--tag-get-property tag 'secondary-overlays)) | ||
| 171 | |||
| 172 | (defun semantic-tag-create-secondary-overlay (tag &optional link-hook) | ||
| 173 | "Create a secondary overlay for TAG. | ||
| 174 | Returns an overlay. The overlay is also saved in TAG. | ||
| 175 | LINK-HOOK is a function called whenever TAG is to be linked into | ||
| 176 | a buffer. It should take TAG and OVERLAY as arguments. | ||
| 177 | The LINK-HOOK should be used to position and set properties on the | ||
| 178 | generated secondary overlay." | ||
| 179 | (if (not (semantic-tag-overlay tag)) | ||
| 180 | ;; do nothing if there is no overlay | ||
| 181 | nil | ||
| 182 | (let* ((os (semantic-tag-start tag)) | ||
| 183 | (oe (semantic-tag-end tag)) | ||
| 184 | (o (semantic-make-overlay os oe (semantic-tag-buffer tag) t)) | ||
| 185 | (attr (semantic-tag-secondary-overlays tag)) | ||
| 186 | ) | ||
| 187 | (semantic--tag-put-property tag 'secondary-overlays (cons o attr)) | ||
| 188 | (semantic-overlay-put o 'semantic-secondary t) | ||
| 189 | (semantic-overlay-put o 'semantic-link-hook link-hook) | ||
| 190 | (semantic-tag-add-hook tag 'link-hook 'semantic--tag-link-secondary-overlays) | ||
| 191 | (semantic-tag-add-hook tag 'unlink-hook 'semantic--tag-unlink-secondary-overlays) | ||
| 192 | (semantic-tag-add-hook tag 'unlink-copy-hook 'semantic--tag-unlink-copy-secondary-overlays) | ||
| 193 | (run-hook-with-args link-hook tag o) | ||
| 194 | o))) | ||
| 195 | |||
| 196 | (defun semantic-tag-get-secondary-overlay (tag property) | ||
| 197 | "Return secondary overlays from TAG with PROPERTY. | ||
| 198 | PROPERTY is a symbol and all overlays with that symbol are returned.." | ||
| 199 | (let* ((olsearch (semantic-tag-secondary-overlays tag)) | ||
| 200 | (o nil)) | ||
| 201 | (while olsearch | ||
| 202 | (when (semantic-overlay-get (car olsearch) property) | ||
| 203 | (setq o (cons (car olsearch) o))) | ||
| 204 | (setq olsearch (cdr olsearch))) | ||
| 205 | o)) | ||
| 206 | |||
| 207 | (defun semantic-tag-delete-secondary-overlay (tag overlay-or-property) | ||
| 208 | "Delete from TAG the secondary overlay OVERLAY-OR-PROPERTY. | ||
| 209 | If OVERLAY-OR-PROPERTY is an overlay, delete that overlay. | ||
| 210 | If OVERLAY-OR-PROPERTY is a symbol, find the overlay with that property." | ||
| 211 | (let* ((o overlay-or-property)) | ||
| 212 | (if (semantic-overlay-p o) | ||
| 213 | (setq o (list o)) | ||
| 214 | (setq o (semantic-tag-get-secondary-overlay tag overlay-or-property))) | ||
| 215 | (while (semantic-overlay-p (car o)) | ||
| 216 | ;; We don't really need to worry about the hooks. | ||
| 217 | ;; They will clean themselves up eventually ?? | ||
| 218 | (semantic--tag-put-property | ||
| 219 | tag 'secondary-overlays | ||
| 220 | (delete (car o) (semantic-tag-secondary-overlays tag))) | ||
| 221 | (semantic-overlay-delete (car o)) | ||
| 222 | (setq o (cdr o))))) | ||
| 223 | |||
| 224 | (defun semantic--tag-unlink-copy-secondary-overlays (tag) | ||
| 225 | "Unlink secondary overlays from TAG which is a copy. | ||
| 226 | This means we don't destroy the overlays, only remove reference | ||
| 227 | from them in TAG." | ||
| 228 | (let ((ol (semantic-tag-secondary-overlays tag))) | ||
| 229 | (while ol | ||
| 230 | ;; Else, remove all traces of ourself from the tag | ||
| 231 | ;; Note to self: Does this prevent multiple types of secondary | ||
| 232 | ;; overlays per tag? | ||
| 233 | (semantic-tag-remove-hook tag 'link-hook 'semantic--tag-link-secondary-overlays) | ||
| 234 | (semantic-tag-remove-hook tag 'unlink-hook 'semantic--tag-unlink-secondary-overlays) | ||
| 235 | (semantic-tag-remove-hook tag 'unlink-copy-hook 'semantic--tag-unlink-copy-secondary-overlays) | ||
| 236 | ;; Next! | ||
| 237 | (setq ol (cdr ol))) | ||
| 238 | (semantic--tag-put-property tag 'secondary-overlays nil) | ||
| 239 | )) | ||
| 240 | |||
| 241 | (defun semantic--tag-unlink-secondary-overlays (tag) | ||
| 242 | "Unlink secondary overlays from TAG." | ||
| 243 | (let ((ol (semantic-tag-secondary-overlays tag)) | ||
| 244 | (nl nil)) | ||
| 245 | (while ol | ||
| 246 | (if (semantic-overlay-get (car ol) 'semantic-link-hook) | ||
| 247 | ;; Only put in a proxy if there is a link-hook. If there is no link-hook | ||
| 248 | ;; the decorating mode must know when tags are unlinked on its own. | ||
| 249 | (setq nl (cons (semantic-overlay-get (car ol) 'semantic-link-hook) | ||
| 250 | nl)) | ||
| 251 | ;; Else, remove all traces of ourself from the tag | ||
| 252 | ;; Note to self: Does this prevent multiple types of secondary | ||
| 253 | ;; overlays per tag? | ||
| 254 | (semantic-tag-remove-hook tag 'link-hook 'semantic--tag-link-secondary-overlays) | ||
| 255 | (semantic-tag-remove-hook tag 'unlink-hook 'semantic--tag-unlink-secondary-overlays) | ||
| 256 | (semantic-tag-remove-hook tag 'unlink-copy-hook 'semantic--tag-unlink-copy-secondary-overlays) | ||
| 257 | ) | ||
| 258 | (semantic-overlay-delete (car ol)) | ||
| 259 | (setq ol (cdr ol))) | ||
| 260 | (semantic--tag-put-property tag 'secondary-overlays (nreverse nl)) | ||
| 261 | )) | ||
| 262 | |||
| 263 | (defun semantic--tag-link-secondary-overlays (tag) | ||
| 264 | "Unlink secondary overlays from TAG." | ||
| 265 | (let ((ol (semantic-tag-secondary-overlays tag))) | ||
| 266 | ;; Wipe out old values. | ||
| 267 | (semantic--tag-put-property tag 'secondary-overlays nil) | ||
| 268 | ;; Run all the link hooks. | ||
| 269 | (while ol | ||
| 270 | (semantic-tag-create-secondary-overlay tag (car ol)) | ||
| 271 | (setq ol (cdr ol))) | ||
| 272 | )) | ||
| 273 | |||
| 274 | ;;; Secondary Overlay Uses | ||
| 275 | ;; | ||
| 276 | ;; States to put on tags that depend on a secondary overlay. | ||
| 277 | (defun semantic-set-tag-folded (tag &optional folded) | ||
| 278 | "Fold TAG, such that only the first line of text is shown. | ||
| 279 | Optional argument FOLDED should be non-nil to fold the tag. | ||
| 280 | nil implies the tag should be fully shown." | ||
| 281 | ;; If they are different, do the deed. | ||
| 282 | (let ((o (semantic-tag-folded-p tag))) | ||
| 283 | (if (not folded) | ||
| 284 | ;; We unfold. | ||
| 285 | (when o | ||
| 286 | (semantic-tag-delete-secondary-overlay tag 'semantic-folded)) | ||
| 287 | (unless o | ||
| 288 | ;; Add the foldn | ||
| 289 | (setq o (semantic-tag-create-secondary-overlay tag)) | ||
| 290 | ;; mark as folded | ||
| 291 | (semantic-overlay-put o 'semantic-folded t) | ||
| 292 | ;; Move to cover end of tag | ||
| 293 | (save-excursion | ||
| 294 | (goto-char (semantic-tag-start tag)) | ||
| 295 | (end-of-line) | ||
| 296 | (semantic-overlay-move o (point) (semantic-tag-end tag))) | ||
| 297 | ;; We need to modify the invisibility spec for this to | ||
| 298 | ;; work. | ||
| 299 | (if (or (eq buffer-invisibility-spec t) | ||
| 300 | (not (assoc 'semantic-fold buffer-invisibility-spec))) | ||
| 301 | (add-to-invisibility-spec '(semantic-fold . t))) | ||
| 302 | (semantic-overlay-put o 'invisible 'semantic-fold) | ||
| 303 | (overlay-put o 'isearch-open-invisible | ||
| 304 | 'semantic-set-tag-folded-isearch))) | ||
| 305 | )) | ||
| 306 | |||
| 307 | (defun semantic-set-tag-folded-isearch (overlay) | ||
| 308 | "Called by isearch if it discovers text in the folded region. | ||
| 309 | OVERLAY is passed in by isearch." | ||
| 310 | (semantic-set-tag-folded (semantic-current-tag) nil) | ||
| 311 | ) | ||
| 312 | |||
| 313 | (defun semantic-tag-folded-p (tag) | ||
| 314 | "Non-nil if TAG is currently folded." | ||
| 315 | (semantic-tag-get-secondary-overlay tag 'semantic-folded) | ||
| 316 | ) | ||
| 317 | |||
| 318 | (provide 'semantic/decorate) | ||
| 319 | |||
| 320 | ;;; semantic-decorate.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..7be7a3a816b --- /dev/null +++ b/lisp/cedet/semantic/lex-spp.el | |||
| @@ -0,0 +1,1187 @@ | |||
| 1 | ;;; semantic-lex-spp.el --- Semantic Lexical Pre-processor | ||
| 2 | |||
| 3 | ;;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | ||
| 6 | |||
| 7 | ;; This file is part of GNU Emacs. | ||
| 8 | |||
| 9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 10 | ;; it under the terms of the GNU General Public License as published by | ||
| 11 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 12 | ;; (at your option) any later version. | ||
| 13 | |||
| 14 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | ;; GNU General Public License for more details. | ||
| 18 | |||
| 19 | ;; You should have received a copy of the GNU General Public License | ||
| 20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | |||
| 22 | ;;; Commentary: | ||
| 23 | ;; | ||
| 24 | ;; The Semantic Preprocessor works with semantic-lex to provide a phase | ||
| 25 | ;; during lexical analysis to do the work of a pre-processor. | ||
| 26 | ;; | ||
| 27 | ;; A pre-processor identifies lexical syntax mixed in with another language | ||
| 28 | ;; and replaces some keyword tokens with streams of alternate tokens. | ||
| 29 | ;; | ||
| 30 | ;; If you use SPP in your language, be sure to specify this in your | ||
| 31 | ;; semantic language setup function: | ||
| 32 | ;; | ||
| 33 | ;; (add-hook 'semantic-lex-reset-hooks 'semantic-lex-spp-reset-hook nil t) | ||
| 34 | ;; | ||
| 35 | ;; | ||
| 36 | ;; Special Lexical Tokens: | ||
| 37 | ;; | ||
| 38 | ;; There are several special lexical tokens that are used by the | ||
| 39 | ;; Semantic PreProcessor lexer. They are: | ||
| 40 | ;; | ||
| 41 | ;; Declarations: | ||
| 42 | ;; spp-macro-def - A definition of a lexical macro. | ||
| 43 | ;; spp-macro-undef - A removal of a definition of a lexical macro. | ||
| 44 | ;; spp-system-include - A system level include file | ||
| 45 | ;; spp-include - An include file | ||
| 46 | ;; spp-concat - A lexical token representing textual concatenation | ||
| 47 | ;; of symbol parts. | ||
| 48 | ;; | ||
| 49 | ;; Operational tokens: | ||
| 50 | ;; spp-arg-list - Represents an argument list to a macro. | ||
| 51 | ;; spp-symbol-merge - A request for multiple symbols to be textually merged. | ||
| 52 | ;; | ||
| 53 | ;;; TODO: | ||
| 54 | ;; | ||
| 55 | ;; Use `semantic-push-parser-warning' for situations where there are likely | ||
| 56 | ;; macros that are undefined unexpectedly, or other problem. | ||
| 57 | ;; | ||
| 58 | ;; TODO: | ||
| 59 | ;; | ||
| 60 | ;; Try to handle the case of: | ||
| 61 | ;; | ||
| 62 | ;; #define NN namespace nn { | ||
| 63 | ;; #define NN_END } | ||
| 64 | ;; | ||
| 65 | ;; NN | ||
| 66 | ;; int mydecl() {} | ||
| 67 | ;; NN_END | ||
| 68 | ;; | ||
| 69 | |||
| 70 | (require 'semantic/lex) | ||
| 71 | |||
| 72 | ;;; Code: | ||
| 73 | (defvar semantic-lex-spp-macro-symbol-obarray nil | ||
| 74 | "Table of macro keywords used by the Semantic Preprocessor. | ||
| 75 | These symbols will be used in addition to those in | ||
| 76 | `semantic-lex-spp-dynamic-macro-symbol-obarray'.") | ||
| 77 | (make-variable-buffer-local 'semantic-lex-spp-macro-symbol-obarray) | ||
| 78 | |||
| 79 | (defvar semantic-lex-spp-project-macro-symbol-obarray nil | ||
| 80 | "Table of macro keywords for this project. | ||
| 81 | These symbols will be used in addition to those in | ||
| 82 | `semantic-lex-spp-dynamic-macro-symbol-obarray'.") | ||
| 83 | (make-variable-buffer-local 'semantic-lex-spp-project-macro-symbol-obarray) | ||
| 84 | |||
| 85 | (defvar semantic-lex-spp-dynamic-macro-symbol-obarray nil | ||
| 86 | "Table of macro keywords used during lexical analysis. | ||
| 87 | Macros are lexical symbols which are replaced by other lexical | ||
| 88 | tokens during lexical analysis. During analysis symbols can be | ||
| 89 | added and removed from this symbol table.") | ||
| 90 | (make-variable-buffer-local 'semantic-lex-spp-dynamic-macro-symbol-obarray) | ||
| 91 | |||
| 92 | (defvar semantic-lex-spp-dynamic-macro-symbol-obarray-stack nil | ||
| 93 | "A stack of obarrays for temporarilly scoped macro values.") | ||
| 94 | (make-variable-buffer-local 'semantic-lex-spp-dynamic-macro-symbol-obarray-stack) | ||
| 95 | |||
| 96 | (defvar semantic-lex-spp-expanded-macro-stack nil | ||
| 97 | "The stack of lexical SPP macros we have expanded.") | ||
| 98 | ;; The above is not buffer local. Some macro expansions need to be | ||
| 99 | ;; dumped into a secondary buffer for re-lexing. | ||
| 100 | |||
| 101 | ;;; NON-RECURSIVE MACRO STACK | ||
| 102 | ;; C Pre-processor does not allow recursive macros. Here are some utils | ||
| 103 | ;; for managing the symbol stack of where we've been. | ||
| 104 | |||
| 105 | (defmacro semantic-lex-with-macro-used (name &rest body) | ||
| 106 | "With the macro NAME currently being expanded, execute BODY. | ||
| 107 | Pushes NAME into the macro stack. The above stack is checked | ||
| 108 | by `semantic-lex-spp-symbol' to not return true for any symbol | ||
| 109 | currently being expanded." | ||
| 110 | `(unwind-protect | ||
| 111 | (progn | ||
| 112 | (push ,name semantic-lex-spp-expanded-macro-stack) | ||
| 113 | ,@body) | ||
| 114 | (pop semantic-lex-spp-expanded-macro-stack))) | ||
| 115 | (put 'semantic-lex-with-macro-used 'lisp-indent-function 1) | ||
| 116 | |||
| 117 | (add-hook | ||
| 118 | 'edebug-setup-hook | ||
| 119 | #'(lambda () | ||
| 120 | |||
| 121 | (def-edebug-spec semantic-lex-with-macro-used | ||
| 122 | (symbolp def-body) | ||
| 123 | ) | ||
| 124 | |||
| 125 | )) | ||
| 126 | |||
| 127 | ;;; MACRO TABLE UTILS | ||
| 128 | ;; | ||
| 129 | ;; The dynamic macro table is a buffer local variable that is modified | ||
| 130 | ;; during the analysis. OBARRAYs are used, so the language must | ||
| 131 | ;; have symbols that are compatible with Emacs Lisp symbols. | ||
| 132 | ;; | ||
| 133 | (defsubst semantic-lex-spp-symbol (name) | ||
| 134 | "Return spp symbol with NAME or nil if not found. | ||
| 135 | The searcy priority is: | ||
| 136 | 1. DYNAMIC symbols | ||
| 137 | 2. PROJECT specified symbols. | ||
| 138 | 3. SYSTEM specified symbols." | ||
| 139 | (and | ||
| 140 | ;; Only strings... | ||
| 141 | (stringp name) | ||
| 142 | ;; Make sure we don't recurse. | ||
| 143 | (not (member name semantic-lex-spp-expanded-macro-stack)) | ||
| 144 | ;; Do the check of the various tables. | ||
| 145 | (or | ||
| 146 | ;; DYNAMIC | ||
| 147 | (and (arrayp semantic-lex-spp-dynamic-macro-symbol-obarray) | ||
| 148 | (intern-soft name semantic-lex-spp-dynamic-macro-symbol-obarray)) | ||
| 149 | ;; PROJECT | ||
| 150 | (and (arrayp semantic-lex-spp-project-macro-symbol-obarray) | ||
| 151 | (intern-soft name semantic-lex-spp-project-macro-symbol-obarray)) | ||
| 152 | ;; SYSTEM | ||
| 153 | (and (arrayp semantic-lex-spp-macro-symbol-obarray) | ||
| 154 | (intern-soft name semantic-lex-spp-macro-symbol-obarray)) | ||
| 155 | ;; ... | ||
| 156 | ))) | ||
| 157 | |||
| 158 | (defsubst semantic-lex-spp-symbol-p (name) | ||
| 159 | "Return non-nil if a keyword with NAME exists in any keyword table." | ||
| 160 | (if (semantic-lex-spp-symbol name) | ||
| 161 | t)) | ||
| 162 | |||
| 163 | (defsubst semantic-lex-spp-dynamic-map () | ||
| 164 | "Return the dynamic macro map for the current buffer." | ||
| 165 | (or semantic-lex-spp-dynamic-macro-symbol-obarray | ||
| 166 | (setq semantic-lex-spp-dynamic-macro-symbol-obarray | ||
| 167 | (make-vector 13 0)))) | ||
| 168 | |||
| 169 | (defsubst semantic-lex-spp-dynamic-map-stack () | ||
| 170 | "Return the dynamic macro map for the current buffer." | ||
| 171 | (or semantic-lex-spp-dynamic-macro-symbol-obarray-stack | ||
| 172 | (setq semantic-lex-spp-dynamic-macro-symbol-obarray-stack | ||
| 173 | (make-vector 13 0)))) | ||
| 174 | |||
| 175 | (defun semantic-lex-spp-symbol-set (name value &optional obarray-in) | ||
| 176 | "Set value of spp symbol with NAME to VALUE and return VALUE. | ||
| 177 | If optional OBARRAY-IN is non-nil, then use that obarray instead of | ||
| 178 | the dynamic map." | ||
| 179 | (if (and (stringp value) (string= value "")) (setq value nil)) | ||
| 180 | (set (intern name (or obarray-in | ||
| 181 | (semantic-lex-spp-dynamic-map))) | ||
| 182 | value)) | ||
| 183 | |||
| 184 | (defsubst semantic-lex-spp-symbol-remove (name &optional obarray) | ||
| 185 | "Remove the spp symbol with NAME. | ||
| 186 | If optional OBARRAY is non-nil, then use that obarray instead of | ||
| 187 | the dynamic map." | ||
| 188 | (unintern name (or obarray | ||
| 189 | (semantic-lex-spp-dynamic-map)))) | ||
| 190 | |||
| 191 | (defun semantic-lex-spp-symbol-push (name value) | ||
| 192 | "Push macro NAME with VALUE into the map. | ||
| 193 | Reverse with `semantic-lex-spp-symbol-pop'." | ||
| 194 | (let* ((map (semantic-lex-spp-dynamic-map)) | ||
| 195 | (stack (semantic-lex-spp-dynamic-map-stack)) | ||
| 196 | (mapsym (intern name map)) | ||
| 197 | (stacksym (intern name stack)) | ||
| 198 | (mapvalue (when (boundp mapsym) (symbol-value mapsym))) | ||
| 199 | ) | ||
| 200 | (when (boundp mapsym) | ||
| 201 | ;; Make sure there is a stack | ||
| 202 | (if (not (boundp stacksym)) (set stacksym nil)) | ||
| 203 | ;; If there is a value to push, then push it. | ||
| 204 | (set stacksym (cons mapvalue (symbol-value stacksym))) | ||
| 205 | ) | ||
| 206 | ;; Set our new value here. | ||
| 207 | (set mapsym value) | ||
| 208 | )) | ||
| 209 | |||
| 210 | (defun semantic-lex-spp-symbol-pop (name) | ||
| 211 | "Pop macro NAME from the stackmap into the orig map. | ||
| 212 | Reverse with `semantic-lex-spp-symbol-pop'." | ||
| 213 | (let* ((map (semantic-lex-spp-dynamic-map)) | ||
| 214 | (stack (semantic-lex-spp-dynamic-map-stack)) | ||
| 215 | (mapsym (intern name map)) | ||
| 216 | (stacksym (intern name stack)) | ||
| 217 | (oldvalue nil) | ||
| 218 | ) | ||
| 219 | (if (or (not (boundp stacksym) ) | ||
| 220 | (= (length (symbol-value stacksym)) 0)) | ||
| 221 | ;; Nothing to pop, remove it. | ||
| 222 | (unintern name map) | ||
| 223 | ;; If there is a value to pop, then add it to the map. | ||
| 224 | (set mapsym (car (symbol-value stacksym))) | ||
| 225 | (set stacksym (cdr (symbol-value stacksym))) | ||
| 226 | ))) | ||
| 227 | |||
| 228 | (defsubst semantic-lex-spp-symbol-stream (name) | ||
| 229 | "Return replacement stream of macro with NAME." | ||
| 230 | (let ((spp (semantic-lex-spp-symbol name))) | ||
| 231 | (if spp | ||
| 232 | (symbol-value spp)))) | ||
| 233 | |||
| 234 | (defun semantic-lex-make-spp-table (specs) | ||
| 235 | "Convert spp macro list SPECS into an obarray and return it. | ||
| 236 | SPECS must be a list of (NAME . REPLACEMENT) elements, where: | ||
| 237 | |||
| 238 | NAME is the name of the spp macro symbol to define. | ||
| 239 | REPLACEMENT a string that would be substituted in for NAME." | ||
| 240 | |||
| 241 | ;; Create the symbol hash table | ||
| 242 | (let ((semantic-lex-spp-macro-symbol-obarray (make-vector 13 0)) | ||
| 243 | spec) | ||
| 244 | ;; fill it with stuff | ||
| 245 | (while specs | ||
| 246 | (setq spec (car specs) | ||
| 247 | specs (cdr specs)) | ||
| 248 | (semantic-lex-spp-symbol-set | ||
| 249 | (car spec) | ||
| 250 | (cdr spec) | ||
| 251 | semantic-lex-spp-macro-symbol-obarray)) | ||
| 252 | semantic-lex-spp-macro-symbol-obarray)) | ||
| 253 | |||
| 254 | (defun semantic-lex-spp-save-table () | ||
| 255 | "Return a list of spp macros and values. | ||
| 256 | The return list is meant to be saved in a semanticdb table." | ||
| 257 | (let (macros) | ||
| 258 | (when (arrayp semantic-lex-spp-dynamic-macro-symbol-obarray) | ||
| 259 | (mapatoms | ||
| 260 | #'(lambda (symbol) | ||
| 261 | (setq macros (cons (cons (symbol-name symbol) | ||
| 262 | (symbol-value symbol)) | ||
| 263 | macros))) | ||
| 264 | semantic-lex-spp-dynamic-macro-symbol-obarray)) | ||
| 265 | macros)) | ||
| 266 | |||
| 267 | (defun semantic-lex-spp-macros () | ||
| 268 | "Return a list of spp macros as Lisp symbols. | ||
| 269 | The value of each symbol is the replacement stream." | ||
| 270 | (let (macros) | ||
| 271 | (when (arrayp semantic-lex-spp-macro-symbol-obarray) | ||
| 272 | (mapatoms | ||
| 273 | #'(lambda (symbol) | ||
| 274 | (setq macros (cons symbol macros))) | ||
| 275 | semantic-lex-spp-macro-symbol-obarray)) | ||
| 276 | (when (arrayp semantic-lex-spp-project-macro-symbol-obarray) | ||
| 277 | (mapatoms | ||
| 278 | #'(lambda (symbol) | ||
| 279 | (setq macros (cons symbol macros))) | ||
| 280 | semantic-lex-spp-project-macro-symbol-obarray)) | ||
| 281 | (when (arrayp semantic-lex-spp-dynamic-macro-symbol-obarray) | ||
| 282 | (mapatoms | ||
| 283 | #'(lambda (symbol) | ||
| 284 | (setq macros (cons symbol macros))) | ||
| 285 | semantic-lex-spp-dynamic-macro-symbol-obarray)) | ||
| 286 | macros)) | ||
| 287 | |||
| 288 | (defun semantic-lex-spp-set-dynamic-table (new-entries) | ||
| 289 | "Set the dynamic symbol table to NEW-ENTRIES. | ||
| 290 | For use with semanticdb restoration of state." | ||
| 291 | (dolist (e new-entries) | ||
| 292 | ;; Default obarray for below is the dynamic map. | ||
| 293 | (semantic-lex-spp-symbol-set (car e) (cdr e)))) | ||
| 294 | |||
| 295 | (defun semantic-lex-spp-reset-hook (start end) | ||
| 296 | "Reset anything needed by SPP for parsing. | ||
| 297 | In this case, reset the dynamic macro symbol table if | ||
| 298 | START is (point-min). | ||
| 299 | END is not used." | ||
| 300 | (when (= start (point-min)) | ||
| 301 | (setq semantic-lex-spp-dynamic-macro-symbol-obarray nil | ||
| 302 | semantic-lex-spp-dynamic-macro-symbol-obarray-stack nil | ||
| 303 | ;; This shouldn't not be nil, but reset just in case. | ||
| 304 | semantic-lex-spp-expanded-macro-stack nil) | ||
| 305 | )) | ||
| 306 | |||
| 307 | ;;; MACRO EXPANSION: Simple cases | ||
| 308 | ;; | ||
| 309 | ;; If a user fills in the table with simple strings, we can | ||
| 310 | ;; support that by converting them into tokens with the | ||
| 311 | ;; various analyzers that are available. | ||
| 312 | |||
| 313 | (defun semantic-lex-spp-extract-regex-and-compare (analyzer value) | ||
| 314 | "Extract a regexp from an ANALYZER and use to match VALUE. | ||
| 315 | Return non-nil if it matches" | ||
| 316 | (let* ((condition (car analyzer)) | ||
| 317 | (regex (cond ((eq (car condition) 'looking-at) | ||
| 318 | (nth 1 condition)) | ||
| 319 | (t | ||
| 320 | nil)))) | ||
| 321 | (when regex | ||
| 322 | (string-match regex value)) | ||
| 323 | )) | ||
| 324 | |||
| 325 | (defun semantic-lex-spp-simple-macro-to-macro-stream (val beg end argvalues) | ||
| 326 | "Convert lexical macro contents VAL into a macro expansion stream. | ||
| 327 | These are for simple macro expansions that a user may have typed in directly. | ||
| 328 | As such, we need to analyze the input text, to figure out what kind of real | ||
| 329 | lexical token we should be inserting in its place. | ||
| 330 | |||
| 331 | Argument VAL is the value of some macro to be converted into a stream. | ||
| 332 | BEG and END are the token bounds of the macro to be expanded | ||
| 333 | that will somehow gain a much longer token stream. | ||
| 334 | ARGVALUES are values for any arg list, or nil." | ||
| 335 | (cond | ||
| 336 | ;; We perform a replacement. Technically, this should | ||
| 337 | ;; be a full lexical step over the "val" string, but take | ||
| 338 | ;; a guess that its just a keyword or existing symbol. | ||
| 339 | ;; | ||
| 340 | ;; Probably a really bad idea. See how it goes. | ||
| 341 | ((semantic-lex-spp-extract-regex-and-compare | ||
| 342 | semantic-lex-symbol-or-keyword val) | ||
| 343 | (semantic-lex-push-token | ||
| 344 | (semantic-lex-token (or (semantic-lex-keyword-p val) 'symbol) | ||
| 345 | beg end | ||
| 346 | val))) | ||
| 347 | |||
| 348 | ;; Ok, the rest of these are various types of syntax. | ||
| 349 | ;; Conveniences for users that type in their symbol table. | ||
| 350 | ((semantic-lex-spp-extract-regex-and-compare | ||
| 351 | semantic-lex-punctuation val) | ||
| 352 | (semantic-lex-token 'punctuation beg end val)) | ||
| 353 | ((semantic-lex-spp-extract-regex-and-compare | ||
| 354 | semantic-lex-number val) | ||
| 355 | (semantic-lex-token 'number beg end val)) | ||
| 356 | ((semantic-lex-spp-extract-regex-and-compare | ||
| 357 | semantic-lex-paren-or-list val) | ||
| 358 | (semantic-lex-token 'semantic-list beg end val)) | ||
| 359 | ((semantic-lex-spp-extract-regex-and-compare | ||
| 360 | semantic-lex-string val) | ||
| 361 | (semantic-lex-token 'string beg end val)) | ||
| 362 | (t nil) | ||
| 363 | )) | ||
| 364 | |||
| 365 | ;;; MACRO EXPANSION : Lexical token replacement | ||
| 366 | ;; | ||
| 367 | ;; When substituting in a macro from a token stream of formatted | ||
| 368 | ;; semantic lex tokens, things can be much more complicated. | ||
| 369 | ;; | ||
| 370 | ;; Some macros have arguments that get set into the dynamic macro | ||
| 371 | ;; table during replacement. | ||
| 372 | ;; | ||
| 373 | ;; In general, the macro tokens are substituted into the regular | ||
| 374 | ;; token stream, but placed under the characters of the original | ||
| 375 | ;; macro symbol. | ||
| 376 | ;; | ||
| 377 | ;; Argument lists are saved as a lexical token at the beginning | ||
| 378 | ;; of a replacement value. | ||
| 379 | |||
| 380 | (defun semantic-lex-spp-one-token-to-txt (tok) | ||
| 381 | "Convert the token TOK into a string. | ||
| 382 | If TOK is made of multiple tokens, convert those to text. This | ||
| 383 | conversion is needed if a macro has a merge symbol in it that | ||
| 384 | combines the text of two previously distinct symbols. For | ||
| 385 | exampe, in c: | ||
| 386 | |||
| 387 | #define (a,b) a ## b;" | ||
| 388 | (let ((txt (semantic-lex-token-text tok)) | ||
| 389 | (sym nil) | ||
| 390 | ) | ||
| 391 | (cond ((and (eq (car tok) 'symbol) | ||
| 392 | (setq sym (semantic-lex-spp-symbol txt)) | ||
| 393 | (not (semantic-lex-spp-macro-with-args (symbol-value sym))) | ||
| 394 | ) | ||
| 395 | ;; Now that we have a symbol, | ||
| 396 | (let ((val (symbol-value sym))) | ||
| 397 | (cond ((and (consp val) | ||
| 398 | (symbolp (car val))) | ||
| 399 | (semantic-lex-spp-one-token-to-txt val)) | ||
| 400 | ((and (consp val) | ||
| 401 | (consp (car val)) | ||
| 402 | (symbolp (car (car val)))) | ||
| 403 | (mapconcat (lambda (subtok) | ||
| 404 | (semantic-lex-spp-one-token-to-txt subtok)) | ||
| 405 | val | ||
| 406 | "")) | ||
| 407 | ;; If val is nil, that's probably wrong. | ||
| 408 | ;; Found a system header case where this was true. | ||
| 409 | ((null val) "") | ||
| 410 | ;; Debug wierd stuff. | ||
| 411 | (t (debug))) | ||
| 412 | )) | ||
| 413 | ((stringp txt) | ||
| 414 | txt) | ||
| 415 | (t nil)) | ||
| 416 | )) | ||
| 417 | |||
| 418 | (defun semantic-lex-spp-macro-with-args (val) | ||
| 419 | "If the macro value VAL has an argument list, return the arglist." | ||
| 420 | (when (and val (consp val) (consp (car val)) | ||
| 421 | (eq 'spp-arg-list (car (car val)))) | ||
| 422 | (car (cdr (car val))))) | ||
| 423 | |||
| 424 | (defun semantic-lex-spp-token-macro-to-macro-stream (val beg end argvalues) | ||
| 425 | "Convert lexical macro contents VAL into a macro expansion stream. | ||
| 426 | Argument VAL is the value of some macro to be converted into a stream. | ||
| 427 | BEG and END are the token bounds of the macro to be expanded | ||
| 428 | that will somehow gain a much longer token stream. | ||
| 429 | ARGVALUES are values for any arg list, or nil. | ||
| 430 | See comments in code for information about how token streams are processed | ||
| 431 | and what valid VAL values are." | ||
| 432 | |||
| 433 | ;; A typical VAL value might be either a stream of tokens. | ||
| 434 | ;; Tokens saved into a macro stream always includes the text from the | ||
| 435 | ;; buffer, since the locations specified probably don't represent | ||
| 436 | ;; that text anymore, or even the same buffer. | ||
| 437 | ;; | ||
| 438 | ;; CASE 1: Simple token stream | ||
| 439 | ;; | ||
| 440 | ;; #define SUPER mysuper:: | ||
| 441 | ;; ==> | ||
| 442 | ;;((symbol "mysuper" 480 . 487) | ||
| 443 | ;; (punctuation ":" 487 . 488) | ||
| 444 | ;; (punctuation ":" 488 . 489)) | ||
| 445 | ;; | ||
| 446 | ;; CASE 2: Token stream with argument list | ||
| 447 | ;; | ||
| 448 | ;; #define INT_FCN(name) int name (int in) | ||
| 449 | ;; ==> | ||
| 450 | ;; ((spp-arg-list ("name") 558 . 564) | ||
| 451 | ;; (INT "int" 565 . 568) | ||
| 452 | ;; (symbol "name" 569 . 573) | ||
| 453 | ;; (semantic-list "(int in)" 574 . 582)) | ||
| 454 | ;; | ||
| 455 | ;; In the second case, a macro with an argument list as the a rgs as the | ||
| 456 | ;; first entry. | ||
| 457 | ;; | ||
| 458 | ;; CASE 3: Symbol text merge | ||
| 459 | ;; | ||
| 460 | ;; #define TMP(a) foo_ ## a | ||
| 461 | ;; ==> | ||
| 462 | ;; ((spp-arg-list ("a") 20 . 23) | ||
| 463 | ;; (spp-symbol-merge ((symbol "foo_" 24 . 28) (symbol "a" 32 . 33)) | ||
| 464 | ;; 24 . 33)) | ||
| 465 | ;; | ||
| 466 | ;; Usually in conjunction with a macro with an argument, merging symbol | ||
| 467 | ;; parts is a way of fabricating new symbols from pieces inside the macro. | ||
| 468 | ;; These macros use `spp-symbol-merge' tokens whose TEXT part is another | ||
| 469 | ;; token stream. This sub-stream ought to consist of only 2 SYMBOL pieces, | ||
| 470 | ;; though I suppose keywords might be ok. The end result of this example | ||
| 471 | ;; merge symbol would be (symbol "foo_A" 24 . 33) where A is the symbol | ||
| 472 | ;; passed in from the arg list "a". | ||
| 473 | ;; | ||
| 474 | ;; CASE 4: Nested token streams | ||
| 475 | ;; | ||
| 476 | ;; #define FOO(f) f | ||
| 477 | ;; #define BLA bla FOO(foo) | ||
| 478 | ;; ==> | ||
| 479 | ;; ((INT "int" 82 . 85) | ||
| 480 | ;; (symbol "FOO" 86 . 89) | ||
| 481 | ;; (semantic-list "(foo)" 89 . 94)) | ||
| 482 | ;; | ||
| 483 | ;; Nested token FOO shows up in the table of macros, and gets replace | ||
| 484 | ;; inline. This is the same as case 2. | ||
| 485 | |||
| 486 | (let ((arglist (semantic-lex-spp-macro-with-args val)) | ||
| 487 | (argalist nil) | ||
| 488 | (val-tmp nil) | ||
| 489 | (v nil) | ||
| 490 | ) | ||
| 491 | ;; CASE 2: Dealing with the arg list. | ||
| 492 | (when arglist | ||
| 493 | ;; Skip the arg list. | ||
| 494 | (setq val (cdr val)) | ||
| 495 | |||
| 496 | ;; Push args into the replacement list. | ||
| 497 | (let ((AV argvalues)) | ||
| 498 | (dolist (A arglist) | ||
| 499 | (let* ((argval (car AV))) | ||
| 500 | |||
| 501 | (semantic-lex-spp-symbol-push A argval) | ||
| 502 | (setq argalist (cons (cons A argval) argalist)) | ||
| 503 | (setq AV (cdr AV))))) | ||
| 504 | ) | ||
| 505 | |||
| 506 | ;; Set val-tmp after stripping arguments. | ||
| 507 | (setq val-tmp val) | ||
| 508 | |||
| 509 | ;; CASE 1: Push everything else onto the list. | ||
| 510 | ;; Once the arg list is stripped off, CASE 2 is the same | ||
| 511 | ;; as CASE 1. | ||
| 512 | (while val-tmp | ||
| 513 | (setq v (car val-tmp)) | ||
| 514 | (setq val-tmp (cdr val-tmp)) | ||
| 515 | |||
| 516 | (let* (;; The text of the current lexical token. | ||
| 517 | (txt (car (cdr v))) | ||
| 518 | ;; Try to convert txt into a macro declaration. If it is | ||
| 519 | ;; not a macro, use nil. | ||
| 520 | (txt-macro-or-nil (semantic-lex-spp-symbol txt)) | ||
| 521 | ;; If our current token is a macro, then pull off the argument | ||
| 522 | ;; list. | ||
| 523 | (macro-and-args | ||
| 524 | (when txt-macro-or-nil | ||
| 525 | (semantic-lex-spp-macro-with-args (symbol-value txt-macro-or-nil))) | ||
| 526 | ) | ||
| 527 | ;; We need to peek at the next token when testing for | ||
| 528 | ;; used macros with arg lists. | ||
| 529 | (next-tok-class (semantic-lex-token-class (car val-tmp))) | ||
| 530 | ) | ||
| 531 | |||
| 532 | (cond | ||
| 533 | ;; CASE 3: Merge symbols together. | ||
| 534 | ((eq (semantic-lex-token-class v) 'spp-symbol-merge) | ||
| 535 | ;; We need to merge the tokens in the 'text segement together, | ||
| 536 | ;; and produce a single symbol from it. | ||
| 537 | (let ((newsym | ||
| 538 | (mapconcat (lambda (tok) | ||
| 539 | (semantic-lex-spp-one-token-to-txt tok)) | ||
| 540 | txt | ||
| 541 | ""))) | ||
| 542 | (semantic-lex-push-token | ||
| 543 | (semantic-lex-token 'symbol beg end newsym)) | ||
| 544 | )) | ||
| 545 | |||
| 546 | ;; CASE 2: Argument replacement. If a discovered symbol is in | ||
| 547 | ;; the active list of arguments, then we need to substitute | ||
| 548 | ;; in the new value. | ||
| 549 | ((and (eq (semantic-lex-token-class v) 'symbol) txt-macro-or-nil | ||
| 550 | (or (and macro-and-args (eq next-tok-class 'semantic-list)) | ||
| 551 | (not macro-and-args)) | ||
| 552 | ) | ||
| 553 | (let ((AV nil)) | ||
| 554 | (when macro-and-args | ||
| 555 | (setq AV | ||
| 556 | (semantic-lex-spp-stream-for-arglist (car val-tmp))) | ||
| 557 | ;; We used up these args. Pull from the stream. | ||
| 558 | (setq val-tmp (cdr val-tmp)) | ||
| 559 | ) | ||
| 560 | |||
| 561 | (semantic-lex-with-macro-used txt | ||
| 562 | ;; Don't recurse directly into this same fcn, because it is | ||
| 563 | ;; convenient to have plain string replacements too. | ||
| 564 | (semantic-lex-spp-macro-to-macro-stream | ||
| 565 | (symbol-value txt-macro-or-nil) | ||
| 566 | beg end AV)) | ||
| 567 | )) | ||
| 568 | |||
| 569 | ;; This is a HACK for the C parser. The 'macros text | ||
| 570 | ;; property is some storage so that the parser can do | ||
| 571 | ;; some C specific text manipulations. | ||
| 572 | ((eq (semantic-lex-token-class v) 'semantic-list) | ||
| 573 | ;; Push our arg list onto the semantic list. | ||
| 574 | (when argalist | ||
| 575 | (setq txt (concat txt)) ; Copy the text. | ||
| 576 | (put-text-property 0 1 'macros argalist txt)) | ||
| 577 | (semantic-lex-push-token | ||
| 578 | (semantic-lex-token (semantic-lex-token-class v) beg end txt)) | ||
| 579 | ) | ||
| 580 | |||
| 581 | ;; CASE 1: Just another token in the stream. | ||
| 582 | (t | ||
| 583 | ;; Nothing new. | ||
| 584 | (semantic-lex-push-token | ||
| 585 | (semantic-lex-token (semantic-lex-token-class v) beg end txt)) | ||
| 586 | ) | ||
| 587 | ))) | ||
| 588 | |||
| 589 | ;; CASE 2: The arg list we pushed onto the symbol table | ||
| 590 | ;; must now be removed. | ||
| 591 | (dolist (A arglist) | ||
| 592 | (semantic-lex-spp-symbol-pop A)) | ||
| 593 | )) | ||
| 594 | |||
| 595 | ;;; Macro Merging | ||
| 596 | ;; | ||
| 597 | ;; Used when token streams from different macros include eachother. | ||
| 598 | ;; Merged macro streams perform in place replacements. | ||
| 599 | |||
| 600 | (defun semantic-lex-spp-merge-streams (raw-stream) | ||
| 601 | "Merge elements from the RAW-STREAM together. | ||
| 602 | Handle spp-concat symbol concatenation. | ||
| 603 | Handle Nested macro replacements. | ||
| 604 | Return the cooked stream." | ||
| 605 | (let ((cooked-stream nil)) | ||
| 606 | ;; Merge the stream | ||
| 607 | (while raw-stream | ||
| 608 | (cond ((eq (semantic-lex-token-class (car raw-stream)) 'spp-concat) | ||
| 609 | ;; handle hashhash, by skipping it. | ||
| 610 | (setq raw-stream (cdr raw-stream)) | ||
| 611 | ;; Now merge the symbols. | ||
| 612 | (let ((prev-tok (car cooked-stream)) | ||
| 613 | (next-tok (car raw-stream))) | ||
| 614 | (setq cooked-stream (cdr cooked-stream)) | ||
| 615 | (push (semantic-lex-token | ||
| 616 | 'spp-symbol-merge | ||
| 617 | (semantic-lex-token-start prev-tok) | ||
| 618 | (semantic-lex-token-end next-tok) | ||
| 619 | (list prev-tok next-tok)) | ||
| 620 | cooked-stream) | ||
| 621 | )) | ||
| 622 | (t | ||
| 623 | (push (car raw-stream) cooked-stream)) | ||
| 624 | ) | ||
| 625 | (setq raw-stream (cdr raw-stream)) | ||
| 626 | ) | ||
| 627 | |||
| 628 | (nreverse cooked-stream)) | ||
| 629 | ) | ||
| 630 | |||
| 631 | ;;; MACRO EXPANSION | ||
| 632 | ;; | ||
| 633 | ;; There are two types of expansion. | ||
| 634 | ;; | ||
| 635 | ;; 1. Expansion using a value made up of lexical tokens. | ||
| 636 | ;; 2. User input replacement from a plain string. | ||
| 637 | |||
| 638 | (defun semantic-lex-spp-macro-to-macro-stream (val beg end argvalues) | ||
| 639 | "Convert lexical macro contents VAL into a macro expansion stream. | ||
| 640 | Argument VAL is the value of some macro to be converted into a stream. | ||
| 641 | BEG and END are the token bounds of the macro to be expanded | ||
| 642 | that will somehow gain a much longer token stream. | ||
| 643 | ARGVALUES are values for any arg list, or nil." | ||
| 644 | (cond | ||
| 645 | ;; If val is nil, then just skip it. | ||
| 646 | ((null val) t) | ||
| 647 | ;; If it is a token, then return that token rebuilt. | ||
| 648 | ((and (consp val) (car val) (symbolp (car val))) | ||
| 649 | (semantic-lex-push-token | ||
| 650 | (semantic-lex-token (car val) beg end (semantic-lex-token-text val)))) | ||
| 651 | ;; Test for a token list. | ||
| 652 | ((and (consp val) (consp (car val)) (car (car val)) | ||
| 653 | (symbolp (car (car val)))) | ||
| 654 | (semantic-lex-spp-token-macro-to-macro-stream val beg end argvalues)) | ||
| 655 | ;; Test for miscellaneous strings. | ||
| 656 | ((stringp val) | ||
| 657 | (semantic-lex-spp-simple-macro-to-macro-stream val beg end argvalues)) | ||
| 658 | )) | ||
| 659 | |||
| 660 | ;;; -------------------------------------------------------- | ||
| 661 | ;;; | ||
| 662 | ;;; ANALYZERS: | ||
| 663 | ;;; | ||
| 664 | |||
| 665 | ;;; Symbol Is Macro | ||
| 666 | ;; | ||
| 667 | ;; An analyser that will push tokens from a macro in place | ||
| 668 | ;; of the macro symbol. | ||
| 669 | ;; | ||
| 670 | (defun semantic-lex-spp-anlyzer-do-replace (sym val beg end) | ||
| 671 | "Do the lexical replacement for SYM with VAL. | ||
| 672 | Argument BEG and END specify the bounds of SYM in the buffer." | ||
| 673 | (if (not val) | ||
| 674 | (setq semantic-lex-end-point end) | ||
| 675 | (let ((arg-in nil) | ||
| 676 | (arg-parsed nil) | ||
| 677 | (arg-split nil) | ||
| 678 | ) | ||
| 679 | |||
| 680 | ;; Check for arguments. | ||
| 681 | (setq arg-in (semantic-lex-spp-macro-with-args val)) | ||
| 682 | |||
| 683 | (when arg-in | ||
| 684 | (save-excursion | ||
| 685 | (goto-char end) | ||
| 686 | (setq arg-parsed | ||
| 687 | (semantic-lex-spp-one-token-and-move-for-macro | ||
| 688 | (point-at-eol))) | ||
| 689 | (setq end (semantic-lex-token-end arg-parsed)) | ||
| 690 | |||
| 691 | (when (and (listp arg-parsed) (eq (car arg-parsed) 'semantic-list)) | ||
| 692 | (setq arg-split | ||
| 693 | ;; Use lex to split up the contents of the argument list. | ||
| 694 | (semantic-lex-spp-stream-for-arglist arg-parsed) | ||
| 695 | )) | ||
| 696 | )) | ||
| 697 | |||
| 698 | ;; if we have something to sub in, then do it. | ||
| 699 | (semantic-lex-spp-macro-to-macro-stream val beg end arg-split) | ||
| 700 | (setq semantic-lex-end-point end) | ||
| 701 | ) | ||
| 702 | )) | ||
| 703 | |||
| 704 | (defvar semantic-lex-spp-replacements-enabled t | ||
| 705 | "Non-nil means do replacements when finding keywords. | ||
| 706 | Disable this only to prevent recursive expansion issues.") | ||
| 707 | |||
| 708 | (defun semantic-lex-spp-analyzer-push-tokens-for-symbol (str beg end) | ||
| 709 | "Push lexical tokens for the symbol or keyword STR. | ||
| 710 | STR occurs in the current buffer between BEG and END." | ||
| 711 | (let (sym val) | ||
| 712 | (cond | ||
| 713 | ;; | ||
| 714 | ;; It is a macro. Prepare for a replacement. | ||
| 715 | ((and semantic-lex-spp-replacements-enabled | ||
| 716 | (semantic-lex-spp-symbol-p str)) | ||
| 717 | (setq sym (semantic-lex-spp-symbol str) | ||
| 718 | val (symbol-value sym) | ||
| 719 | count 0) | ||
| 720 | |||
| 721 | (let ((semantic-lex-spp-expanded-macro-stack | ||
| 722 | semantic-lex-spp-expanded-macro-stack)) | ||
| 723 | |||
| 724 | (semantic-lex-with-macro-used str | ||
| 725 | ;; Do direct replacements of single value macros of macros. | ||
| 726 | ;; This solves issues with a macro containing one symbol that | ||
| 727 | ;; is another macro, and get arg lists passed around. | ||
| 728 | (while (and val (consp val) | ||
| 729 | (semantic-lex-token-p (car val)) | ||
| 730 | (eq (length val) 1) | ||
| 731 | (eq (semantic-lex-token-class (car val)) 'symbol) | ||
| 732 | (semantic-lex-spp-symbol-p (semantic-lex-token-text (car val))) | ||
| 733 | (< count 10) | ||
| 734 | ) | ||
| 735 | (setq str (semantic-lex-token-text (car val))) | ||
| 736 | (setq sym (semantic-lex-spp-symbol str) | ||
| 737 | val (symbol-value sym)) | ||
| 738 | ;; Prevent recursion | ||
| 739 | (setq count (1+ count)) | ||
| 740 | ;; This prevents a different kind of recursion. | ||
| 741 | (push str semantic-lex-spp-expanded-macro-stack) | ||
| 742 | ) | ||
| 743 | |||
| 744 | (semantic-lex-spp-anlyzer-do-replace sym val beg end)) | ||
| 745 | |||
| 746 | )) | ||
| 747 | ;; Anything else. | ||
| 748 | (t | ||
| 749 | ;; A regular keyword. | ||
| 750 | (semantic-lex-push-token | ||
| 751 | (semantic-lex-token (or (semantic-lex-keyword-p str) 'symbol) | ||
| 752 | beg end)))) | ||
| 753 | )) | ||
| 754 | |||
| 755 | (define-lex-regex-analyzer semantic-lex-spp-replace-or-symbol-or-keyword | ||
| 756 | "Like 'semantic-lex-symbol-or-keyword' plus preprocessor macro replacement." | ||
| 757 | "\\(\\sw\\|\\s_\\)+" | ||
| 758 | (let ((str (match-string 0)) | ||
| 759 | (beg (match-beginning 0)) | ||
| 760 | (end (match-end 0))) | ||
| 761 | (semantic-lex-spp-analyzer-push-tokens-for-symbol str beg end))) | ||
| 762 | |||
| 763 | ;;; ANALYZERS FOR NEW MACROS | ||
| 764 | ;; | ||
| 765 | ;; These utilities and analyzer declaration function are for | ||
| 766 | ;; creating an analyzer which produces new macros in the macro table. | ||
| 767 | ;; | ||
| 768 | ;; There are two analyzers. One for new macros, and one for removing | ||
| 769 | ;; a macro. | ||
| 770 | |||
| 771 | (defun semantic-lex-spp-first-token-arg-list (token) | ||
| 772 | "If TOKEN is a semantic-list, turn it into a an SPP ARG LIST." | ||
| 773 | (when (and (consp token) | ||
| 774 | (symbolp (car token)) | ||
| 775 | (eq 'semantic-list (car token))) | ||
| 776 | ;; Convert TOKEN in place. | ||
| 777 | (let ((argsplit (cedet-split-string (semantic-lex-token-text token) | ||
| 778 | "[(), ]" t))) | ||
| 779 | (setcar token 'spp-arg-list) | ||
| 780 | (setcar (nthcdr 1 token) argsplit)) | ||
| 781 | )) | ||
| 782 | |||
| 783 | (defun semantic-lex-spp-one-token-and-move-for-macro (max) | ||
| 784 | "Lex up one token, and move to end of that token. | ||
| 785 | Don't go past MAX." | ||
| 786 | (let ((ans (semantic-lex (point) max 0 0))) | ||
| 787 | (if (not ans) | ||
| 788 | (progn (goto-char max) | ||
| 789 | nil) | ||
| 790 | (when (> (semantic-lex-token-end (car ans)) max) | ||
| 791 | (let ((bounds (semantic-lex-token-bounds (car ans)))) | ||
| 792 | (setcdr bounds max))) | ||
| 793 | (goto-char (semantic-lex-token-end (car ans))) | ||
| 794 | (car ans)) | ||
| 795 | )) | ||
| 796 | |||
| 797 | (defun semantic-lex-spp-stream-for-arglist (token) | ||
| 798 | "Lex up the contents of the arglist TOKEN. | ||
| 799 | Parsing starts inside the parens, and ends at the end of TOKEN." | ||
| 800 | (let ((end (semantic-lex-token-end token)) | ||
| 801 | (fresh-toks nil) | ||
| 802 | (toks nil)) | ||
| 803 | (save-excursion | ||
| 804 | |||
| 805 | (if (stringp (nth 1 token)) | ||
| 806 | ;; If the 2nd part of the token is a string, then we have | ||
| 807 | ;; a token specifically extracted from a buffer. Possibly | ||
| 808 | ;; a different buffer. This means we need to do something | ||
| 809 | ;; nice to parse its contents. | ||
| 810 | (let ((txt (semantic-lex-token-text token))) | ||
| 811 | (semantic-lex-spp-lex-text-string | ||
| 812 | (substring txt 1 (1- (length txt))))) | ||
| 813 | |||
| 814 | ;; This part is like the original | ||
| 815 | (goto-char (semantic-lex-token-start token)) | ||
| 816 | ;; A cheat for going into the semantic list. | ||
| 817 | (forward-char 1) | ||
| 818 | (setq fresh-toks (semantic-lex-spp-stream-for-macro (1- end))) | ||
| 819 | (dolist (tok fresh-toks) | ||
| 820 | (when (memq (semantic-lex-token-class tok) '(symbol semantic-list)) | ||
| 821 | (setq toks (cons tok toks)))) | ||
| 822 | |||
| 823 | (nreverse toks))))) | ||
| 824 | |||
| 825 | (defun semantic-lex-spp-lex-text-string (text) | ||
| 826 | "Lex the text string TEXT using the current buffer's state. | ||
| 827 | Use this to parse text extracted from a macro as if it came from | ||
| 828 | the current buffer. Since the lexer is designed to only work in | ||
| 829 | a buffer, we need to create a new buffer, and populate it with rules | ||
| 830 | and variable state from the current buffer." | ||
| 831 | (let* ((buf (get-buffer-create " *SPP parse hack*")) | ||
| 832 | (mode major-mode) | ||
| 833 | (fresh-toks nil) | ||
| 834 | (toks nil) | ||
| 835 | (origbuff (current-buffer)) | ||
| 836 | (important-vars '(semantic-lex-spp-macro-symbol-obarray | ||
| 837 | semantic-lex-spp-project-macro-symbol-obarray | ||
| 838 | semantic-lex-spp-dynamic-macro-symbol-obarray | ||
| 839 | semantic-lex-spp-dynamic-macro-symbol-obarray-stack | ||
| 840 | semantic-lex-spp-expanded-macro-stack | ||
| 841 | )) | ||
| 842 | ) | ||
| 843 | (set-buffer buf) | ||
| 844 | (erase-buffer) | ||
| 845 | ;; Below is a painful hack to make sure everything is setup correctly. | ||
| 846 | (when (not (eq major-mode mode)) | ||
| 847 | (funcall mode) | ||
| 848 | ;; Hack in mode-local | ||
| 849 | (activate-mode-local-bindings) | ||
| 850 | ;; CHEATER! The following 3 lines are from | ||
| 851 | ;; `semantic-new-buffer-fcn', but we don't want to turn | ||
| 852 | ;; on all the other annoying modes for this little task. | ||
| 853 | (setq semantic-new-buffer-fcn-was-run t) | ||
| 854 | (semantic-lex-init) | ||
| 855 | (semantic-clear-toplevel-cache) | ||
| 856 | (remove-hook 'semantic-lex-reset-hooks 'semantic-lex-spp-reset-hook | ||
| 857 | t) | ||
| 858 | ;; Second Cheat: copy key variables reguarding macro state from the | ||
| 859 | ;; the originating buffer we are parsing. | ||
| 860 | (dolist (V important-vars) | ||
| 861 | (set V (semantic-buffer-local-value V origbuff))) | ||
| 862 | ) | ||
| 863 | (insert text) | ||
| 864 | (goto-char (point-min)) | ||
| 865 | |||
| 866 | (setq fresh-toks (semantic-lex-spp-stream-for-macro (point-max))) | ||
| 867 | (dolist (tok fresh-toks) | ||
| 868 | (when (memq (semantic-lex-token-class tok) '(symbol semantic-list)) | ||
| 869 | (setq toks (cons tok toks)))) | ||
| 870 | |||
| 871 | (nreverse toks))) | ||
| 872 | |||
| 873 | ;;;; FIRST DRAFT | ||
| 874 | ;; This is the fist version of semantic-lex-spp-stream-for-arglist | ||
| 875 | ;; that worked pretty well. It doesn't work if the TOKEN was derived | ||
| 876 | ;; from some other buffer, in which case it can get the wrong answer | ||
| 877 | ;; or throw an error if the token location in the originating buffer is | ||
| 878 | ;; larger than the current buffer. | ||
| 879 | ;;(defun semantic-lex-spp-stream-for-arglist-orig (token) | ||
| 880 | ;; "Lex up the contents of the arglist TOKEN. | ||
| 881 | ;; Parsing starts inside the parens, and ends at the end of TOKEN." | ||
| 882 | ;; (save-excursion | ||
| 883 | ;; (let ((end (semantic-lex-token-end token)) | ||
| 884 | ;; (fresh-toks nil) | ||
| 885 | ;; (toks nil)) | ||
| 886 | ;; (goto-char (semantic-lex-token-start token)) | ||
| 887 | ;; ;; A cheat for going into the semantic list. | ||
| 888 | ;; (forward-char 1) | ||
| 889 | ;; (setq fresh-toks (semantic-lex-spp-stream-for-macro (1- end))) | ||
| 890 | ;; (dolist (tok fresh-toks) | ||
| 891 | ;; (when (memq (semantic-lex-token-class tok) '(symbol semantic-list)) | ||
| 892 | ;; (setq toks (cons tok toks)))) | ||
| 893 | ;; (nreverse toks)) | ||
| 894 | ;; )) | ||
| 895 | |||
| 896 | ;;;; USING SPLIT | ||
| 897 | ;; This doesn't work, because some arguments passed into a macro | ||
| 898 | ;; might contain non-simple symbol words, which this doesn't handle. | ||
| 899 | ;; | ||
| 900 | ;; Thus, you need a full lex to occur. | ||
| 901 | ;; (defun semantic-lex-spp-stream-for-arglist-split (token) | ||
| 902 | ;; "Lex up the contents of the arglist TOKEN. | ||
| 903 | ;; Parsing starts inside the parens, and ends at the end of TOKEN." | ||
| 904 | ;; (let* ((txt (semantic-lex-token-text token)) | ||
| 905 | ;; (split (split-string (substring txt 1 (1- (length txt))) | ||
| 906 | ;; "(), " t)) | ||
| 907 | ;; ;; Hack for lexing. | ||
| 908 | ;; (semantic-lex-spp-analyzer-push-tokens-for-symbol nil)) | ||
| 909 | ;; (dolist (S split) | ||
| 910 | ;; (semantic-lex-spp-analyzer-push-tokens-for-symbol S 0 1)) | ||
| 911 | ;; (reverse semantic-lex-spp-analyzer-push-tokens-for-symbol))) | ||
| 912 | |||
| 913 | |||
| 914 | (defun semantic-lex-spp-stream-for-macro (eos) | ||
| 915 | "Lex up a stream of tokens for a #define statement. | ||
| 916 | Parsing starts at the current point location. | ||
| 917 | EOS is the end of the stream to lex for this macro." | ||
| 918 | (let ((stream nil)) | ||
| 919 | (while (< (point) eos) | ||
| 920 | (let* ((tok (semantic-lex-spp-one-token-and-move-for-macro eos)) | ||
| 921 | (str (when tok | ||
| 922 | (semantic-lex-token-text tok))) | ||
| 923 | ) | ||
| 924 | (if str | ||
| 925 | (push (semantic-lex-token (semantic-lex-token-class tok) | ||
| 926 | (semantic-lex-token-start tok) | ||
| 927 | (semantic-lex-token-end tok) | ||
| 928 | str) | ||
| 929 | stream) | ||
| 930 | ;; Nothing to push. | ||
| 931 | nil))) | ||
| 932 | (goto-char eos) | ||
| 933 | ;; Fix the order | ||
| 934 | (nreverse stream) | ||
| 935 | )) | ||
| 936 | |||
| 937 | (defmacro define-lex-spp-macro-declaration-analyzer (name doc regexp tokidx | ||
| 938 | &rest valform) | ||
| 939 | "Define a lexical analyzer for defining new MACROS. | ||
| 940 | NAME is the name of the analyzer. | ||
| 941 | DOC is the documentation for the analyzer. | ||
| 942 | REGEXP is a regular expression for the analyzer to match. | ||
| 943 | See `define-lex-regex-analyzer' for more on regexp. | ||
| 944 | TOKIDX is an index into REGEXP for which a new lexical token | ||
| 945 | of type `spp-macro-def' is to be created. | ||
| 946 | VALFORM are forms that return the value to be saved for this macro, or nil. | ||
| 947 | When implementing a macro, you can use `semantic-lex-spp-stream-for-macro' | ||
| 948 | to convert text into a lexical stream for storage in the macro." | ||
| 949 | (let ((start (make-symbol "start")) | ||
| 950 | (end (make-symbol "end")) | ||
| 951 | (val (make-symbol "val")) | ||
| 952 | (startpnt (make-symbol "startpnt")) | ||
| 953 | (endpnt (make-symbol "endpnt"))) | ||
| 954 | `(define-lex-regex-analyzer ,name | ||
| 955 | ,doc | ||
| 956 | ,regexp | ||
| 957 | (let ((,start (match-beginning ,tokidx)) | ||
| 958 | (,end (match-end ,tokidx)) | ||
| 959 | (,startpnt semantic-lex-end-point) | ||
| 960 | (,val (save-match-data ,@valform)) | ||
| 961 | (,endpnt semantic-lex-end-point)) | ||
| 962 | (semantic-lex-spp-symbol-set | ||
| 963 | (buffer-substring-no-properties ,start ,end) | ||
| 964 | ,val) | ||
| 965 | (semantic-lex-push-token | ||
| 966 | (semantic-lex-token 'spp-macro-def | ||
| 967 | ,start ,end)) | ||
| 968 | ;; Preserve setting of the end point from the calling macro. | ||
| 969 | (when (and (/= ,startpnt ,endpnt) | ||
| 970 | (/= ,endpnt semantic-lex-end-point)) | ||
| 971 | (setq semantic-lex-end-point ,endpnt)) | ||
| 972 | )))) | ||
| 973 | |||
| 974 | (defmacro define-lex-spp-macro-undeclaration-analyzer (name doc regexp tokidx) | ||
| 975 | "Undefine a lexical analyzer for defining new MACROS. | ||
| 976 | NAME is the name of the analyzer. | ||
| 977 | DOC is the documentation for the analyzer. | ||
| 978 | REGEXP is a regular expression for the analyzer to match. | ||
| 979 | See `define-lex-regex-analyzer' for more on regexp. | ||
| 980 | TOKIDX is an index into REGEXP for which a new lexical token | ||
| 981 | of type `spp-macro-undef' is to be created." | ||
| 982 | (let ((start (make-symbol "start")) | ||
| 983 | (end (make-symbol "end"))) | ||
| 984 | `(define-lex-regex-analyzer ,name | ||
| 985 | ,doc | ||
| 986 | ,regexp | ||
| 987 | (let ((,start (match-beginning ,tokidx)) | ||
| 988 | (,end (match-end ,tokidx)) | ||
| 989 | ) | ||
| 990 | (semantic-lex-spp-symbol-remove | ||
| 991 | (buffer-substring-no-properties ,start ,end)) | ||
| 992 | (semantic-lex-push-token | ||
| 993 | (semantic-lex-token 'spp-macro-undef | ||
| 994 | ,start ,end)) | ||
| 995 | )))) | ||
| 996 | |||
| 997 | ;;; INCLUDES | ||
| 998 | ;; | ||
| 999 | ;; These analyzers help a language define how include files | ||
| 1000 | ;; are identified. These are ONLY for languages that perform | ||
| 1001 | ;; an actual textual includesion, and not for imports. | ||
| 1002 | ;; | ||
| 1003 | ;; This section is supposed to allow the macros from the headers to be | ||
| 1004 | ;; added to the local dynamic macro table, but that hasn't been | ||
| 1005 | ;; written yet. | ||
| 1006 | ;; | ||
| 1007 | (defcustom semantic-lex-spp-use-headers-flag nil | ||
| 1008 | "*Non-nil means to pre-parse headers as we go. | ||
| 1009 | For languages that use the Semantic pre-processor, this can | ||
| 1010 | improve the accuracy of parsed files where include files | ||
| 1011 | can change the state of what's parsed in the current file. | ||
| 1012 | |||
| 1013 | Note: Note implemented yet" | ||
| 1014 | :group 'semantic | ||
| 1015 | :type 'boolean) | ||
| 1016 | |||
| 1017 | (defun semantic-lex-spp-merge-header (name) | ||
| 1018 | "Extract and merge any macros from the header with NAME. | ||
| 1019 | Finds the header file belonging to NAME, gets the macros | ||
| 1020 | from that file, and then merge the macros with our current | ||
| 1021 | symbol table." | ||
| 1022 | (when semantic-lex-spp-use-headers-flag | ||
| 1023 | ;; @todo - do this someday, ok? | ||
| 1024 | )) | ||
| 1025 | |||
| 1026 | (defmacro define-lex-spp-include-analyzer (name doc regexp tokidx | ||
| 1027 | &rest valform) | ||
| 1028 | "Define a lexical analyzer for defining a new INCLUDE lexical token. | ||
| 1029 | Macros defined in the found include will be added to our running table | ||
| 1030 | at the time the include statement is found. | ||
| 1031 | NAME is the name of the analyzer. | ||
| 1032 | DOC is the documentation for the analyzer. | ||
| 1033 | REGEXP is a regular expression for the analyzer to match. | ||
| 1034 | See `define-lex-regex-analyzer' for more on regexp. | ||
| 1035 | TOKIDX is an index into REGEXP for which a new lexical token | ||
| 1036 | of type `spp-macro-include' is to be created. | ||
| 1037 | VALFORM are forms that return the name of the thing being included, and the | ||
| 1038 | type of include. The return value should be of the form: | ||
| 1039 | (NAME . TYPE) | ||
| 1040 | where NAME is the name of the include, and TYPE is the type of the include, | ||
| 1041 | where a valid symbol is 'system, or nil." | ||
| 1042 | (let ((start (make-symbol "start")) | ||
| 1043 | (end (make-symbol "end")) | ||
| 1044 | (val (make-symbol "val")) | ||
| 1045 | (startpnt (make-symbol "startpnt")) | ||
| 1046 | (endpnt (make-symbol "endpnt"))) | ||
| 1047 | `(define-lex-regex-analyzer ,name | ||
| 1048 | ,doc | ||
| 1049 | ,regexp | ||
| 1050 | (let ((,start (match-beginning ,tokidx)) | ||
| 1051 | (,end (match-end ,tokidx)) | ||
| 1052 | (,startpnt semantic-lex-end-point) | ||
| 1053 | (,val (save-match-data ,@valform)) | ||
| 1054 | (,endpnt semantic-lex-end-point)) | ||
| 1055 | ;;(message "(car ,val) -> %S" (car ,val)) | ||
| 1056 | (semantic-lex-spp-merge-header (car ,val)) | ||
| 1057 | (semantic-lex-push-token | ||
| 1058 | (semantic-lex-token (if (eq (cdr ,val) 'system) | ||
| 1059 | 'spp-system-include | ||
| 1060 | 'spp-include) | ||
| 1061 | ,start ,end | ||
| 1062 | (car ,val))) | ||
| 1063 | ;; Preserve setting of the end point from the calling macro. | ||
| 1064 | (when (and (/= ,startpnt ,endpnt) | ||
| 1065 | (/= ,endpnt semantic-lex-end-point)) | ||
| 1066 | (setq semantic-lex-end-point ,endpnt)) | ||
| 1067 | )))) | ||
| 1068 | |||
| 1069 | ;;; EIEIO USAGE | ||
| 1070 | ;; | ||
| 1071 | ;; Semanticdb can save off macro tables for quick lookup later. | ||
| 1072 | ;; | ||
| 1073 | ;; These routines are for saving macro lists into an EIEIO persistent | ||
| 1074 | ;; file. | ||
| 1075 | (defvar semantic-lex-spp-macro-max-length-to-save 200 | ||
| 1076 | "*Maximum length of an SPP macro before we opt to not save it.") | ||
| 1077 | |||
| 1078 | (defun semantic-lex-spp-table-write-slot-value (value) | ||
| 1079 | "Write out the VALUE of a slot for EIEIO. | ||
| 1080 | The VALUE is a spp lexical table." | ||
| 1081 | (if (not value) | ||
| 1082 | (princ "nil") | ||
| 1083 | (princ "\n '(") | ||
| 1084 | ;(princ value) | ||
| 1085 | (dolist (sym value) | ||
| 1086 | (princ "(") | ||
| 1087 | (prin1 (car sym)) | ||
| 1088 | (let* ((first (car (cdr sym))) | ||
| 1089 | (rest (cdr sym))) | ||
| 1090 | (when (not (listp first)) | ||
| 1091 | (error "Error in macro \"%s\"" (car sym))) | ||
| 1092 | (when (eq (car first) 'spp-arg-list) | ||
| 1093 | (princ " ") | ||
| 1094 | (prin1 first) | ||
| 1095 | (setq rest (cdr rest)) | ||
| 1096 | ) | ||
| 1097 | |||
| 1098 | (when rest | ||
| 1099 | (princ " . ") | ||
| 1100 | (let ((len (length (cdr rest)))) | ||
| 1101 | (cond ((< len 2) | ||
| 1102 | (condition-case nil | ||
| 1103 | (prin1 rest) | ||
| 1104 | (error | ||
| 1105 | (princ "nil ;; Error writing macro\n")))) | ||
| 1106 | ((< len semantic-lex-spp-macro-max-length-to-save) | ||
| 1107 | (princ "\n ") | ||
| 1108 | (condition-case nil | ||
| 1109 | (prin1 rest) | ||
| 1110 | (error | ||
| 1111 | (princ "nil ;; Error writing macro\n "))) | ||
| 1112 | ) | ||
| 1113 | (t ;; Too Long! | ||
| 1114 | (princ "nil ;; Too Long!\n ") | ||
| 1115 | )))) | ||
| 1116 | ) | ||
| 1117 | (princ ")\n ") | ||
| 1118 | ) | ||
| 1119 | (princ ")\n")) | ||
| 1120 | ) | ||
| 1121 | |||
| 1122 | ;;; TESTS | ||
| 1123 | ;; | ||
| 1124 | (defun semantic-lex-spp-write-test () | ||
| 1125 | "Test the semantic tag writer against the current buffer." | ||
| 1126 | (interactive) | ||
| 1127 | (with-output-to-temp-buffer "*SPP Write Test*" | ||
| 1128 | (semantic-lex-spp-table-write-slot-value | ||
| 1129 | (semantic-lex-spp-save-table)))) | ||
| 1130 | |||
| 1131 | (defun semantic-lex-spp-write-utest () | ||
| 1132 | "Unit test using the test spp file to test the slot write fcn." | ||
| 1133 | (interactive) | ||
| 1134 | (let* ((sem (locate-library "semantic-lex-spp.el")) | ||
| 1135 | (dir (file-name-directory sem))) | ||
| 1136 | (save-excursion | ||
| 1137 | (set-buffer (find-file-noselect | ||
| 1138 | (expand-file-name "tests/testsppreplace.c" | ||
| 1139 | dir))) | ||
| 1140 | (semantic-lex-spp-write-test)))) | ||
| 1141 | |||
| 1142 | ;;; MACRO TABLE DEBUG | ||
| 1143 | ;; | ||
| 1144 | (defun semantic-lex-spp-describe (&optional buffer) | ||
| 1145 | "Describe the current list of spp macros for BUFFER. | ||
| 1146 | If BUFFER is not provided, use the current buffer." | ||
| 1147 | (interactive) | ||
| 1148 | (let ((syms (save-excursion | ||
| 1149 | (if buffer (set-buffer buffer)) | ||
| 1150 | (semantic-lex-spp-macros))) | ||
| 1151 | (sym nil)) | ||
| 1152 | (with-output-to-temp-buffer "*SPP MACROS*" | ||
| 1153 | (princ "Macro\t\tValue\n") | ||
| 1154 | (while syms | ||
| 1155 | (setq sym (car syms) | ||
| 1156 | syms (cdr syms)) | ||
| 1157 | (princ (symbol-name sym)) | ||
| 1158 | (princ "\t") | ||
| 1159 | (if (< (length (symbol-name sym)) 8) | ||
| 1160 | (princ "\t")) | ||
| 1161 | (prin1 (symbol-value sym)) | ||
| 1162 | (princ "\n") | ||
| 1163 | )))) | ||
| 1164 | |||
| 1165 | ;;; EDEBUG Handlers | ||
| 1166 | ;; | ||
| 1167 | (add-hook | ||
| 1168 | 'edebug-setup-hook | ||
| 1169 | #'(lambda () | ||
| 1170 | |||
| 1171 | (def-edebug-spec define-lex-spp-macro-declaration-analyzer | ||
| 1172 | (&define name stringp stringp form def-body) | ||
| 1173 | ) | ||
| 1174 | |||
| 1175 | (def-edebug-spec define-lex-spp-macro-undeclaration-analyzer | ||
| 1176 | (&define name stringp stringp form) | ||
| 1177 | ) | ||
| 1178 | |||
| 1179 | (def-edebug-spec define-lex-spp-include-analyzer | ||
| 1180 | (&define name stringp stringp form def-body) | ||
| 1181 | ) | ||
| 1182 | )) | ||
| 1183 | |||
| 1184 | |||
| 1185 | (provide 'semantic/lex-spp) | ||
| 1186 | |||
| 1187 | ;;; semantic-lex-spp.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..e38e1b94637 --- /dev/null +++ b/lisp/cedet/semantic/util-modes.el | |||
| @@ -0,0 +1,1228 @@ | |||
| 1 | ;;; semantic-util-modes.el --- Semantic minor modes | ||
| 2 | |||
| 3 | ;;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 | ||
| 4 | ;;; Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | ;; Authors: Eric M. Ludlam <zappo@gnu.org> | ||
| 7 | ;; David Ponce <david@dponce.com> | ||
| 8 | ;; Keywords: syntax | ||
| 9 | |||
| 10 | ;; This file is part of GNU Emacs. | ||
| 11 | |||
| 12 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 13 | ;; it under the terms of the GNU General Public License as published by | ||
| 14 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 15 | ;; (at your option) any later version. | ||
| 16 | |||
| 17 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 20 | ;; GNU General Public License for more details. | ||
| 21 | |||
| 22 | ;; You should have received a copy of the GNU General Public License | ||
| 23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 24 | |||
| 25 | ;;; Commentary: | ||
| 26 | ;; | ||
| 27 | ;; Semantic utility minor modes. | ||
| 28 | ;; | ||
| 29 | |||
| 30 | ;;; Code: | ||
| 31 | (require 'semantic) | ||
| 32 | |||
| 33 | (eval-when-compile | ||
| 34 | (require 'semantic/decorate) | ||
| 35 | ) | ||
| 36 | |||
| 37 | ;;; Compatibility | ||
| 38 | (if (fboundp 'propertize) | ||
| 39 | (defalias 'semantic-propertize 'propertize) | ||
| 40 | (defsubst semantic-propertize (string &rest properties) | ||
| 41 | "Return a copy of STRING with text properties added. | ||
| 42 | Dummy implementation for compatibility which just return STRING and | ||
| 43 | ignore PROPERTIES." | ||
| 44 | string) | ||
| 45 | ) | ||
| 46 | |||
| 47 | ;;; Group for all semantic enhancing modes | ||
| 48 | (defgroup semantic-modes nil | ||
| 49 | "Minor modes associated with the Semantic architecture." | ||
| 50 | :group 'semantic) | ||
| 51 | |||
| 52 | ;;;; | ||
| 53 | ;;;; Semantic minor modes stuff | ||
| 54 | ;;;; | ||
| 55 | (defcustom semantic-update-mode-line t | ||
| 56 | "*If non-nil, show enabled minor modes in the mode line. | ||
| 57 | Only minor modes that are not turned on globally are shown in the mode | ||
| 58 | line." | ||
| 59 | :group 'semantic | ||
| 60 | :type 'boolean | ||
| 61 | :require 'semantic/util-modes | ||
| 62 | :initialize 'custom-initialize-default | ||
| 63 | :set (lambda (sym val) | ||
| 64 | (set-default sym val) | ||
| 65 | ;; Update status of all Semantic enabled buffers | ||
| 66 | (semantic-map-buffers | ||
| 67 | #'semantic-mode-line-update))) | ||
| 68 | |||
| 69 | (defcustom semantic-mode-line-prefix | ||
| 70 | (semantic-propertize "S" 'face 'bold) | ||
| 71 | "*Prefix added to minor mode indicators in the mode line." | ||
| 72 | :group 'semantic | ||
| 73 | :type 'string | ||
| 74 | :require 'semantic/util-modes | ||
| 75 | :initialize 'custom-initialize-default) | ||
| 76 | |||
| 77 | (defvar semantic-minor-modes-status nil | ||
| 78 | "String showing Semantic minor modes which are locally enabled. | ||
| 79 | It is displayed in the mode line.") | ||
| 80 | (make-variable-buffer-local 'semantic-minor-modes-status) | ||
| 81 | |||
| 82 | (defvar semantic-minor-mode-alist nil | ||
| 83 | "Alist saying how to show Semantic minor modes in the mode line. | ||
| 84 | Like variable `minor-mode-alist'.") | ||
| 85 | |||
| 86 | (defun semantic-mode-line-update () | ||
| 87 | "Update display of Semantic minor modes in the mode line. | ||
| 88 | Only minor modes that are locally enabled are shown in the mode line." | ||
| 89 | (setq semantic-minor-modes-status nil) | ||
| 90 | (if semantic-update-mode-line | ||
| 91 | (let ((ml semantic-minor-mode-alist) | ||
| 92 | mm ms see) | ||
| 93 | (while ml | ||
| 94 | (setq mm (car ml) | ||
| 95 | ms (cadr mm) | ||
| 96 | mm (car mm) | ||
| 97 | ml (cdr ml)) | ||
| 98 | (when (and (symbol-value mm) | ||
| 99 | ;; Only show local minor mode status | ||
| 100 | (not (memq mm semantic-init-hooks))) | ||
| 101 | (and ms | ||
| 102 | (symbolp ms) | ||
| 103 | (setq ms (symbol-value ms))) | ||
| 104 | (and (stringp ms) | ||
| 105 | (not (member ms see)) ;; Don't duplicate same status | ||
| 106 | (setq see (cons ms see) | ||
| 107 | ms (if (string-match "^[ ]*\\(.+\\)" ms) | ||
| 108 | (match-string 1 ms))) | ||
| 109 | (setq semantic-minor-modes-status | ||
| 110 | (if semantic-minor-modes-status | ||
| 111 | (concat semantic-minor-modes-status "/" ms) | ||
| 112 | ms))))) | ||
| 113 | (if semantic-minor-modes-status | ||
| 114 | (setq semantic-minor-modes-status | ||
| 115 | (concat | ||
| 116 | " " | ||
| 117 | (if (string-match "^[ ]*\\(.+\\)" | ||
| 118 | semantic-mode-line-prefix) | ||
| 119 | (match-string 1 semantic-mode-line-prefix) | ||
| 120 | "S") | ||
| 121 | "/" | ||
| 122 | semantic-minor-modes-status)))))) | ||
| 123 | |||
| 124 | (defun semantic-desktop-ignore-this-minor-mode (buffer) | ||
| 125 | "Installed as a minor-mode initializer for Desktop mode. | ||
| 126 | BUFFER is the buffer to not initialize a Semantic minor mode in." | ||
| 127 | nil) | ||
| 128 | |||
| 129 | (defun semantic-add-minor-mode (toggle name &optional keymap) | ||
| 130 | "Register a new Semantic minor mode. | ||
| 131 | TOGGLE is a symbol which is the name of a buffer-local variable that | ||
| 132 | is toggled on or off to say whether the minor mode is active or not. | ||
| 133 | It is also an interactive function to toggle the mode. | ||
| 134 | |||
| 135 | NAME specifies what will appear in the mode line when the minor mode | ||
| 136 | is active. NAME should be either a string starting with a space, or a | ||
| 137 | symbol whose value is such a string. | ||
| 138 | |||
| 139 | Optional KEYMAP is the keymap for the minor mode that will be added to | ||
| 140 | `minor-mode-map-alist'." | ||
| 141 | ;; Add a dymmy semantic minor mode to display the status | ||
| 142 | (or (assq 'semantic-minor-modes-status minor-mode-alist) | ||
| 143 | (setq minor-mode-alist (cons (list 'semantic-minor-modes-status | ||
| 144 | 'semantic-minor-modes-status) | ||
| 145 | minor-mode-alist))) | ||
| 146 | (if (fboundp 'add-minor-mode) | ||
| 147 | ;; Emacs 21 & XEmacs | ||
| 148 | (add-minor-mode toggle "" keymap) | ||
| 149 | ;; Emacs 20 | ||
| 150 | (or (assq toggle minor-mode-alist) | ||
| 151 | (setq minor-mode-alist (cons (list toggle "") minor-mode-alist))) | ||
| 152 | (or (not keymap) | ||
| 153 | (assq toggle minor-mode-map-alist) | ||
| 154 | (setq minor-mode-map-alist (cons (cons toggle keymap) | ||
| 155 | minor-mode-map-alist)))) | ||
| 156 | ;; Record how to display this minor mode in the mode line | ||
| 157 | (let ((mm (assq toggle semantic-minor-mode-alist))) | ||
| 158 | (if mm | ||
| 159 | (setcdr mm (list name)) | ||
| 160 | (setq semantic-minor-mode-alist (cons (list toggle name) | ||
| 161 | semantic-minor-mode-alist)))) | ||
| 162 | |||
| 163 | ;; Semantic minor modes don't work w/ Desktop restore. | ||
| 164 | ;; This line will disable this minor mode from being restored | ||
| 165 | ;; by Desktop. | ||
| 166 | (when (boundp 'desktop-minor-mode-handlers) | ||
| 167 | (add-to-list 'desktop-minor-mode-handlers | ||
| 168 | (cons toggle 'semantic-desktop-ignore-this-minor-mode))) | ||
| 169 | ) | ||
| 170 | |||
| 171 | (defun semantic-toggle-minor-mode-globally (mode &optional arg) | ||
| 172 | "Toggle minor mode MODE in every Semantic enabled buffer. | ||
| 173 | Return non-nil if MODE is turned on in every Semantic enabled buffer. | ||
| 174 | If ARG is positive, enable, if it is negative, disable. If ARG is | ||
| 175 | nil, then toggle. Otherwise do nothing. MODE must be a valid minor | ||
| 176 | mode defined in `minor-mode-alist' and must be too an interactive | ||
| 177 | function used to toggle the mode." | ||
| 178 | (or (and (fboundp mode) (assq mode minor-mode-alist)) | ||
| 179 | (error "Semantic minor mode %s not found" mode)) | ||
| 180 | (if (not arg) | ||
| 181 | (if (memq mode semantic-init-hooks) | ||
| 182 | (setq arg -1) | ||
| 183 | (setq arg 1))) | ||
| 184 | ;; Add or remove the MODE toggle function from | ||
| 185 | ;; `semantic-init-hooks'. Then turn MODE on or off in every | ||
| 186 | ;; Semantic enabled buffer. | ||
| 187 | (cond | ||
| 188 | ;; Turn off if ARG < 0 | ||
| 189 | ((< arg 0) | ||
| 190 | (remove-hook 'semantic-init-hooks mode) | ||
| 191 | (semantic-map-buffers #'(lambda () (funcall mode -1))) | ||
| 192 | nil) | ||
| 193 | ;; Turn on if ARG > 0 | ||
| 194 | ((> arg 0) | ||
| 195 | (add-hook 'semantic-init-hooks mode) | ||
| 196 | (semantic-map-buffers #'(lambda () (funcall mode 1))) | ||
| 197 | t) | ||
| 198 | ;; Otherwise just check MODE state | ||
| 199 | (t | ||
| 200 | (memq mode semantic-init-hooks)) | ||
| 201 | )) | ||
| 202 | |||
| 203 | ;;;; | ||
| 204 | ;;;; Minor mode to highlight areas that a user edits. | ||
| 205 | ;;;; | ||
| 206 | |||
| 207 | (defun global-semantic-highlight-edits-mode (&optional arg) | ||
| 208 | "Toggle global use of option `semantic-highlight-edits-mode'. | ||
| 209 | If ARG is positive, enable, if it is negative, disable. | ||
| 210 | If ARG is nil, then toggle." | ||
| 211 | (interactive "P") | ||
| 212 | (setq global-semantic-highlight-edits-mode | ||
| 213 | (semantic-toggle-minor-mode-globally | ||
| 214 | 'semantic-highlight-edits-mode arg))) | ||
| 215 | |||
| 216 | (defcustom global-semantic-highlight-edits-mode nil | ||
| 217 | "*If non-nil enable global use of variable `semantic-highlight-edits-mode'. | ||
| 218 | When this mode is enabled, changes made to a buffer are highlighted | ||
| 219 | until the buffer is reparsed." | ||
| 220 | :group 'semantic | ||
| 221 | :group 'semantic-modes | ||
| 222 | :type 'boolean | ||
| 223 | :require 'semantic/util-modes | ||
| 224 | :initialize 'custom-initialize-default | ||
| 225 | :set (lambda (sym val) | ||
| 226 | (global-semantic-highlight-edits-mode (if val 1 -1)))) | ||
| 227 | |||
| 228 | (defcustom semantic-highlight-edits-mode-hook nil | ||
| 229 | "*Hook run at the end of function `semantic-highlight-edits-mode'." | ||
| 230 | :group 'semantic | ||
| 231 | :type 'hook) | ||
| 232 | |||
| 233 | (defface semantic-highlight-edits-face | ||
| 234 | '((((class color) (background dark)) | ||
| 235 | ;; Put this back to something closer to black later. | ||
| 236 | (:background "gray20")) | ||
| 237 | (((class color) (background light)) | ||
| 238 | (:background "gray90"))) | ||
| 239 | "*Face used to show dirty tokens in `semantic-highlight-edits-mode'." | ||
| 240 | :group 'semantic-faces) | ||
| 241 | |||
| 242 | (defun semantic-highlight-edits-new-change-hook-fcn (overlay) | ||
| 243 | "Function set into `semantic-edits-new-change-hook'. | ||
| 244 | Argument OVERLAY is the overlay created to mark the change. | ||
| 245 | This function will set the face property on this overlay." | ||
| 246 | (semantic-overlay-put overlay 'face 'semantic-highlight-edits-face)) | ||
| 247 | |||
| 248 | (defvar semantic-highlight-edits-mode-map | ||
| 249 | (let ((km (make-sparse-keymap))) | ||
| 250 | km) | ||
| 251 | "Keymap for highlight-edits minor mode.") | ||
| 252 | |||
| 253 | (defvar semantic-highlight-edits-mode nil | ||
| 254 | "Non-nil if highlight-edits minor mode is enabled. | ||
| 255 | Use the command `semantic-highlight-edits-mode' to change this variable.") | ||
| 256 | (make-variable-buffer-local 'semantic-highlight-edits-mode) | ||
| 257 | |||
| 258 | (defun semantic-highlight-edits-mode-setup () | ||
| 259 | "Setup option `semantic-highlight-edits-mode'. | ||
| 260 | The minor mode can be turned on only if semantic feature is available | ||
| 261 | and the current buffer was set up for parsing. When minor mode is | ||
| 262 | enabled parse the current buffer if needed. Return non-nil if the | ||
| 263 | minor mode is enabled." | ||
| 264 | (if semantic-highlight-edits-mode | ||
| 265 | (if (not (and (featurep 'semantic) (semantic-active-p))) | ||
| 266 | (progn | ||
| 267 | ;; Disable minor mode if semantic stuff not available | ||
| 268 | (setq semantic-highlight-edits-mode nil) | ||
| 269 | (error "Buffer %s was not set up for parsing" | ||
| 270 | (buffer-name))) | ||
| 271 | (semantic-make-local-hook 'semantic-edits-new-change-hooks) | ||
| 272 | (add-hook 'semantic-edits-new-change-hooks | ||
| 273 | 'semantic-highlight-edits-new-change-hook-fcn nil t) | ||
| 274 | ) | ||
| 275 | ;; Remove hooks | ||
| 276 | (remove-hook 'semantic-edits-new-change-hooks | ||
| 277 | 'semantic-highlight-edits-new-change-hook-fcn t) | ||
| 278 | ) | ||
| 279 | semantic-highlight-edits-mode) | ||
| 280 | |||
| 281 | (defun semantic-highlight-edits-mode (&optional arg) | ||
| 282 | "Minor mode for highlighting changes made in a buffer. | ||
| 283 | Changes are tracked by semantic so that the incremental parser can work | ||
| 284 | properly. | ||
| 285 | This mode will highlight those changes as they are made, and clear them | ||
| 286 | when the incremental parser accounts for those edits. | ||
| 287 | With prefix argument ARG, turn on if positive, otherwise off. The | ||
| 288 | minor mode can be turned on only if semantic feature is available and | ||
| 289 | the current buffer was set up for parsing. Return non-nil if the | ||
| 290 | minor mode is enabled." | ||
| 291 | (interactive | ||
| 292 | (list (or current-prefix-arg | ||
| 293 | (if semantic-highlight-edits-mode 0 1)))) | ||
| 294 | (setq semantic-highlight-edits-mode | ||
| 295 | (if arg | ||
| 296 | (> | ||
| 297 | (prefix-numeric-value arg) | ||
| 298 | 0) | ||
| 299 | (not semantic-highlight-edits-mode))) | ||
| 300 | (semantic-highlight-edits-mode-setup) | ||
| 301 | (run-hooks 'semantic-highlight-edits-mode-hook) | ||
| 302 | (if (interactive-p) | ||
| 303 | (message "highlight-edits minor mode %sabled" | ||
| 304 | (if semantic-highlight-edits-mode "en" "dis"))) | ||
| 305 | (semantic-mode-line-update) | ||
| 306 | semantic-highlight-edits-mode) | ||
| 307 | |||
| 308 | (semantic-add-minor-mode 'semantic-highlight-edits-mode | ||
| 309 | "e" | ||
| 310 | semantic-highlight-edits-mode-map) | ||
| 311 | |||
| 312 | |||
| 313 | ;;;; | ||
| 314 | ;;;; Minor mode to show unmatched-syntax elements | ||
| 315 | ;;;; | ||
| 316 | (defun global-semantic-show-unmatched-syntax-mode (&optional arg) | ||
| 317 | "Toggle global use of option `semantic-show-unmatched-syntax-mode'. | ||
| 318 | If ARG is positive, enable, if it is negative, disable. | ||
| 319 | If ARG is nil, then toggle." | ||
| 320 | (interactive "P") | ||
| 321 | (setq global-semantic-show-unmatched-syntax-mode | ||
| 322 | (semantic-toggle-minor-mode-globally | ||
| 323 | 'semantic-show-unmatched-syntax-mode arg))) | ||
| 324 | |||
| 325 | (defcustom global-semantic-show-unmatched-syntax-mode nil | ||
| 326 | "*If non-nil, enable global use of `semantic-show-unmatched-syntax-mode'. | ||
| 327 | When this mode is enabled, syntax in the current buffer which the | ||
| 328 | semantic parser cannot match is highlighted with a red underline." | ||
| 329 | :group 'semantic | ||
| 330 | :group 'semantic-modes | ||
| 331 | :type 'boolean | ||
| 332 | :require 'semantic/util-modes | ||
| 333 | :initialize 'custom-initialize-default | ||
| 334 | :set (lambda (sym val) | ||
| 335 | (global-semantic-show-unmatched-syntax-mode (if val 1 -1)))) | ||
| 336 | |||
| 337 | (defcustom semantic-show-unmatched-syntax-mode-hook nil | ||
| 338 | "*Hook run at the end of function `semantic-show-unmatched-syntax-mode'." | ||
| 339 | :group 'semantic | ||
| 340 | :type 'hook) | ||
| 341 | |||
| 342 | (defface semantic-unmatched-syntax-face | ||
| 343 | '((((class color) (background dark)) | ||
| 344 | (:underline "red")) | ||
| 345 | (((class color) (background light)) | ||
| 346 | (:underline "red"))) | ||
| 347 | "*Face used to show unmatched syntax in. | ||
| 348 | The face is used in `semantic-show-unmatched-syntax-mode'." | ||
| 349 | :group 'semantic-faces) | ||
| 350 | |||
| 351 | (defsubst semantic-unmatched-syntax-overlay-p (overlay) | ||
| 352 | "Return non-nil if OVERLAY is an unmatched syntax one." | ||
| 353 | (eq (semantic-overlay-get overlay 'semantic) 'unmatched)) | ||
| 354 | |||
| 355 | (defun semantic-showing-unmatched-syntax-p () | ||
| 356 | "Return non-nil if an unmatched syntax overlay was found in buffer." | ||
| 357 | (let ((ol (semantic-overlays-in (point-min) (point-max))) | ||
| 358 | found) | ||
| 359 | (while (and ol (not found)) | ||
| 360 | (setq found (semantic-unmatched-syntax-overlay-p (car ol)) | ||
| 361 | ol (cdr ol))) | ||
| 362 | found)) | ||
| 363 | |||
| 364 | (defun semantic-show-unmatched-lex-tokens-fetch () | ||
| 365 | "Fetch a list of unmatched lexical tokens from the current buffer. | ||
| 366 | Uses the overlays which have accurate bounds, and rebuilds what was | ||
| 367 | originally passed in." | ||
| 368 | (let ((ol (semantic-overlays-in (point-min) (point-max))) | ||
| 369 | (ustc nil)) | ||
| 370 | (while ol | ||
| 371 | (if (semantic-unmatched-syntax-overlay-p (car ol)) | ||
| 372 | (setq ustc (cons (cons 'thing | ||
| 373 | (cons (semantic-overlay-start (car ol)) | ||
| 374 | (semantic-overlay-end (car ol)))) | ||
| 375 | ustc))) | ||
| 376 | (setq ol (cdr ol))) | ||
| 377 | (nreverse ustc)) | ||
| 378 | ) | ||
| 379 | |||
| 380 | (defun semantic-clean-unmatched-syntax-in-region (beg end) | ||
| 381 | "Remove all unmatched syntax overlays between BEG and END." | ||
| 382 | (let ((ol (semantic-overlays-in beg end))) | ||
| 383 | (while ol | ||
| 384 | (if (semantic-unmatched-syntax-overlay-p (car ol)) | ||
| 385 | (semantic-overlay-delete (car ol))) | ||
| 386 | (setq ol (cdr ol))))) | ||
| 387 | |||
| 388 | (defsubst semantic-clean-unmatched-syntax-in-buffer () | ||
| 389 | "Remove all unmatched syntax overlays found in current buffer." | ||
| 390 | (semantic-clean-unmatched-syntax-in-region | ||
| 391 | (point-min) (point-max))) | ||
| 392 | |||
| 393 | (defsubst semantic-clean-token-of-unmatched-syntax (token) | ||
| 394 | "Clean the area covered by TOKEN of unmatched syntax markers." | ||
| 395 | (semantic-clean-unmatched-syntax-in-region | ||
| 396 | (semantic-tag-start token) (semantic-tag-end token))) | ||
| 397 | |||
| 398 | (defun semantic-show-unmatched-syntax (syntax) | ||
| 399 | "Function set into `semantic-unmatched-syntax-hook'. | ||
| 400 | This will highlight elements in SYNTAX as unmatched syntax." | ||
| 401 | ;; This is called when `semantic-show-unmatched-syntax-mode' is | ||
| 402 | ;; enabled. Highlight the unmatched syntax, and then add a semantic | ||
| 403 | ;; property to that overlay so we can add it to the official list of | ||
| 404 | ;; semantic supported overlays. This gets it cleaned up for errors, | ||
| 405 | ;; buffer cleaning, and the like. | ||
| 406 | (semantic-clean-unmatched-syntax-in-buffer) ;Clear previous highlighting | ||
| 407 | (if syntax | ||
| 408 | (let (o) | ||
| 409 | (while syntax | ||
| 410 | (setq o (semantic-make-overlay (semantic-lex-token-start (car syntax)) | ||
| 411 | (semantic-lex-token-end (car syntax)))) | ||
| 412 | (semantic-overlay-put o 'semantic 'unmatched) | ||
| 413 | (semantic-overlay-put o 'face 'semantic-unmatched-syntax-face) | ||
| 414 | (setq syntax (cdr syntax)))) | ||
| 415 | )) | ||
| 416 | |||
| 417 | (defun semantic-next-unmatched-syntax (point &optional bound) | ||
| 418 | "Find the next overlay for unmatched syntax after POINT. | ||
| 419 | Do not search past BOUND if non-nil." | ||
| 420 | (save-excursion | ||
| 421 | (goto-char point) | ||
| 422 | (let ((os point) (ol nil)) | ||
| 423 | (while (and os (< os (or bound (point-max))) (not ol)) | ||
| 424 | (setq os (semantic-overlay-next-change os)) | ||
| 425 | (when os | ||
| 426 | ;; Get overlays at position | ||
| 427 | (setq ol (semantic-overlays-at os)) | ||
| 428 | ;; find the overlay that belongs to semantic | ||
| 429 | ;; and starts at the found position. | ||
| 430 | (while (and ol (listp ol)) | ||
| 431 | (and (semantic-unmatched-syntax-overlay-p (car ol)) | ||
| 432 | (setq ol (car ol))) | ||
| 433 | (if (listp ol) | ||
| 434 | (setq ol (cdr ol)))))) | ||
| 435 | ol))) | ||
| 436 | |||
| 437 | (defvar semantic-show-unmatched-syntax-mode-map | ||
| 438 | (let ((km (make-sparse-keymap))) | ||
| 439 | (define-key km "\C-c,`" 'semantic-show-unmatched-syntax-next) | ||
| 440 | km) | ||
| 441 | "Keymap for command `semantic-show-unmatched-syntax-mode'.") | ||
| 442 | |||
| 443 | (defvar semantic-show-unmatched-syntax-mode nil | ||
| 444 | "Non-nil if show-unmatched-syntax minor mode is enabled. | ||
| 445 | Use the command `semantic-show-unmatched-syntax-mode' to change this | ||
| 446 | variable.") | ||
| 447 | (make-variable-buffer-local 'semantic-show-unmatched-syntax-mode) | ||
| 448 | |||
| 449 | (defun semantic-show-unmatched-syntax-mode-setup () | ||
| 450 | "Setup the `semantic-show-unmatched-syntax' minor mode. | ||
| 451 | The minor mode can be turned on only if semantic feature is available | ||
| 452 | and the current buffer was set up for parsing. When minor mode is | ||
| 453 | enabled parse the current buffer if needed. Return non-nil if the | ||
| 454 | minor mode is enabled." | ||
| 455 | (if semantic-show-unmatched-syntax-mode | ||
| 456 | (if (not (and (featurep 'semantic) (semantic-active-p))) | ||
| 457 | (progn | ||
| 458 | ;; Disable minor mode if semantic stuff not available | ||
| 459 | (setq semantic-show-unmatched-syntax-mode nil) | ||
| 460 | (error "Buffer %s was not set up for parsing" | ||
| 461 | (buffer-name))) | ||
| 462 | ;; Add hooks | ||
| 463 | (semantic-make-local-hook 'semantic-unmatched-syntax-hook) | ||
| 464 | (add-hook 'semantic-unmatched-syntax-hook | ||
| 465 | 'semantic-show-unmatched-syntax nil t) | ||
| 466 | (semantic-make-local-hook 'semantic-pre-clean-token-hooks) | ||
| 467 | (add-hook 'semantic-pre-clean-token-hooks | ||
| 468 | 'semantic-clean-token-of-unmatched-syntax nil t) | ||
| 469 | ;; Show unmatched syntax elements | ||
| 470 | (if (not (semantic--umatched-syntax-needs-refresh-p)) | ||
| 471 | (semantic-show-unmatched-syntax | ||
| 472 | (semantic-unmatched-syntax-tokens)))) | ||
| 473 | ;; Remove hooks | ||
| 474 | (remove-hook 'semantic-unmatched-syntax-hook | ||
| 475 | 'semantic-show-unmatched-syntax t) | ||
| 476 | (remove-hook 'semantic-pre-clean-token-hooks | ||
| 477 | 'semantic-clean-token-of-unmatched-syntax t) | ||
| 478 | ;; Cleanup unmatched-syntax highlighting | ||
| 479 | (semantic-clean-unmatched-syntax-in-buffer)) | ||
| 480 | semantic-show-unmatched-syntax-mode) | ||
| 481 | |||
| 482 | (defun semantic-show-unmatched-syntax-mode (&optional arg) | ||
| 483 | "Minor mode to highlight unmatched lexical syntax tokens. | ||
| 484 | When a parser executes, some elements in the buffer may not match any | ||
| 485 | parser rules. These text characters are considered unmatched syntax. | ||
| 486 | Often time, the display of unmatched syntax can expose coding | ||
| 487 | problems before the compiler is run. | ||
| 488 | |||
| 489 | With prefix argument ARG, turn on if positive, otherwise off. The | ||
| 490 | minor mode can be turned on only if semantic feature is available and | ||
| 491 | the current buffer was set up for parsing. Return non-nil if the | ||
| 492 | minor mode is enabled. | ||
| 493 | |||
| 494 | \\{semantic-show-unmatched-syntax-mode-map}" | ||
| 495 | (interactive | ||
| 496 | (list (or current-prefix-arg | ||
| 497 | (if semantic-show-unmatched-syntax-mode 0 1)))) | ||
| 498 | (setq semantic-show-unmatched-syntax-mode | ||
| 499 | (if arg | ||
| 500 | (> | ||
| 501 | (prefix-numeric-value arg) | ||
| 502 | 0) | ||
| 503 | (not semantic-show-unmatched-syntax-mode))) | ||
| 504 | (semantic-show-unmatched-syntax-mode-setup) | ||
| 505 | (run-hooks 'semantic-show-unmatched-syntax-mode-hook) | ||
| 506 | (if (interactive-p) | ||
| 507 | (message "show-unmatched-syntax minor mode %sabled" | ||
| 508 | (if semantic-show-unmatched-syntax-mode "en" "dis"))) | ||
| 509 | (semantic-mode-line-update) | ||
| 510 | semantic-show-unmatched-syntax-mode) | ||
| 511 | |||
| 512 | (semantic-add-minor-mode 'semantic-show-unmatched-syntax-mode | ||
| 513 | "u" | ||
| 514 | semantic-show-unmatched-syntax-mode-map) | ||
| 515 | |||
| 516 | (defun semantic-show-unmatched-syntax-next () | ||
| 517 | "Move forward to the next occurrence of unmatched syntax." | ||
| 518 | (interactive) | ||
| 519 | (let ((o (semantic-next-unmatched-syntax (point)))) | ||
| 520 | (if o | ||
| 521 | (goto-char (semantic-overlay-start o))))) | ||
| 522 | |||
| 523 | |||
| 524 | ;;;; | ||
| 525 | ;;;; Minor mode to display the parser state in the modeline. | ||
| 526 | ;;;; | ||
| 527 | |||
| 528 | (defcustom global-semantic-show-parser-state-mode nil | ||
| 529 | "*If non-nil enable global use of `semantic-show-parser-state-mode'. | ||
| 530 | When enabled, the current parse state of the current buffer is displayed | ||
| 531 | in the mode line. See `semantic-show-parser-state-marker' for details | ||
| 532 | on what is displayed." | ||
| 533 | :group 'semantic | ||
| 534 | :type 'boolean | ||
| 535 | :require 'semantic/util-modes | ||
| 536 | :initialize 'custom-initialize-default | ||
| 537 | :set (lambda (sym val) | ||
| 538 | (global-semantic-show-parser-state-mode (if val 1 -1)))) | ||
| 539 | |||
| 540 | (defun global-semantic-show-parser-state-mode (&optional arg) | ||
| 541 | "Toggle global use of option `semantic-show-parser-state-mode'. | ||
| 542 | If ARG is positive, enable, if it is negative, disable. | ||
| 543 | If ARG is nil, then toggle." | ||
| 544 | (interactive "P") | ||
| 545 | (setq global-semantic-show-parser-state-mode | ||
| 546 | (semantic-toggle-minor-mode-globally | ||
| 547 | 'semantic-show-parser-state-mode arg))) | ||
| 548 | |||
| 549 | (defcustom semantic-show-parser-state-mode-hook nil | ||
| 550 | "*Hook run at the end of function `semantic-show-parser-state-mode'." | ||
| 551 | :group 'semantic | ||
| 552 | :type 'hook) | ||
| 553 | |||
| 554 | (defvar semantic-show-parser-state-mode-map | ||
| 555 | (let ((km (make-sparse-keymap))) | ||
| 556 | km) | ||
| 557 | "Keymap for show-parser-state minor mode.") | ||
| 558 | |||
| 559 | (defvar semantic-show-parser-state-mode nil | ||
| 560 | "Non-nil if show-parser-state minor mode is enabled. | ||
| 561 | Use the command `semantic-show-parser-state-mode' to change this variable.") | ||
| 562 | (make-variable-buffer-local 'semantic-show-parser-state-mode) | ||
| 563 | |||
| 564 | (defun semantic-show-parser-state-mode-setup () | ||
| 565 | "Setup option `semantic-show-parser-state-mode'. | ||
| 566 | The minor mode can be turned on only if semantic feature is available | ||
| 567 | and the current buffer was set up for parsing. When minor mode is | ||
| 568 | enabled parse the current buffer if needed. Return non-nil if the | ||
| 569 | minor mode is enabled." | ||
| 570 | (if semantic-show-parser-state-mode | ||
| 571 | (if (not (and (featurep 'semantic) (semantic-active-p))) | ||
| 572 | (progn | ||
| 573 | ;; Disable minor mode if semantic stuff not available | ||
| 574 | (setq semantic-show-parser-state-mode nil) | ||
| 575 | (error "Buffer %s was not set up for parsing" | ||
| 576 | (buffer-name))) | ||
| 577 | ;; Set up mode line | ||
| 578 | |||
| 579 | (when (not | ||
| 580 | (memq 'semantic-show-parser-state-string mode-line-modified)) | ||
| 581 | (setq mode-line-modified | ||
| 582 | (append mode-line-modified | ||
| 583 | '(semantic-show-parser-state-string)))) | ||
| 584 | ;; Add hooks | ||
| 585 | (semantic-make-local-hook 'semantic-edits-new-change-hooks) | ||
| 586 | (add-hook 'semantic-edits-new-change-hooks | ||
| 587 | 'semantic-show-parser-state-marker nil t) | ||
| 588 | (semantic-make-local-hook 'semantic-edits-incremental-reparse-failed-hooks) | ||
| 589 | (add-hook 'semantic-edits-incremental-reparse-failed-hooks | ||
| 590 | 'semantic-show-parser-state-marker nil t) | ||
| 591 | (semantic-make-local-hook 'semantic-after-partial-cache-change-hook) | ||
| 592 | (add-hook 'semantic-after-partial-cache-change-hook | ||
| 593 | 'semantic-show-parser-state-marker nil t) | ||
| 594 | (semantic-make-local-hook 'semantic-after-toplevel-cache-change-hook) | ||
| 595 | (add-hook 'semantic-after-toplevel-cache-change-hook | ||
| 596 | 'semantic-show-parser-state-marker nil t) | ||
| 597 | (semantic-show-parser-state-marker) | ||
| 598 | |||
| 599 | (semantic-make-local-hook 'semantic-before-auto-parse-hooks) | ||
| 600 | (add-hook 'semantic-before-auto-parse-hooks | ||
| 601 | 'semantic-show-parser-state-auto-marker nil t) | ||
| 602 | (semantic-make-local-hook 'semantic-after-auto-parse-hooks) | ||
| 603 | (add-hook 'semantic-after-auto-parse-hooks | ||
| 604 | 'semantic-show-parser-state-marker nil t) | ||
| 605 | |||
| 606 | (semantic-make-local-hook 'semantic-before-idle-scheduler-reparse-hooks) | ||
| 607 | (add-hook 'semantic-before-idle-scheduler-reparse-hooks | ||
| 608 | 'semantic-show-parser-state-auto-marker nil t) | ||
| 609 | (semantic-make-local-hook 'semantic-after-idle-scheduler-reparse-hooks) | ||
| 610 | (add-hook 'semantic-after-idle-scheduler-reparse-hooks | ||
| 611 | 'semantic-show-parser-state-marker nil t) | ||
| 612 | ) | ||
| 613 | ;; Remove parts of mode line | ||
| 614 | (setq mode-line-modified | ||
| 615 | (delq 'semantic-show-parser-state-string mode-line-modified)) | ||
| 616 | ;; Remove hooks | ||
| 617 | (remove-hook 'semantic-edits-new-change-hooks | ||
| 618 | 'semantic-show-parser-state-marker t) | ||
| 619 | (remove-hook 'semantic-edits-incremental-reparse-failed-hooks | ||
| 620 | 'semantic-show-parser-state-marker t) | ||
| 621 | (remove-hook 'semantic-after-partial-cache-change-hook | ||
| 622 | 'semantic-show-parser-state-marker t) | ||
| 623 | (remove-hook 'semantic-after-toplevel-cache-change-hook | ||
| 624 | 'semantic-show-parser-state-marker t) | ||
| 625 | |||
| 626 | (remove-hook 'semantic-before-auto-parse-hooks | ||
| 627 | 'semantic-show-parser-state-auto-marker t) | ||
| 628 | (remove-hook 'semantic-after-auto-parse-hooks | ||
| 629 | 'semantic-show-parser-state-marker t) | ||
| 630 | |||
| 631 | (remove-hook 'semantic-before-idle-scheduler-reparse-hooks | ||
| 632 | 'semantic-show-parser-state-auto-marker t) | ||
| 633 | (remove-hook 'semantic-after-idle-scheduler-reparse-hooks | ||
| 634 | 'semantic-show-parser-state-marker t) | ||
| 635 | ) | ||
| 636 | semantic-show-parser-state-mode) | ||
| 637 | |||
| 638 | (defun semantic-show-parser-state-mode (&optional arg) | ||
| 639 | "Minor mode for displaying parser cache state in the modeline. | ||
| 640 | The cache can be in one of three states. They are | ||
| 641 | Up to date, Partial reprase needed, and Full reparse needed. | ||
| 642 | The state is indicated in the modeline with the following characters: | ||
| 643 | `-' -> The cache is up to date. | ||
| 644 | `!' -> The cache requires a full update. | ||
| 645 | `~' -> The cache needs to be incrementally parsed. | ||
| 646 | `%' -> The cache is not currently parseable. | ||
| 647 | `@' -> Auto-parse in progress (not set here.) | ||
| 648 | With prefix argument ARG, turn on if positive, otherwise off. The | ||
| 649 | minor mode can be turned on only if semantic feature is available and | ||
| 650 | the current buffer was set up for parsing. Return non-nil if the | ||
| 651 | minor mode is enabled." | ||
| 652 | (interactive | ||
| 653 | (list (or current-prefix-arg | ||
| 654 | (if semantic-show-parser-state-mode 0 1)))) | ||
| 655 | (setq semantic-show-parser-state-mode | ||
| 656 | (if arg | ||
| 657 | (> | ||
| 658 | (prefix-numeric-value arg) | ||
| 659 | 0) | ||
| 660 | (not semantic-show-parser-state-mode))) | ||
| 661 | (semantic-show-parser-state-mode-setup) | ||
| 662 | (run-hooks 'semantic-show-parser-state-mode-hook) | ||
| 663 | (if (interactive-p) | ||
| 664 | (message "show-parser-state minor mode %sabled" | ||
| 665 | (if semantic-show-parser-state-mode "en" "dis"))) | ||
| 666 | (semantic-mode-line-update) | ||
| 667 | semantic-show-parser-state-mode) | ||
| 668 | |||
| 669 | (semantic-add-minor-mode 'semantic-show-parser-state-mode | ||
| 670 | "" | ||
| 671 | semantic-show-parser-state-mode-map) | ||
| 672 | |||
| 673 | (defvar semantic-show-parser-state-string nil | ||
| 674 | "String showing the parser state for this buffer. | ||
| 675 | See `semantic-show-parser-state-marker' for details.") | ||
| 676 | (make-variable-buffer-local 'semantic-show-parser-state-string) | ||
| 677 | |||
| 678 | (defun semantic-show-parser-state-marker (&rest ignore) | ||
| 679 | "Set `semantic-show-parser-state-string' to indicate parser state. | ||
| 680 | This marker is one of the following: | ||
| 681 | `-' -> The cache is up to date. | ||
| 682 | `!' -> The cache requires a full update. | ||
| 683 | `~' -> The cache needs to be incrementally parsed. | ||
| 684 | `%' -> The cache is not currently parseable. | ||
| 685 | `@' -> Auto-parse in progress (not set here.) | ||
| 686 | Arguments IGNORE are ignored, and accepted so this can be used as a hook | ||
| 687 | in many situations." | ||
| 688 | (setq semantic-show-parser-state-string | ||
| 689 | (cond ((semantic-parse-tree-needs-rebuild-p) | ||
| 690 | "!") | ||
| 691 | ((semantic-parse-tree-needs-update-p) | ||
| 692 | "^") | ||
| 693 | ((semantic-parse-tree-unparseable-p) | ||
| 694 | "%") | ||
| 695 | (t | ||
| 696 | "-"))) | ||
| 697 | ;;(message "Setup mode line indicator to [%s]" semantic-show-parser-state-string) | ||
| 698 | (semantic-mode-line-update)) | ||
| 699 | |||
| 700 | (defun semantic-show-parser-state-auto-marker () | ||
| 701 | "Hook function run before an autoparse. | ||
| 702 | Set up `semantic-show-parser-state-marker' to show `@' | ||
| 703 | to indicate a parse in progress." | ||
| 704 | (unless (semantic-parse-tree-up-to-date-p) | ||
| 705 | (setq semantic-show-parser-state-string "@") | ||
| 706 | (semantic-mode-line-update) | ||
| 707 | ;; For testing. | ||
| 708 | ;;(sit-for 1) | ||
| 709 | )) | ||
| 710 | |||
| 711 | |||
| 712 | ;;;; | ||
| 713 | ;;;; Minor mode to make function decls sticky. | ||
| 714 | ;;;; | ||
| 715 | |||
| 716 | (defun global-semantic-stickyfunc-mode (&optional arg) | ||
| 717 | "Toggle global use of option `semantic-stickyfunc-mode'. | ||
| 718 | If ARG is positive, enable, if it is negative, disable. | ||
| 719 | If ARG is nil, then toggle." | ||
| 720 | (interactive "P") | ||
| 721 | (setq global-semantic-stickyfunc-mode | ||
| 722 | (semantic-toggle-minor-mode-globally | ||
| 723 | 'semantic-stickyfunc-mode arg))) | ||
| 724 | |||
| 725 | (defcustom global-semantic-stickyfunc-mode nil | ||
| 726 | "*If non-nil, enable global use of `semantic-stickyfunc-mode'. | ||
| 727 | This minor mode only works for Emacs 21 or later. | ||
| 728 | When enabled, the header line is enabled, and the first line | ||
| 729 | of the current function or method is displayed in it. | ||
| 730 | This makes it appear that the first line of that tag is | ||
| 731 | `sticky' to the top of the window." | ||
| 732 | :group 'semantic | ||
| 733 | :group 'semantic-modes | ||
| 734 | :type 'boolean | ||
| 735 | :require 'semantic/util-modes | ||
| 736 | :initialize 'custom-initialize-default | ||
| 737 | :set (lambda (sym val) | ||
| 738 | (global-semantic-stickyfunc-mode (if val 1 -1)))) | ||
| 739 | |||
| 740 | (defcustom semantic-stickyfunc-mode-hook nil | ||
| 741 | "*Hook run at the end of function `semantic-stickyfunc-mode'." | ||
| 742 | :group 'semantic | ||
| 743 | :type 'hook) | ||
| 744 | |||
| 745 | (defvar semantic-stickyfunc-mode-map | ||
| 746 | (let ((km (make-sparse-keymap))) | ||
| 747 | (define-key km [ header-line down-mouse-1 ] 'semantic-stickyfunc-menu) | ||
| 748 | km) | ||
| 749 | "Keymap for stickyfunc minor mode.") | ||
| 750 | |||
| 751 | (defvar semantic-stickyfunc-popup-menu nil | ||
| 752 | "Menu used if the user clicks on the header line used by stickyfunc mode.") | ||
| 753 | |||
| 754 | (easy-menu-define | ||
| 755 | semantic-stickyfunc-popup-menu | ||
| 756 | semantic-stickyfunc-mode-map | ||
| 757 | "Stickyfunc Menu" | ||
| 758 | '("Stickyfunc Mode" :visible (progn nil) | ||
| 759 | [ "Copy Headerline Tag" senator-copy-tag | ||
| 760 | :active (semantic-current-tag) | ||
| 761 | :help "Copy the current tag to the tag ring"] | ||
| 762 | [ "Kill Headerline Tag" senator-kill-tag | ||
| 763 | :active (semantic-current-tag) | ||
| 764 | :help "Kill tag text to the kill ring, and copy the tag to the tag ring" | ||
| 765 | ] | ||
| 766 | [ "Copy Headerline Tag to Register" senator-copy-tag-to-register | ||
| 767 | :active (semantic-current-tag) | ||
| 768 | :help "Copy the current tag to a register" | ||
| 769 | ] | ||
| 770 | [ "Narrow To Headerline Tag" senator-narrow-to-defun | ||
| 771 | :active (semantic-current-tag) | ||
| 772 | :help "Narrow to the bounds of the current tag."] | ||
| 773 | [ "Fold Headerline Tag" senator-fold-tag-toggle | ||
| 774 | :active (semantic-current-tag) | ||
| 775 | :style toggle | ||
| 776 | :selected (let ((tag (semantic-current-tag))) | ||
| 777 | (and tag (semantic-tag-folded-p tag))) | ||
| 778 | :help "Fold the current tag to one line" | ||
| 779 | ] | ||
| 780 | "---" | ||
| 781 | [ "About This Header Line" | ||
| 782 | (lambda () (interactive) | ||
| 783 | (describe-function 'semantic-stickyfunc-mode)) t]) | ||
| 784 | ) | ||
| 785 | |||
| 786 | (defvar semantic-stickyfunc-mode nil | ||
| 787 | "Non-nil if stickyfunc minor mode is enabled. | ||
| 788 | Use the command `semantic-stickyfunc-mode' to change this variable.") | ||
| 789 | (make-variable-buffer-local 'semantic-stickyfunc-mode) | ||
| 790 | |||
| 791 | (defcustom semantic-stickyfunc-indent-string | ||
| 792 | (if (and window-system (not (featurep 'xemacs))) | ||
| 793 | (concat | ||
| 794 | (condition-case nil | ||
| 795 | ;; Test scroll bar location | ||
| 796 | (let ((charwidth (frame-char-width)) | ||
| 797 | (scrollpos (frame-parameter (selected-frame) | ||
| 798 | 'vertical-scroll-bars)) | ||
| 799 | ) | ||
| 800 | (if (or (eq scrollpos 'left) | ||
| 801 | ;; Now wait a minute. If you turn scroll-bar-mode | ||
| 802 | ;; on, then off, the new value is t, not left. | ||
| 803 | ;; Will this mess up older emacs where the default | ||
| 804 | ;; was on the right? I don't think so since they don't | ||
| 805 | ;; support a header line. | ||
| 806 | (eq scrollpos t)) | ||
| 807 | (let ((w (when (boundp 'scroll-bar-width) | ||
| 808 | (symbol-value 'scroll-bar-width)))) | ||
| 809 | |||
| 810 | (if (not w) | ||
| 811 | (setq w (frame-parameter (selected-frame) | ||
| 812 | 'scroll-bar-width))) | ||
| 813 | |||
| 814 | ;; in 21.2, the frame parameter is sometimes empty | ||
| 815 | ;; so we need to get the value here. | ||
| 816 | (if (not w) | ||
| 817 | (setq w (+ (get 'scroll-bar-width 'x-frame-parameter) | ||
| 818 | ;; In 21.4, or perhaps 22.1 the x-frame | ||
| 819 | ;; parameter is different from the frame | ||
| 820 | ;; parameter by only 1 pixel. | ||
| 821 | 1))) | ||
| 822 | |||
| 823 | (if (not w) | ||
| 824 | " " | ||
| 825 | (setq w (+ 2 w)) ; Some sort of border around | ||
| 826 | ; the scrollbar. | ||
| 827 | (make-string (/ w charwidth) ? ))) | ||
| 828 | "")) | ||
| 829 | (error "")) | ||
| 830 | (condition-case nil | ||
| 831 | ;; Test fringe size. | ||
| 832 | (let* ((f (window-fringes)) | ||
| 833 | (fw (car f)) | ||
| 834 | (numspace (/ fw (frame-char-width))) | ||
| 835 | ) | ||
| 836 | (make-string numspace ? )) | ||
| 837 | (error | ||
| 838 | ;; Well, the fancy new Emacs functions failed. Try older | ||
| 839 | ;; tricks. | ||
| 840 | (condition-case nil | ||
| 841 | ;; I'm not so sure what's up with the 21.1-21.3 fringe. | ||
| 842 | ;; It looks to be about 1 space wide. | ||
| 843 | (if (get 'fringe 'face) | ||
| 844 | " " | ||
| 845 | "") | ||
| 846 | (error "")))) | ||
| 847 | ) | ||
| 848 | ;; Not Emacs or a window system means no scrollbar or fringe, | ||
| 849 | ;; and perhaps not even a header line to worry about. | ||
| 850 | "") | ||
| 851 | "*String used to indent the stickyfunc header. | ||
| 852 | Customize this string to match the space used by scrollbars and | ||
| 853 | fringe so it does not appear that the code is moving left/right | ||
| 854 | when it lands in the sticky line." | ||
| 855 | :group 'semantic | ||
| 856 | :type 'string) | ||
| 857 | |||
| 858 | (defvar semantic-stickyfunc-old-hlf nil | ||
| 859 | "Value of the header line when entering sticky func mode.") | ||
| 860 | |||
| 861 | (defconst semantic-stickyfunc-header-line-format | ||
| 862 | (cond ((featurep 'xemacs) | ||
| 863 | nil) | ||
| 864 | ((>= emacs-major-version 22) | ||
| 865 | '(:eval (list | ||
| 866 | ;; Magic bit I found on emacswiki. | ||
| 867 | (propertize " " 'display '((space :align-to 0))) | ||
| 868 | (semantic-stickyfunc-fetch-stickyline)))) | ||
| 869 | ((= emacs-major-version 21) | ||
| 870 | '(:eval (list semantic-stickyfunc-indent-string | ||
| 871 | (semantic-stickyfunc-fetch-stickyline)))) | ||
| 872 | (t nil)) | ||
| 873 | "The header line format used by sticky func mode.") | ||
| 874 | |||
| 875 | (defun semantic-stickyfunc-mode-setup () | ||
| 876 | "Setup option `semantic-stickyfunc-mode'. | ||
| 877 | For semantic enabled buffers, make the function declaration for the top most | ||
| 878 | function \"sticky\". This is accomplished by putting the first line of | ||
| 879 | text for that function in Emacs 21's header line." | ||
| 880 | (if semantic-stickyfunc-mode | ||
| 881 | (progn | ||
| 882 | (unless (and (featurep 'semantic) (semantic-active-p)) | ||
| 883 | ;; Disable minor mode if semantic stuff not available | ||
| 884 | (setq semantic-stickyfunc-mode nil) | ||
| 885 | (error "Buffer %s was not set up for parsing" (buffer-name))) | ||
| 886 | (unless (boundp 'default-header-line-format) | ||
| 887 | ;; Disable if there are no header lines to use. | ||
| 888 | (setq semantic-stickyfunc-mode nil) | ||
| 889 | (error "Sticky Function mode requires Emacs 21")) | ||
| 890 | ;; Enable the mode | ||
| 891 | ;; Save previous buffer local value of header line format. | ||
| 892 | (when (and (local-variable-p 'header-line-format (current-buffer)) | ||
| 893 | (not (eq header-line-format | ||
| 894 | semantic-stickyfunc-header-line-format))) | ||
| 895 | (set (make-local-variable 'semantic-stickyfunc-old-hlf) | ||
| 896 | header-line-format)) | ||
| 897 | (setq header-line-format semantic-stickyfunc-header-line-format) | ||
| 898 | ) | ||
| 899 | ;; Disable sticky func mode | ||
| 900 | ;; Restore previous buffer local value of header line format if | ||
| 901 | ;; the current one is the sticky func one. | ||
| 902 | (when (eq header-line-format semantic-stickyfunc-header-line-format) | ||
| 903 | (kill-local-variable 'header-line-format) | ||
| 904 | (when (local-variable-p 'semantic-stickyfunc-old-hlf (current-buffer)) | ||
| 905 | (setq header-line-format semantic-stickyfunc-old-hlf) | ||
| 906 | (kill-local-variable 'semantic-stickyfunc-old-hlf)))) | ||
| 907 | semantic-stickyfunc-mode) | ||
| 908 | |||
| 909 | (defun semantic-stickyfunc-mode (&optional arg) | ||
| 910 | "Minor mode to show the title of a tag in the header line. | ||
| 911 | Enables/disables making the header line of functions sticky. | ||
| 912 | A function (or other tag class specified by | ||
| 913 | `semantic-stickyfunc-sticky-classes') has a header line, meaning the | ||
| 914 | first line which describes the rest of the construct. This first | ||
| 915 | line is what is displayed in the Emacs 21 header line. | ||
| 916 | |||
| 917 | With prefix argument ARG, turn on if positive, otherwise off. The | ||
| 918 | minor mode can be turned on only if semantic feature is available and | ||
| 919 | the current buffer was set up for parsing. Return non-nil if the | ||
| 920 | minor mode is enabled." | ||
| 921 | (interactive | ||
| 922 | (list (or current-prefix-arg | ||
| 923 | (if semantic-stickyfunc-mode 0 1)))) | ||
| 924 | (setq semantic-stickyfunc-mode | ||
| 925 | (if arg | ||
| 926 | (> | ||
| 927 | (prefix-numeric-value arg) | ||
| 928 | 0) | ||
| 929 | (not semantic-stickyfunc-mode))) | ||
| 930 | (semantic-stickyfunc-mode-setup) | ||
| 931 | (run-hooks 'semantic-stickyfunc-mode-hook) | ||
| 932 | (if (interactive-p) | ||
| 933 | (message "Stickyfunc minor mode %sabled" | ||
| 934 | (if semantic-stickyfunc-mode "en" "dis"))) | ||
| 935 | (semantic-mode-line-update) | ||
| 936 | semantic-stickyfunc-mode) | ||
| 937 | |||
| 938 | (defvar semantic-stickyfunc-sticky-classes | ||
| 939 | '(function type) | ||
| 940 | "List of tag classes which sticky func will display in the header line.") | ||
| 941 | (make-variable-buffer-local 'semantic-stickyfunc-sticky-classes) | ||
| 942 | |||
| 943 | (defun semantic-stickyfunc-tag-to-stick () | ||
| 944 | "Return the tag to stick at the current point." | ||
| 945 | (let ((tags (nreverse (semantic-find-tag-by-overlay (point))))) | ||
| 946 | ;; Get rid of non-matching tags. | ||
| 947 | (while (and tags | ||
| 948 | (not (member | ||
| 949 | (semantic-tag-class (car tags)) | ||
| 950 | semantic-stickyfunc-sticky-classes)) | ||
| 951 | ) | ||
| 952 | (setq tags (cdr tags))) | ||
| 953 | (car tags))) | ||
| 954 | |||
| 955 | (defun semantic-stickyfunc-fetch-stickyline () | ||
| 956 | "Make the function at the top of the current window sticky. | ||
| 957 | Capture it's function declaration, and place it in the header line. | ||
| 958 | If there is no function, disable the header line." | ||
| 959 | (let ((str | ||
| 960 | (save-excursion | ||
| 961 | (goto-char (window-start (selected-window))) | ||
| 962 | (forward-line -1) | ||
| 963 | (end-of-line) | ||
| 964 | ;; Capture this function | ||
| 965 | (let* ((tag (semantic-stickyfunc-tag-to-stick))) | ||
| 966 | ;; TAG is nil if there was nothing of the apropriate type there. | ||
| 967 | (if (not tag) | ||
| 968 | ;; Set it to be the text under the header line | ||
| 969 | (buffer-substring (point-at-bol) (point-at-eol)) | ||
| 970 | ;; Get it | ||
| 971 | (goto-char (semantic-tag-start tag)) | ||
| 972 | ;; Klaus Berndl <klaus.berndl@sdm.de>: | ||
| 973 | ;; goto the tag name; this is especially needed for languages | ||
| 974 | ;; like c++ where a often used style is like: | ||
| 975 | ;; void | ||
| 976 | ;; ClassX::methodM(arg1...) | ||
| 977 | ;; { | ||
| 978 | ;; ... | ||
| 979 | ;; } | ||
| 980 | ;; Without going to the tag-name we would get"void" in the | ||
| 981 | ;; header line which is IMHO not really useful | ||
| 982 | (search-forward (semantic-tag-name tag) nil t) | ||
| 983 | (buffer-substring (point-at-bol) (point-at-eol)) | ||
| 984 | )))) | ||
| 985 | (start 0)) | ||
| 986 | (while (string-match "%" str start) | ||
| 987 | (setq str (replace-match "%%" t t str 0) | ||
| 988 | start (1+ (match-end 0))) | ||
| 989 | ) | ||
| 990 | ;; In 21.4 (or 22.1) the heder doesn't expand tabs. Hmmmm. | ||
| 991 | ;; We should replace them here. | ||
| 992 | ;; | ||
| 993 | ;; This hack assumes that tabs are kept smartly at tab boundaries | ||
| 994 | ;; instead of in a tab boundary where it might only represent 4 spaces. | ||
| 995 | (while (string-match "\t" str start) | ||
| 996 | (setq str (replace-match " " t t str 0))) | ||
| 997 | str)) | ||
| 998 | |||
| 999 | (defun semantic-stickyfunc-menu (event) | ||
| 1000 | "Popup a menu that can help a user understand stickyfunc-mode. | ||
| 1001 | Argument EVENT describes the event that caused this function to be called." | ||
| 1002 | (interactive "e") | ||
| 1003 | (let* ((startwin (selected-window)) | ||
| 1004 | (win (car (car (cdr event)))) | ||
| 1005 | ) | ||
| 1006 | (select-window win t) | ||
| 1007 | (save-excursion | ||
| 1008 | (goto-char (window-start win)) | ||
| 1009 | (sit-for 0) | ||
| 1010 | (popup-menu semantic-stickyfunc-popup-menu event) | ||
| 1011 | ) | ||
| 1012 | (select-window startwin))) | ||
| 1013 | |||
| 1014 | |||
| 1015 | (semantic-add-minor-mode 'semantic-stickyfunc-mode | ||
| 1016 | "" ;; Don't need indicator. It's quite visible | ||
| 1017 | semantic-stickyfunc-mode-map) | ||
| 1018 | |||
| 1019 | |||
| 1020 | |||
| 1021 | ;;;; | ||
| 1022 | ;;;; Minor mode to make highlight the current function | ||
| 1023 | ;;;; | ||
| 1024 | |||
| 1025 | ;; Highlight the first like of the function we are in if it is different | ||
| 1026 | ;; from the the tag going off the top of the screen. | ||
| 1027 | (defun global-semantic-highlight-func-mode (&optional arg) | ||
| 1028 | "Toggle global use of option `semantic-highlight-func-mode'. | ||
| 1029 | If ARG is positive, enable, if it is negative, disable. | ||
| 1030 | If ARG is nil, then toggle." | ||
| 1031 | (interactive "P") | ||
| 1032 | (setq global-semantic-highlight-func-mode | ||
| 1033 | (semantic-toggle-minor-mode-globally | ||
| 1034 | 'semantic-highlight-func-mode arg))) | ||
| 1035 | |||
| 1036 | (defcustom global-semantic-highlight-func-mode nil | ||
| 1037 | "*If non-nil, enable global use of `semantic-highlight-func-mode'. | ||
| 1038 | When enabled, the first line of the current tag is highlighted." | ||
| 1039 | :group 'semantic | ||
| 1040 | :group 'semantic-modes | ||
| 1041 | :type 'boolean | ||
| 1042 | :require 'semantic/util-modes | ||
| 1043 | :initialize 'custom-initialize-default | ||
| 1044 | :set (lambda (sym val) | ||
| 1045 | (global-semantic-highlight-func-mode (if val 1 -1)))) | ||
| 1046 | |||
| 1047 | (defcustom semantic-highlight-func-mode-hook nil | ||
| 1048 | "*Hook run at the end of function `semantic-highlight-func-mode'." | ||
| 1049 | :group 'semantic | ||
| 1050 | :type 'hook) | ||
| 1051 | |||
| 1052 | (defvar semantic-highlight-func-mode-map | ||
| 1053 | (let ((km (make-sparse-keymap)) | ||
| 1054 | (m3 (if (featurep 'xemacs) [ button3 ] [ mouse-3 ])) | ||
| 1055 | ) | ||
| 1056 | (define-key km m3 'semantic-highlight-func-menu) | ||
| 1057 | km) | ||
| 1058 | "Keymap for highlight-func minor mode.") | ||
| 1059 | |||
| 1060 | (defvar semantic-highlight-func-popup-menu nil | ||
| 1061 | "Menu used if the user clicks on the header line used by `semantic-highlight-func-mode'.") | ||
| 1062 | |||
| 1063 | (easy-menu-define | ||
| 1064 | semantic-highlight-func-popup-menu | ||
| 1065 | semantic-highlight-func-mode-map | ||
| 1066 | "Highlight-Func Menu" | ||
| 1067 | '("Highlight-Func Mode" :visible (progn nil) | ||
| 1068 | [ "Copy Tag" senator-copy-tag | ||
| 1069 | :active (semantic-current-tag) | ||
| 1070 | :help "Copy the current tag to the tag ring"] | ||
| 1071 | [ "Kill Tag" senator-kill-tag | ||
| 1072 | :active (semantic-current-tag) | ||
| 1073 | :help "Kill tag text to the kill ring, and copy the tag to the tag ring" | ||
| 1074 | ] | ||
| 1075 | [ "Copy Tag to Register" senator-copy-tag-to-register | ||
| 1076 | :active (semantic-current-tag) | ||
| 1077 | :help "Copy the current tag to a register" | ||
| 1078 | ] | ||
| 1079 | [ "Narrow To Tag" senator-narrow-to-defun | ||
| 1080 | :active (semantic-current-tag) | ||
| 1081 | :help "Narrow to the bounds of the current tag."] | ||
| 1082 | [ "Fold Tag" senator-fold-tag-toggle | ||
| 1083 | :active (semantic-current-tag) | ||
| 1084 | :style toggle | ||
| 1085 | :selected (let ((tag (semantic-stickyfunc-tag-to-stick))) | ||
| 1086 | (and tag (semantic-tag-folded-p tag))) | ||
| 1087 | :help "Fold the current tag to one line" | ||
| 1088 | ] | ||
| 1089 | "---" | ||
| 1090 | [ "About This Tag" semantic-describe-tag t]) | ||
| 1091 | ) | ||
| 1092 | |||
| 1093 | (defun semantic-highlight-func-menu (event) | ||
| 1094 | "Popup a menu that displays things to do to the current tag. | ||
| 1095 | Argument EVENT describes the event that caused this function to be called." | ||
| 1096 | (interactive "e") | ||
| 1097 | (let* ((startwin (selected-window)) | ||
| 1098 | (win (semantic-event-window event)) | ||
| 1099 | ) | ||
| 1100 | (select-window win t) | ||
| 1101 | (save-excursion | ||
| 1102 | ;(goto-char (window-start win)) | ||
| 1103 | (mouse-set-point event) | ||
| 1104 | (sit-for 0) | ||
| 1105 | (semantic-popup-menu semantic-highlight-func-popup-menu) | ||
| 1106 | ) | ||
| 1107 | (select-window startwin))) | ||
| 1108 | |||
| 1109 | (defvar semantic-highlight-func-mode nil | ||
| 1110 | "Non-nil if highlight-func minor mode is enabled. | ||
| 1111 | Use the command `semantic-highlight-func-mode' to change this variable.") | ||
| 1112 | (make-variable-buffer-local 'semantic-highlight-func-mode) | ||
| 1113 | |||
| 1114 | (defvar semantic-highlight-func-ct-overlay nil | ||
| 1115 | "Overlay used to highlight the tag the cursor is in.") | ||
| 1116 | (make-variable-buffer-local 'semantic-highlight-func-ct-overlay) | ||
| 1117 | |||
| 1118 | (defface semantic-highlight-func-current-tag-face | ||
| 1119 | '((((class color) (background dark)) | ||
| 1120 | ;; Put this back to something closer to black later. | ||
| 1121 | (:background "gray20")) | ||
| 1122 | (((class color) (background light)) | ||
| 1123 | (:background "gray90"))) | ||
| 1124 | "Face used to show the top of current function." | ||
| 1125 | :group 'semantic-faces) | ||
| 1126 | |||
| 1127 | |||
| 1128 | (defun semantic-highlight-func-mode-setup () | ||
| 1129 | "Setup option `semantic-highlight-func-mode'. | ||
| 1130 | For semantic enabled buffers, highlight the first line of the | ||
| 1131 | current tag declaration." | ||
| 1132 | (if semantic-highlight-func-mode | ||
| 1133 | (progn | ||
| 1134 | (unless (and (featurep 'semantic) (semantic-active-p)) | ||
| 1135 | ;; Disable minor mode if semantic stuff not available | ||
| 1136 | (setq semantic-highlight-func-mode nil) | ||
| 1137 | (error "Buffer %s was not set up for parsing" (buffer-name))) | ||
| 1138 | ;; Setup our hook | ||
| 1139 | (add-hook 'post-command-hook 'semantic-highlight-func-highlight-current-tag nil t) | ||
| 1140 | ) | ||
| 1141 | ;; Disable highlight func mode | ||
| 1142 | (remove-hook 'post-command-hook 'semantic-highlight-func-highlight-current-tag t) | ||
| 1143 | (semantic-highlight-func-highlight-current-tag t) | ||
| 1144 | ) | ||
| 1145 | semantic-highlight-func-mode) | ||
| 1146 | |||
| 1147 | (defun semantic-highlight-func-mode (&optional arg) | ||
| 1148 | "Minor mode to highlight the first line of the current tag. | ||
| 1149 | Enables/disables making the header line of functions sticky. | ||
| 1150 | A function (or other tag class specified by | ||
| 1151 | `semantic-stickfunc-sticky-classes') is highlighted, meaning the | ||
| 1152 | first line which describes the rest of the construct. | ||
| 1153 | |||
| 1154 | See `semantic-stickfunc-mode' for putting a function in the | ||
| 1155 | header line. This mode recycles the stickyfunc configuration | ||
| 1156 | classes list. | ||
| 1157 | |||
| 1158 | With prefix argument ARG, turn on if positive, otherwise off. The | ||
| 1159 | minor mode can be turned on only if semantic feature is available and | ||
| 1160 | the current buffer was set up for parsing. Return non-nil if the | ||
| 1161 | minor mode is enabled." | ||
| 1162 | (interactive | ||
| 1163 | (list (or current-prefix-arg | ||
| 1164 | (if semantic-highlight-func-mode 0 1)))) | ||
| 1165 | (setq semantic-highlight-func-mode | ||
| 1166 | (if arg | ||
| 1167 | (> | ||
| 1168 | (prefix-numeric-value arg) | ||
| 1169 | 0) | ||
| 1170 | (not semantic-highlight-func-mode))) | ||
| 1171 | (semantic-highlight-func-mode-setup) | ||
| 1172 | (run-hooks 'semantic-highlight-func-mode-hook) | ||
| 1173 | (if (interactive-p) | ||
| 1174 | (message "Highlight-Func minor mode %sabled" | ||
| 1175 | (if semantic-highlight-func-mode "en" "dis"))) | ||
| 1176 | semantic-highlight-func-mode) | ||
| 1177 | |||
| 1178 | (defun semantic-highlight-func-highlight-current-tag (&optional disable) | ||
| 1179 | "Highlight the current tag under point. | ||
| 1180 | Optional argument DISABLE will turn off any active highlight. | ||
| 1181 | If the current tag for this buffer is different from the last time this | ||
| 1182 | function was called, move the overlay." | ||
| 1183 | (when (and (not (minibufferp)) | ||
| 1184 | (or (not semantic-highlight-func-ct-overlay) | ||
| 1185 | (eq (semantic-overlay-buffer | ||
| 1186 | semantic-highlight-func-ct-overlay) | ||
| 1187 | (current-buffer)))) | ||
| 1188 | (let* ((tag (semantic-stickyfunc-tag-to-stick)) | ||
| 1189 | (ol semantic-highlight-func-ct-overlay)) | ||
| 1190 | (when (not ol) | ||
| 1191 | ;; No overlay in this buffer. Make one. | ||
| 1192 | (setq ol (semantic-make-overlay (point-min) (point-min) | ||
| 1193 | (current-buffer) t nil)) | ||
| 1194 | (semantic-overlay-put ol 'highlight-func t) | ||
| 1195 | (semantic-overlay-put ol 'face 'semantic-highlight-func-current-tag-face) | ||
| 1196 | (semantic-overlay-put ol 'keymap semantic-highlight-func-mode-map) | ||
| 1197 | (semantic-overlay-put ol 'help-echo | ||
| 1198 | "Current Function : mouse-3 - Context menu") | ||
| 1199 | (setq semantic-highlight-func-ct-overlay ol) | ||
| 1200 | ) | ||
| 1201 | |||
| 1202 | ;; TAG is nil if there was nothing of the apropriate type there. | ||
| 1203 | (if (or (not tag) disable) | ||
| 1204 | ;; No tag, make the overlay go away. | ||
| 1205 | (progn | ||
| 1206 | (semantic-overlay-put ol 'tag nil) | ||
| 1207 | (semantic-overlay-move ol (point-min) (point-min) (current-buffer)) | ||
| 1208 | ) | ||
| 1209 | |||
| 1210 | ;; We have a tag, if it is the same, do nothing. | ||
| 1211 | (unless (eq (semantic-overlay-get ol 'tag) tag) | ||
| 1212 | (save-excursion | ||
| 1213 | (goto-char (semantic-tag-start tag)) | ||
| 1214 | (search-forward (semantic-tag-name tag) nil t) | ||
| 1215 | (semantic-overlay-put ol 'tag tag) | ||
| 1216 | (semantic-overlay-move ol (point-at-bol) (point-at-eol)) | ||
| 1217 | ) | ||
| 1218 | ) | ||
| 1219 | ))) | ||
| 1220 | nil) | ||
| 1221 | |||
| 1222 | (semantic-add-minor-mode 'semantic-highlight-func-mode | ||
| 1223 | "" ;; Don't need indicator. It's quite visible | ||
| 1224 | nil) | ||
| 1225 | |||
| 1226 | (provide 'semantic/util-modes) | ||
| 1227 | |||
| 1228 | ;;; semantic-util-modes.el ends here | ||