[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/scala-mode c5ab657 160/217: Merge pull request #82 from Iv
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/scala-mode c5ab657 160/217: Merge pull request #82 from IvanMalison/support_imenu |
Date: |
Sun, 29 Aug 2021 11:31:04 -0400 (EDT) |
branch: elpa/scala-mode
commit c5ab65732c000baf08b50da96a839e7aa6620084
Merge: 34888c0 d4885ac
Author: Heikki Vesalainen <heikki.vesalainen@iki.fi>
Commit: Heikki Vesalainen <heikki.vesalainen@iki.fi>
Merge pull request #82 from IvanMalison/support_imenu
Support imenu
---
README.md | 52 +++++++++++++++++++--
scala-mode2-imenu.el | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++
scala-mode2-syntax.el | 91 ++++++++++++++++++++++++++++++++++++
scala-mode2.el | 13 ++++--
4 files changed, 275 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index df0ec19..3a90d00 100644
--- a/README.md
+++ b/README.md
@@ -428,6 +428,37 @@ Very complex scala files may need the following in your
emacs init (.emacs, etc)
(setq max-specpdl-size 5000)
```
+## `beginning-of-defun` and `end-of-defun`
+
+scala-mode2 defines `scala-syntax:beginning-of-definition` and
+`scala-syntax:end-of-definition` which move the cursor forward and
+backward over class, trait, object, def, val, var, and type definitions. These
+functions are assigned to the buffer local variables
+`beginning-of-defun-function` and `end-of-defun-function` which makes
+it so that the `beginning-of-defun` and `end-of-defun` functions behave
+in a way that is appropriate to scala. These functions are not currently able
to
+support some of the more advanced scala definition types.
+Multiple assignment to variables e.g.
+
+```scala
+val a, b = (1, 2)
+```
+
+are among the assignment types that are not currently supported. In general
+the only types of definition that these functions are likely to support
+are ones that use only a single, simple (but possibly generic) identifier as
+its identifer.
+
+## imenu
+
+scala-mode2 supports imenu, a library for accessing locations in documents that
+is included in emacs 24. The custom variable `scala-imenu:should-flatten-index`
+controls whether or not the imenu index will be hierarchical or completely
flat.
+The current iMenu implementation only goes one level deep i.e. nested classes
are
+not traversed. scala-mode2's imenu support depends heavily on the
+`scala-syntax:end-of-definition` and `scala-syntax:beginning-of-definition`
+functions, and as such, it shares their limitations.
+
## Other features
- highlights only properly formatted string and character constants
- indenting a code line removes trailing whitespace
@@ -447,16 +478,30 @@ The indenter thinks the second occurrence of `foo` is the
body of the while.
To work around this, terminate the while with a semicolon,
or put a blank line after it.
+As mentioned above,
+`scala-syntax:end-of-definition` `scala-syntax:beginning-of-definition`
+don't properly handle any defintions that don't have a simple, single
+identifier.
+Its likely that they will stumble when presented with some of the more advanced
+and obscure scala definitions out there.
+
+There also seems to be a strange bug with scala-modes2's `end-of-defun`
integration
+where two functions are skipped instead of just one.
`scala-syntax:end-of-definition`
+does not have this problem, so if you find this bug bothering you a lot
+you can bind whatever you normally bind to `end-of-defun` to
+`scala-syntax:end-of-definition` in scala mode to alleviate the issue.
+
## Future work
- syntax-begin-function for reliably fontifying elements which span
multiple lines
-- beginning-of-defun, end-of-defun
-- movement commands to move to previous or next definition (val,
- var, def, class, trait, object)
- highlight headings and annotations inside Scaladoc specially (use
underline for headings)
- highlight variables in string interpolation (scala 2.10)
+- Improve `end-of-defun` and `beginning-of-defun`. In particular,
+ figure out why `end-of-defun` sometimes skips defintions
+ even though `scala-syntax:end-of-definition` does not and add
+ support for obscure types of val declarations.
All suggestions and especially pull requests are welcomed in github
https://github.com/hvesalai/scala-mode2
@@ -477,3 +522,4 @@ Contributors and valuable feedback:
- Nic Ferrier
- Tillmann Rendel
- Jim Powers
+- Ivan Malison
diff --git a/scala-mode2-imenu.el b/scala-mode2-imenu.el
new file mode 100644
index 0000000..5eec53a
--- /dev/null
+++ b/scala-mode2-imenu.el
@@ -0,0 +1,126 @@
+;;; 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-mode2-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.")
+(defcustom scala-imenu:build-imenu-candidate
+ 'scala-imenu:default-build-imenu-candidate
+ "Controls whether or not the imenu index has definition type information.")
+(defcustom scala-imenu:cleanup-hooks nil
+ "Functions that will be run after the construction of each imenu")
+
+(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)
+ (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-mode2-imenu)
+;;; scala-mode2-imenu.el ends here
diff --git a/scala-mode2-syntax.el b/scala-mode2-syntax.el
index 1eeb109..80cc8cc 100644
--- a/scala-mode2-syntax.el
+++ b/scala-mode2-syntax.el
@@ -899,4 +899,95 @@ not. A list must be either enclosed in parentheses or
start with
(when (looking-at scala-syntax:list-keywords-re)
(goto-char (match-end 0))))))))
+;; Functions to help with finding the beginning and end of scala definitions.
+
+(defconst scala-syntax:modifiers-re
+ (regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy"
+ "private" "protected" "case") 'words))
+
+(defconst scala-syntax:whitespace-delimeted-modifiers-re
+ (concat "\\(?:" scala-syntax:modifiers-re "\\(?: *\\)" "\\)*"))
+
+(defconst scala-syntax:definition-words-re
+ (mapconcat 'regexp-quote '("class" "object" "trait" "val" "var" "def"
"type") "\\|"))
+
+(defun scala-syntax:build-definition-re (words-re)
+ (concat " *"
+ scala-syntax:whitespace-delimeted-modifiers-re
+ words-re
+ "\\(?: *\\)"
+ "\\(?2:"
+ scala-syntax:id-re
+ "\\)"))
+
+(defconst scala-syntax:all-definition-re
+ (scala-syntax:build-definition-re
+ (concat "\\(?1:" scala-syntax:definition-words-re "\\)")))
+
+;; Functions to help with beginning and end of definitions.
+
+(defun scala-syntax:backward-sexp-forcing ()
+ (condition-case ex (backward-sexp) ('error (backward-char))))
+
+(defun scala-syntax:forward-sexp-or-next-line ()
+ (interactive)
+ (cond ((looking-at "\n") (next-line) (beginning-of-line))
+ (t (forward-sexp))))
+
+(defun scala-syntax:beginning-of-definition ()
+ "This function may not work properly with certain types of scala definitions.
+For example, no care has been taken to support multiple assignments to vals
such as
+
+val a, b = (1, 2)
+"
+ (interactive)
+ (let ((found-position
+ (save-excursion
+ (scala-syntax:backward-sexp-forcing)
+ (scala-syntax:movement-function-until-re
scala-syntax:all-definition-re
+
'scala-syntax:backward-sexp-forcing))))
+ (when found-position (progn (goto-char found-position)
(back-to-indentation)))))
+
+(defun scala-syntax:end-of-definition ()
+ "This function may not work properly with certain types of scala definitions.
+For example, no care has been taken to support multiple assignments to vals
such as
+
+val a, b = (1, 2)
+"
+ (interactive)
+ (re-search-forward scala-syntax:all-definition-re)
+ (scala-syntax:find-brace-equals-or-next)
+ (scala-syntax:handle-brace-equals-or-next))
+
+(defun scala-syntax:find-brace-equals-or-next ()
+ (scala-syntax:go-to-pos
+ (save-excursion
+ (scala-syntax:movement-function-until-cond-function
+ (lambda () (or (looking-at "[[:space:]]*[{=]")
+ (looking-at scala-syntax:all-definition-re)))
+ (lambda () (condition-case ex (scala-syntax:forward-sexp-or-next-line)
('error nil)))))))
+
+(defun scala-syntax:handle-brace-equals-or-next ()
+ (cond ((looking-at "[[:space:]]*{") (forward-sexp))
+ ((looking-at "[[:space:]]*=") (scala-syntax:forward-sexp-or-next-line)
+ (scala-syntax:handle-brace-equals-or-next))
+ ((looking-at scala-syntax:all-definition-re) nil)
+ (t (scala-syntax:forward-sexp-or-next-line)
+ (scala-syntax:handle-brace-equals-or-next))))
+
+(defun scala-syntax:movement-function-until-re (re movement-function)
+ (save-excursion
+ (scala-syntax:movement-function-until-cond-function
+ (lambda () (looking-at re)) movement-function)))
+
+(defun scala-syntax:movement-function-until-cond-function (cond-function
movement-function)
+ (let ((last-point (point)))
+ (if (not (funcall cond-function))
+ (progn (funcall movement-function)
+ (if (equal last-point (point)) nil
+ (scala-syntax:movement-function-until-cond-function
+ cond-function movement-function))) last-point)))
+
+(defun scala-syntax:go-to-pos (pos) (when pos (goto-char pos)))
+
(provide 'scala-mode2-syntax)
diff --git a/scala-mode2.el b/scala-mode2.el
index efe2a68..44ab399 100644
--- a/scala-mode2.el
+++ b/scala-mode2.el
@@ -12,6 +12,7 @@
(require 'scala-mode2-fontlock)
(require 'scala-mode2-map)
(require 'scala-mode2-sbt)
+(require 'scala-mode2-imenu)
;; Tested only for emacs 24
(unless (<= 24 emacs-major-version)
@@ -108,7 +109,10 @@ When started, runs `scala-mode-hook'.
'indent-line-function
'fixup-whitespace
'delete-indentation
- 'indent-tabs-mode)
+ 'indent-tabs-mode
+ 'imenu-create-index-function
+ 'beginning-of-defun-function
+ 'end-of-defun-function)
(add-hook 'syntax-propertize-extend-region-functions
'scala-syntax:propertize-extend-region)
@@ -142,12 +146,13 @@ When started, runs `scala-mode-hook'.
fixup-whitespace 'scala-indent:fixup-whitespace
delete-indentation 'scala-indent:join-line
indent-tabs-mode nil
- )
+ beginning-of-defun-function #'scala-syntax:beginning-of-definition
+ end-of-defun-function #'scala-syntax:end-of-definition
+ imenu-create-index-function #'scala-imenu:create-imenu-index)
(use-local-map scala-mode-map)
;; add indent functionality to some characters
(scala-mode-map:add-remove-indent-hook)
- (scala-mode-map:add-self-insert-hooks)
-)
+ (scala-mode-map:add-self-insert-hooks))
;; Attach .scala files to the scala-mode
;;;###autoload
- [nongnu] elpa/scala-mode 11a710d 044/217: Correct annotation highlighting., (continued)
- [nongnu] elpa/scala-mode 11a710d 044/217: Correct annotation highlighting., ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 49bd528 045/217: working on README, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode d357a77 038/217: New 'scala-indent:align-parameters' customization variable, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode af0e387 053/217: Fixes #18: highlight escapes in strings after chars, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode bc446e7 120/217: sbt support is now in sbt-mode, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 597d421 145/217: Seems the imenu var needs to be set in a very local way., ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode c154f16 139/217: better join-line and fixup-whitespace, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode e277378 143/217: WIP on end-of-defun and beggining of defun which should make writing imenu functionality much easier., ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 811494f 155/217: Support nesting of classes in imenu index creation function., ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode d4885ac 159/217: Use [[:space:]] instead of literal space in syntax end/beggining of, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode c5ab657 160/217: Merge pull request #82 from IvanMalison/support_imenu,
ELPA Syncer <=
- [nongnu] elpa/scala-mode b8e835f 161/217: scala-mode:goto-start-of-code close #90, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode e8c8a51 169/217: Couple imenu-related issues (#95), ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 3989897 174/217: bump version (#111), ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 360b5ba 162/217: Merge pull request #92 from fommil/master, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 387e93c 178/217: revert 618f0fbef4d3d94156f834a66e36c79f07a54e02, fixes #118 (#119), ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode c71efcc 188/217: quoteids are now propertized as symbols (#137), ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 1d08e88 201/217: Merge pull request #154 from pgronkievitz/patch-1, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 265dadc 214/217: fix: `//`-comments filled incorrectly (`//` not acting as prefix), ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode b85fa5e 213/217: Merge pull request #167 from Kazark/trailing-comma, ELPA Syncer, 2021/08/29
- [nongnu] elpa/scala-mode 6966328 216/217: Merge pull request #168 from Kazark/slash-slash, ELPA Syncer, 2021/08/29