emacs-elpa-diffs
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]