[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/swift-mode 7d9aabb 426/496: Improve support for Imenu
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/swift-mode 7d9aabb 426/496: Improve support for Imenu |
Date: |
Sun, 29 Aug 2021 11:34:21 -0400 (EDT) |
branch: elpa/swift-mode
commit 7d9aabbc151b7b8e5abaa0025720eceb2c12394e
Author: taku0 <mxxouy6x3m_github@tatapa.org>
Commit: taku0 <mxxouy6x3m_github@tatapa.org>
Improve support for Imenu
Declarations are parsed with a custom parser rather than regexp.
It is two customizable styles, `nested` or `flat`.
Speedbar is also supported.
---
swift-mode-imenu.el | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++++
swift-mode.el | 32 ++---
2 files changed, 345 insertions(+), 24 deletions(-)
diff --git a/swift-mode-imenu.el b/swift-mode-imenu.el
new file mode 100644
index 0000000..ea72901
--- /dev/null
+++ b/swift-mode-imenu.el
@@ -0,0 +1,337 @@
+;;; swift-mode-imenu.el --- Major-mode for Apple's Swift programming language,
, Imenu -*- lexical-binding: t -*-
+
+;; Copyright (C) 2019 taku0
+
+;; Authors: taku0 (http://github.com/taku0)
+;;
+;; Version: 7.1.0
+;; Package-Requires: ((emacs "24.4") (seq "2.3"))
+;; Keywords: languages swift
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; List declarations for Imenu
+
+;;; Code:
+
+(require 'swift-mode-lexer)
+
+;;;###autoload
+(defgroup swift-mode:imenu nil
+ "Imenu."
+ :group 'swift)
+
+;;;###autoload
+(defcustom swift-mode:imenu-style
+ 'nested
+ "Style of Imenu hierarchy.
+
+Values:
+
+- `nested': Class and its members are organized as trees.
+- `flat': Organized into a flat list of fully qualified names."
+ :type '(choice (const :tag "Nested" nested)
+ (const :tag "Flat" flat))
+ :group 'swift-mode:imenu
+ :safe 'symbolp)
+
+(defun swift-mode:declaration (type name-token children)
+ "Construct and return a declaration.
+
+TYPE is the type of the declaration such as `class' or `struct'.
+NAME-TOKEN is the name token of the declaration. For declarations like `init',
+it is the keyword token itself.
+CHILDREN is the child declarations if exists."
+ (list type name-token children))
+
+(defun swift-mode:declaration:type (declaration)
+ "Return the type of DECLARATION."
+ (nth 0 declaration))
+
+(defun swift-mode:declaration:name-token (declaration)
+ "Return the name token of DECLARATION."
+ (nth 1 declaration))
+
+(defun swift-mode:declaration:children (declaration)
+ "Return the children of DECLARATION."
+ (nth 2 declaration))
+
+
+(defun swift-mode:imenu-create-index (&optional style)
+ "Create an index alist of the current buffer for Imenu.
+
+STYLE is either `nested' or `flat', defaults to `nested'.
+If it is `nested', class and its members are organized as trees.
+If it is `flat', declarations are organized into a flat list of fully qualified
+names."
+ (unless style (setq style swift-mode:imenu-style))
+ (save-excursion
+ (goto-char (point-min))
+
+ (let ((declarations '())
+ (customization-item (list
+ "*Customize*"
+ 0
+ (lambda (_name _position)
+ (customize-group 'swift-mode:imenu)))))
+ (while (not (eq (swift-mode:token:type
+ (save-excursion (swift-mode:forward-token)))
+ 'outside-of-buffer))
+ (setq declarations
+ (append (swift-mode:scan-declarations) declarations)))
+ (append (if (eq style 'flat)
+ (swift-mode:format-for-imenu:flat (nreverse declarations))
+ (swift-mode:format-for-imenu:nested (nreverse declarations)))
+ (list customization-item)))))
+
+(defun swift-mode:scan-declarations ()
+ "Scan declarations from current point.
+
+Return found declarations in reverse order."
+ (let (next-token
+ next-type
+ next-text
+ name-token
+ last-class-token
+ (done nil)
+ (declarations '()))
+ (while (not done)
+ (setq next-token
+ (swift-mode:forward-token-or-list-except-curly-bracket))
+ (setq next-type (swift-mode:token:type next-token))
+ (setq next-text (swift-mode:token:text next-token))
+
+ (cond
+ ((equal next-text "class")
+ ;; "class" token may be either a class declaration keyword or a
+ ;; modifier:
+ ;;
+ ;; // Nested class named "final"
+ ;; class Foo { class final {} }
+ ;;
+ ;; // Non-overridable class method named "foo"
+ ;; class Foo { class final func foo() {} }
+ ;;
+ ;; So delays until "{" token.
+ (setq last-class-token next-token))
+
+ ((memq next-type '(\; implicit-\; { } outside-of-buffer))
+ (when (memq next-type '(} outside-of-buffer))
+ (setq done t))
+ (cond
+ ;; Having pending "class" token
+ (last-class-token
+ (save-excursion
+ (goto-char (swift-mode:token:end last-class-token))
+ (setq name-token (swift-mode:forward-token)))
+ (setq last-class-token nil)
+ (when (eq (swift-mode:token:type name-token) 'identifier)
+ (push
+ (swift-mode:declaration
+ 'class
+ name-token
+ (when (eq (swift-mode:token:type next-token) '{)
+ (nreverse (swift-mode:scan-declarations))))
+ declarations)))
+
+ ;; Closure or other unknown block
+ ((eq next-type '{)
+ (goto-char (swift-mode:token:start next-token))
+ (swift-mode:forward-token-or-list))
+
+ ;; Ignores the token otherwise.
+ ))
+
+ ((member next-text '("struct" "protocol" "extension" "enum"))
+ (setq last-class-token nil)
+ (let ((declaration
+ (swift-mode:scan-declarations:handle-struct-like next-token)))
+ (when declaration
+ (push declaration declarations))))
+
+ ((equal next-text "case")
+ (setq last-class-token nil)
+ (let ((case-declarations
+ (swift-mode:scan-declarations:handle-case-or-variable 'case)))
+ (setq declarations (append case-declarations declarations))))
+
+ ((member next-text '("typealias" "associatedtype"))
+ (setq last-class-token nil)
+ (setq name-token
+ (swift-mode:forward-token-or-list-except-curly-bracket))
+ (when (eq (swift-mode:token:type name-token) 'identifier)
+ (push
+ (swift-mode:declaration (intern next-text) name-token nil)
+ declarations)))
+
+ ((equal next-text "func")
+ (setq last-class-token nil)
+ (setq name-token
+ (swift-mode:forward-token-or-list-except-curly-bracket))
+ ;; TODO parse parameter names?
+ (when (memq (swift-mode:token:type name-token) '(identifier operator))
+ (push
+ (swift-mode:declaration 'func name-token nil)
+ declarations)))
+
+ ((member next-text '("init" "deinit" "subscript"))
+ (setq last-class-token nil)
+ (push
+ (swift-mode:declaration (intern next-text) next-token nil)
+ declarations))
+
+ ((member next-text '("let" "var"))
+ (setq last-class-token nil)
+ (let ((variable-declarations
+ (swift-mode:scan-declarations:handle-case-or-variable
+ (intern next-text))))
+ (setq declarations (append variable-declarations declarations))))
+
+ ((member next-text '("prefix" "postfix" "infix"))
+ (setq last-class-token nil)
+ (setq next-token
+ (swift-mode:forward-token-or-list-except-curly-bracket))
+ (when (equal (swift-mode:token:text next-token) "operator")
+ (setq name-token
+ (swift-mode:forward-token-or-list-except-curly-bracket))
+ (when (eq (swift-mode:token:type name-token) 'operator)
+ (push
+ (swift-mode:declaration 'operator name-token nil)
+ declarations))))
+
+ ((equal next-text "precedencegroup")
+ (setq last-class-token nil)
+ (setq name-token
+ (swift-mode:forward-token-or-list-except-curly-bracket))
+ (when (eq (swift-mode:token:type name-token) 'identifier)
+ (push
+ (swift-mode:declaration 'precedencegroup name-token nil)
+ declarations)))))
+ declarations))
+
+(defun swift-mode:forward-token-or-list-except-curly-bracket ()
+ "Move point to the end position of the next token or list.
+
+Curly brackets are not regarded as a list.
+Return the token skipped."
+ (let ((next-token (swift-mode:forward-token)))
+ (if (or (memq (swift-mode:token:type next-token) '(\( \[))
+ (equal (swift-mode:token:text next-token) "<"))
+ (progn
+ (goto-char (swift-mode:token:start next-token))
+ (swift-mode:forward-token-or-list))
+ next-token)))
+
+(defun swift-mode:scan-declarations:handle-struct-like (keyword-token)
+ "Parse struct-like declaration.
+
+Return a declaration if it have a name. Return nil otherwise.
+KEYWORD-TOKEN is the keyword beginning the declaration like \"struct\" or
+\"enum\"."
+ (let (next-token
+ (name-token (swift-mode:forward-token)))
+ (when (eq (swift-mode:token:type name-token) 'identifier)
+ (while (progn
+ (setq next-token
+ (swift-mode:forward-token-or-list-except-curly-bracket))
+ (not (memq (swift-mode:token:type next-token)
+ '(\; implicit-\; { } outside-of-buffer)))))
+ (swift-mode:declaration
+ (intern (swift-mode:token:text keyword-token))
+ name-token
+ (when (eq (swift-mode:token:type next-token) '{)
+ (nreverse (swift-mode:scan-declarations)))))))
+
+(defun swift-mode:scan-declarations:handle-case-or-variable (type)
+ "Parse enum-case, let, or var.
+
+Return a list of declarations.
+TYPE is one of `case', `let', or `var'."
+ ;; case A, B(String), C
+ ;; case A, B = 2, C
+ ;;
+ ;; let x = 1,
+ ;; y = 2,
+ ;; z = 3
+ ;;
+ ;; var x {
+ ;; get {
+ ;; return 1
+ ;; }
+ ;; }
+ ;;
+ ;; var x {
+ ;; willSet {
+ ;; }
+ ;; }
+ ;;
+ ;; let (x, y) = (1, 2) // not supported yet
+ (let (next-token
+ (items '()))
+ (while
+ (progn
+ (setq next-token (swift-mode:forward-token-or-list))
+ (when (eq (swift-mode:token:type next-token) 'identifier)
+ (push (swift-mode:declaration type next-token nil) items))
+ (while
+ (progn
+ (setq next-token (swift-mode:forward-token-or-list))
+ (not (memq (swift-mode:token:type next-token)
+ '(\, \; implicit-\; } outside-of-buffer)))))
+ (eq (swift-mode:token:type next-token) '\,)))
+ (when (eq (swift-mode:token:type next-token) '})
+ (goto-char (swift-mode:token:start next-token)))
+ items))
+
+(defun swift-mode:format-for-imenu:flat (declarations)
+ "Convert list of DECLARATIONS to alist for `imenu--index-alist'.
+
+Declarations are organized as trees."
+ (mapcan
+ (lambda (declaration)
+ (let* ((name-token (swift-mode:declaration:name-token declaration))
+ (name (swift-mode:token:text name-token))
+ (position (swift-mode:token:start name-token))
+ (children (swift-mode:declaration:children declaration)))
+ (cons
+ (cons name position)
+ (mapcar
+ (lambda (pair)
+ (cons (concat name "." (car pair)) (cdr pair)))
+ (swift-mode:format-for-imenu:flat children)))))
+ declarations))
+
+(defun swift-mode:format-for-imenu:nested (declarations)
+ "Convert list of DECLARATIONS to alist for `imenu--index-alist'.
+
+Declarations are organized as a flat list of fully qualified names."
+ (mapcar
+ (lambda (declaration)
+ (let* ((name-token (swift-mode:declaration:name-token declaration))
+ (name (swift-mode:token:text name-token))
+ (position (swift-mode:token:start name-token))
+ (children (swift-mode:declaration:children declaration)))
+ (if children
+ (cons name (cons (cons "self" position)
+ (swift-mode:format-for-imenu:nested children)))
+ (cons name position))))
+ declarations))
+
+(provide 'swift-mode-imenu)
+
+;;; swift-mode-imenu.el ends here
diff --git a/swift-mode.el b/swift-mode.el
index 0ad68d0..e7d2f6b 100644
--- a/swift-mode.el
+++ b/swift-mode.el
@@ -38,6 +38,7 @@
(require 'swift-mode-font-lock)
(require 'swift-mode-beginning-of-defun)
(require 'swift-mode-repl)
+(require 'swift-mode-imenu)
;;;###autoload
(defgroup swift nil
@@ -134,29 +135,6 @@ Signal `scan-error' if it hits opening parentheses."
(swift-mode:token:end token))))
token))
-
-;; Imenu
-
-(defun swift-mode:mk-regex-for-def (keyword)
- "Make a regex matching the identifier introduced by KEYWORD."
- (concat "\\<" (regexp-quote keyword) "\\>"
- "\\s *"
- "\\("
- "\\(?:" "\\sw" "\\|" "\\s_" "\\)" "+"
- "\\)"))
-
-(defconst swift-mode:imenu-generic-expression
- (list
- (list "Functions" (swift-mode:mk-regex-for-def "func") 1)
- (list "Classes" (swift-mode:mk-regex-for-def "class") 1)
- (list "Enums" (swift-mode:mk-regex-for-def "enum") 1)
- (list "Protocols" (swift-mode:mk-regex-for-def "protocol") 1)
- (list "Structs" (swift-mode:mk-regex-for-def "struct") 1)
- (list "Extensions" (swift-mode:mk-regex-for-def "extension") 1)
- (list "Constants" (swift-mode:mk-regex-for-def "let") 1)
- (list "Variables" (swift-mode:mk-regex-for-def "var") 1))
- "Value for `imenu-generic-expression' in `swift-mode'.")
-
;;;###autoload
(define-derived-mode swift-mode prog-mode "Swift"
"Major mode for editing Swift code.
@@ -202,7 +180,7 @@ Signal `scan-error' if it hits opening parentheses."
(add-hook 'post-self-insert-hook #'swift-mode:post-self-insert nil t)
- (setq-local imenu-generic-expression swift-mode:imenu-generic-expression)
+ (setq-local imenu-create-index-function #'swift-mode:imenu-create-index)
(setq-local beginning-of-defun-function #'swift-mode:beginning-of-defun)
(setq-local end-of-defun-function #'swift-mode:end-of-defun)
@@ -219,6 +197,12 @@ Signal `scan-error' if it hits opening parentheses."
;;;###autoload (add-to-list 'auto-mode-alist '("\\.swift\\'" . swift-mode))
+;;;###autoload (if (fboundp 'speedbar-add-supported-extension)
+;;;###autoload (speedbar-add-supported-extension ".swift")
+;;;###autoload (add-hook 'speedbar-load-hook
+;;;###autoload (lambda ()
+;;;###autoload (speedbar-add-supported-extension ".swift"))))
+
(provide 'swift-mode)
;;; swift-mode.el ends here
- [nongnu] elpa/swift-mode 8f1a697 483/496: Fix documentations, (continued)
- [nongnu] elpa/swift-mode 8f1a697 483/496: Fix documentations, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode cb6186e 474/496: Add support for font-lock-negation-char-face, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode b6d0351 338/496: Fix indentation of switch, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 07939df 413/496: Bump version to 7.0.0, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 55ce4e5 416/496: Fix command name, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode fa4f491 433/496: Add Emacs 25.3 and 26.1 to CI, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 3b6bdad 443/496: Bump version to 8.0.0, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode be8d770 446/496: Fix indentation after `class' modifier, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 3d1cfcc 047/496: Clarify manual installation, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 5448098 366/496: Improve functions related to defuns, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 7d9aabb 426/496: Improve support for Imenu,
ELPA Syncer <=
- [nongnu] elpa/swift-mode b4e0622 444/496: Add test for indent of import declarations, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode d266fbd 462/496: Update standard types, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 3bbc38e 469/496: Update copyright notices, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode f88e1ac 491/496: Support actor, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode f614620 493/496: Add tests for effectful read-only properties, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 471d158 490/496: Highlight actor, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode 59dcd60 358/496: Fix errors in 24.4, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode c862d2c 438/496: Update test, ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode aa49efa 332/496: Fix indentation of switch., ELPA Syncer, 2021/08/29
- [nongnu] elpa/swift-mode b9ad000 409/496: Make customs autoloaded, ELPA Syncer, 2021/08/29