| | 317 | |
| | 318 | ;;; Etags |
| | 319 | ;; Struct phpcmp-tag |
| | 320 | ;; tag-file: TAGSファイルのパス |
| | 321 | |
| | 322 | ;; path: fileのfullpath |
| | 323 | |
| | 324 | ;; classes: |
| | 325 | ;; (("classname" . ("method" "method1" "method2")) |
| | 326 | ;; ("classname1" . ("method" "method1" "method2"))) |
| | 327 | ;; のような構造の association list |
| | 328 | |
| | 329 | ;; functions: 関数のリスト |
| | 330 | |
| | 331 | ;; variables: 変数のリスト |
| | 332 | (defstruct (phpcmp-tag |
| | 333 | (:constructor phpcmp-make-tag |
| | 334 | (&key tag-file path relative-path classes functions variables)) |
| | 335 | (:type list)) |
| | 336 | tag-file path relative-path classes functions variables) |
| | 337 | |
| | 338 | (defstruct (phpcmp-class |
| | 339 | (:constructor phpcmp-make-class |
| | 340 | (&key name parent methods variables)) |
| | 341 | (:type list)) |
| | 342 | name parent methods variables) |
| | 343 | |
| | 344 | ;; this function is copied from anything-etags.el |
| | 345 | (defvar phpcmp-etags-tag-file-name "TAGS") |
| | 346 | (defvar phpcmp-etags-tag-file-search-limit 10) |
| | 347 | (defun phpcmp-etags-find-tag-file () |
| | 348 | "Return tags file. |
| | 349 | If file is not found, return nil" |
| | 350 | (let ((file-exists? (lambda (dir) |
| | 351 | (let ((tag-path (concat dir phpcmp-etags-tag-file-name))) |
| | 352 | (and (stringp tag-path) |
| | 353 | (file-exists-p tag-path) |
| | 354 | (file-readable-p tag-path))))) |
| | 355 | (current-dir (phpcmp-get-current-directory))) |
| | 356 | (ignore-errors |
| | 357 | (loop with count = 0 |
| | 358 | until (funcall file-exists? current-dir) |
| | 359 | ;; Return nil if outside the value of |
| | 360 | ;; `phpcmp-etags-tag-file-search-limit'. |
| | 361 | if (= count phpcmp-etags-tag-file-search-limit) |
| | 362 | do (return nil) |
| | 363 | ;; Or search upper directories. |
| | 364 | else |
| | 365 | do (progn (incf count) |
| | 366 | (setq current-dir (expand-file-name (concat current-dir "../")))) |
| | 367 | finally return (concat current-dir phpcmp-etags-tag-file-name))))) |
| | 368 | |
| | 369 | (defun phpcmp-etags-get-tags () |
| | 370 | "Return list of struct `phpcmp-tag'" |
| | 371 | (let ((tag-file (phpcmp-etags-find-tag-file))) |
| | 372 | (when tag-file |
| | 373 | (with-temp-buffer |
| | 374 | (insert-file-contents tag-file) |
| | 375 | (phpcmp-etags-parse-tags-buffer tag-file) |
| | 376 | )))) |
| | 377 | |
| | 378 | ;; This monster regexp matches an etags tag line. |
| | 379 | ;; \1 is the string to match; |
| | 380 | ;; \2 is not interesting; |
| | 381 | ;; \3 is the guessed tag name; XXX guess should be better eg DEFUN |
| | 382 | ;; \4 is not interesting; |
| | 383 | ;; \5 is the explicitly-specified tag name. |
| | 384 | ;; \6 is the line to start searching at; |
| | 385 | ;; \7 is the char to start searching at. |
| | 386 | (defvar phpcmp-etags-parse-tags-file-regexp |
| | 387 | (rx bol |
| | 388 | (group ;1 |
| | 389 | (regexp "\\([^\177]+[^-a-zA-Z0-9_+*$:\177]+\\)?") ;2 |
| | 390 | (regexp "\\([-a-zA-Z0-9_+*$?:]+\\)") ;3 |
| | 391 | (regexp "[^-a-zA-Z0-9_+*$?:\177]*")) |
| | 392 | "\177" |
| | 393 | (regexp "\\(\\([^\n\001]+\\)\001\\)?") ;4, 5 |
| | 394 | |
| | 395 | (regexp "\\([0-9]+\\)?") ;6 |
| | 396 | "," |
| | 397 | (regexp "\\([0-9]+\\)?") ;7 |
| | 398 | "\n" |
| | 399 | )) |
| | 400 | |
| | 401 | (defun phpcmp-etags-split-each-file (s) |
| | 402 | (split-string s "\14[ \n]+")) |
| | 403 | |
| | 404 | (defun phpcmp-etags-parse-tags-buffer (tag-file) |
| | 405 | (let ((each-file-tag-strings |
| | 406 | (delete "" (split-string (buffer-string) "\14[ \n]+")))) |
| | 407 | (loop for s in each-file-tag-strings |
| | 408 | collect (phpcmp-deftag s tag-file)))) |
| | 409 | |
| | 410 | (defun phpcmp-deftag (tag-string tag-file) |
| | 411 | (with-temp-buffer |
| | 412 | (insert tag-string) |
| | 413 | (goto-char (point-min)) |
| | 414 | ;; Return struct `phpcmp-tag' |
| | 415 | (phpcmp-deftag-parse tag-file))) |
| | 416 | |
| | 417 | (defun phpcmp-deftag-parse (tag-file) |
| | 418 | (let ((relative-file-path (phpcmp-deftag-parse-file-info))) |
| | 419 | (let (path relative-path classes functions variables) |
| | 420 | (while (not (eobp)) |
| | 421 | (cond |
| | 422 | ((looking-at (rx bol (* space) "class")) |
| | 423 | (push (phpcmp-deftag-parse-class) classes)) |
| | 424 | ((looking-at phpcmp-etags-parse-tags-file-regexp) |
| | 425 | (push (match-string 5) functions) |
| | 426 | (forward-line)) |
| | 427 | (t |
| | 428 | (forward-line)))) |
| | 429 | (phpcmp-make-tag |
| | 430 | :tag-file tag-file |
| | 431 | :path (concat (file-name-directory tag-file) |
| | 432 | relative-file-path) |
| | 433 | :classes classes |
| | 434 | :functions functions |
| | 435 | :variables variables)))) |
| | 436 | |
| | 437 | (defun phpcmp-deftag-parse-file-info () |
| | 438 | (when (looking-at (rx bol (group (+ (not (any ",")))) "," (? (* digit)) "\n")) |
| | 439 | (let* ((relative-file-path (match-string 1))) |
| | 440 | (prog1 relative-file-path |
| | 441 | (forward-line))))) |
| | 442 | |
| | 443 | (defun phpcmp-deftag-parse-class () |
| | 444 | (let ((class-str (phpcmp-take-same-indent-string)) |
| | 445 | name parent methods variables) |
| | 446 | (with-temp-buffer |
| | 447 | (insert class-str) |
| | 448 | (goto-char (point-min)) |
| | 449 | (while (not (eobp)) |
| | 450 | (cond |
| | 451 | ((looking-at (rx bol (* space) "class" (? (*? not-newline) "extends" (+ space) (group (+ (any alpha "_")))))) |
| | 452 | (let ((parent-class (match-string 1))) |
| | 453 | (when parent-class |
| | 454 | (setq parent parent-class))) |
| | 455 | (when (looking-at phpcmp-etags-parse-tags-file-regexp) |
| | 456 | (let ((class-name (match-string 5))) |
| | 457 | (setq name class-name))) |
| | 458 | (forward-line)) |
| | 459 | ((looking-at (rx bol (* space) (? (or "public" "private")) (* space) "function")) |
| | 460 | (when (looking-at phpcmp-etags-parse-tags-file-regexp) |
| | 461 | (let ((function (match-string 5))) |
| | 462 | (push function methods))) |
| | 463 | (forward-line)) |
| | 464 | ((looking-at (rx bol (* space) "$" (+ (any alnum "_")) (* space) "=")) |
| | 465 | (when (looking-at phpcmp-etags-parse-tags-file-regexp) |
| | 466 | (let ((variable (match-string 5))) |
| | 467 | (push variable variables))) |
| | 468 | (forward-line)) |
| | 469 | (t |
| | 470 | (forward-line))))) |
| | 471 | (phpcmp-make-class |
| | 472 | :name name |
| | 473 | :parent parent |
| | 474 | :methods methods |
| | 475 | :variables variables))) |
| | 476 | |
| | 477 | (defun phpcmp-take-same-indent-string () |
| | 478 | "move point" |
| | 479 | (let ((cur-indent (current-indentation)) |
| | 480 | (cur-point (point))) |
| | 481 | (forward-line) |
| | 482 | (buffer-substring-no-properties |
| | 483 | cur-point |
| | 484 | (loop while (and |
| | 485 | (not |
| | 486 | (>= cur-indent |
| | 487 | (current-indentation))) |
| | 488 | (not (eobp))) |
| | 489 | do (forward-line) |
| | 490 | finally return (point))))) |