1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
;;; scala-mode-imenu.el - Major mode for editing scala
;;; Copyright (c) 2014 Heikki Vesalainen
;;; For information on the License, see the LICENSE file
;;; Code:
(require 'scala-mode-syntax)
;; Make lambdas proper clousures (only in this file)
(make-local-variable 'lexical-binding)
(setq lexical-binding t)
(defcustom scala-imenu:should-flatten-index t
"Controls whether or not the imenu index is flattened or hierarchical."
:type 'boolean
:safe #'booleanp
:group 'scala)
(defcustom scala-imenu:build-imenu-candidate
'scala-imenu:default-build-imenu-candidate
"Controls whether or not the imenu index has definition type information."
:type 'function
:group 'scala)
(defcustom scala-imenu:cleanup-hooks nil
"Functions that will be run after the construction of each imenu"
:type 'hook
:group 'scala)
(defun scala-imenu:flatten-list (incoming-list &optional predicate)
(when (not predicate) (setq predicate 'listp))
(cl-mapcan (lambda (x) (if (funcall predicate x)
(scala-imenu:flatten-list x predicate) (list x))) incoming-list))
(defun scala-imenu:flatten-imenu-index (index)
(cl-mapcan (lambda (x) (if (listp (cdr x))
(scala-imenu:flatten-imenu-index (cdr x))
(list x))) index))
(defun scala-imenu:create-imenu-index ()
(let ((imenu-index (cl-mapcar 'scala-imenu:build-imenu-candidates
(scala-imenu:create-index))))
(dolist (cleanup-hook scala-imenu:cleanup-hooks)
(funcall cleanup-hook))
(if scala-imenu:should-flatten-index
(scala-imenu:flatten-imenu-index imenu-index)
imenu-index)))
(defun scala-imenu:build-imenu-candidates (member-info &optional parents)
(if (listp (car member-info))
(let* ((current-member-info (car member-info))
(child-member-infos (cdr member-info))
(current-member-result
(scala-imenu:destructure-for-build-imenu-candidate
current-member-info parents))
(current-member-name (car current-member-result)))
(if child-member-infos
(let ((current-member-members
(scala-imenu:build-child-members
(append parents `(,current-member-info))
(cdr member-info))))
`(,current-member-name .
,(cons current-member-result current-member-members)))
current-member-result))
(scala-imenu:destructure-for-build-imenu-candidate member-info parents)))
(defun scala-imenu:build-child-members (parents child-members)
(cl-mapcar (lambda (child) (scala-imenu:build-imenu-candidates
child parents)) child-members))
(defun scala-imenu:destructure-for-build-imenu-candidate (member-info parents)
(cl-destructuring-bind (member-name definition-type marker)
member-info (funcall scala-imenu:build-imenu-candidate
member-name definition-type marker parents)))
(defun scala-imenu:default-build-imenu-candidate (member-name definition-type
marker parents)
(let* ((all-names
(append (cl-mapcar (lambda (parent) (car parent)) parents)
`(,member-name)))
(member-string (mapconcat 'identity all-names ".")))
`(,(format "(%s)%s" definition-type member-string) . ,marker)))
(defun scala-imenu:create-index ()
(let ((class nil) (index nil))
(goto-char (point-max))
(while (setq class (scala-imenu:parse-nested-from-end))
(setq index (cons class index)))
index))
(defun scala-imenu:parse-nested-from-end ()
(let ((last-point (point)) (class-name nil) (definition-type nil))
(scala-syntax:beginning-of-definition)
;; We're done if scala-syntax:beginning-of-definition has no effect.
(if (eq (point) last-point) nil
(progn (looking-at scala-syntax:all-definition-re)
(setq class-name (match-string-no-properties 2))
(setq definition-type (match-string-no-properties 1)))
`(,`(,class-name ,definition-type ,(point-marker)) .
,(scala-imenu:nested-members)))))
(defun scala-imenu:parse-nested-from-beginning ()
(scala-syntax:end-of-definition)
(scala-imenu:parse-nested-from-end))
(defun scala-imenu:nested-members ()
(let ((start-point (point)))
(save-excursion
(scala-syntax:end-of-definition)
;; This gets us inside of the class definition
;; It seems like there should be a better way
;; to do this.
(backward-char)
(reverse (scala-imenu:get-nested-members start-point)))))
(defvar scala-imenu:nested-definition-types '("class" "object" "trait"))
(defun scala-imenu:get-nested-members (parent-start-point)
(scala-syntax:beginning-of-definition)
(if (< parent-start-point (point))
(cons (scala-imenu:get-member-info-at-point)
(scala-imenu:get-nested-members parent-start-point))
nil))
(defun scala-imenu:get-member-info-at-point ()
(looking-at scala-syntax:all-definition-re)
(let* ((member-name (match-string-no-properties 2))
(definition-type (match-string-no-properties 1)))
(if (member definition-type scala-imenu:nested-definition-types)
(save-excursion (scala-imenu:parse-nested-from-beginning))
`(,member-name ,definition-type ,(point-marker)))))
(provide 'scala-mode-imenu)
;;; scala-mode-imenu.el ends here
|