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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/kotlin-mode 8d5b1cada0 1/2: Making a number of improvement


From: ELPA Syncer
Subject: [nongnu] elpa/kotlin-mode 8d5b1cada0 1/2: Making a number of improvements to the indentation logic
Date: Tue, 6 Dec 2022 14:59:16 -0500 (EST)

branch: elpa/kotlin-mode
commit 8d5b1cada00aaea4824b97a4cabdd153efb174fa
Author: Alex Figl-Brick <alex@alexbrick.me>
Commit: Alex Figl-Brick <alex@alexbrick.me>

    Making a number of improvements to the indentation logic
    
    Major changes:
    
    1. `->` is no longer considered a continuation
    2. Control statements without brackets correctly indent the following line
    
    I also simplified quite a bit of the logic by using Emacs's ability to
    automatically find enclosing blocks using syntax tables.
---
 kotlin-mode.el           | 280 ++++++++++++-----------------------------------
 test/kotlin-mode-test.el |  28 +++++
 test/sample.kt           |  65 +++++------
 3 files changed, 129 insertions(+), 244 deletions(-)

diff --git a/kotlin-mode.el b/kotlin-mode.el
index f494a26c27..201b00691e 100644
--- a/kotlin-mode.el
+++ b/kotlin-mode.el
@@ -155,6 +155,7 @@
     (modify-syntax-entry ?\r "> b" st)
     st))
 
+(defconst kotlin-mode--closing-brackets '(?} ?\) ?\]))
 
 ;;; Font Lock
 
@@ -368,187 +369,13 @@
 (defun kotlin-mode--line-continuation()
   "Return whether this line continues a statement in the previous line"
   (or
-   (and (kotlin-mode--prev-line-begins "\\(if\\|else\\|for\\|while\\)[ \t]*")
+   (and (kotlin-mode--prev-line-begins "\\(if\\|for\\|while\\)[ \t]+(")
+        (kotlin-mode--prev-line-ends ")[[:space:]]*\\(\/\/.*\\|\\/\\*.*\\)?"))
+   (and (kotlin-mode--prev-line-begins "else[ \t]*")
+        (not (kotlin-mode--prev-line-begins "else [ \t]*->"))
         (not (kotlin-mode--prev-line-ends "{.*")))
    (or
-    (kotlin-mode--line-begins 
"\\([.=:]\\|->\\|\\(\\(private\\|public\\|protected\\|internal\\)[ 
\t]*\\)?[sg]et\\b\\)")
-    (save-excursion
-      (kotlin-mode--prev-line)
-      (kotlin-mode--line-ends "\\([=:]\\|->\\)")))))
-
-(defun kotlin-mode--base-indentation ()
-  "Return the indentation level of the current line based on brackets only,
-   i.e. ignoring 'continuation' indentation."
-  (cond ((kotlin-mode--line-continuation)
-         (- (current-indentation) kotlin-tab-width))
-        ((kotlin-mode--in-comment-block)
-         (- (current-indentation) 1))
-        (t
-         (current-indentation))))
-
-(defclass kotlin-mode--bracket-counter ()
-  ((count :initarg :count
-          :initform 0
-          :type integer
-          :documentation "The net bracket count (+1 for open, -1 for close).")
-   (indent :initarg :indent
-           :initform 0
-           :type integer
-           :documentation "The indentation based on bracket layout.")
-   (finished :initarg :finished
-             :initform nil
-             :type boolean
-             :documentation "Whether the counting has finished.")
-   (use-base :initarg :use-base
-             :initform t
-             :type boolean
-             :documentation "Whether to factor out extra indentations."))
-  "A class for counting brackets to find the appropriate bracket-based indent.
-   The logic here involves keeping track of the net-bracket-count,
-   defined as the number of open-brackets minus the number of close-brackets.
-   We scroll backwards until the net-bracket-count is zero, and this point
-   determines the desired indentation level for the current line.")
-
-(defun kotlin-mode--count-to-line-start (counter)
-  "Count the brackets on the current line, starting from the
-cursor position, and working backward, incrementing the count +1
-for open-brackets, -1 for close-brackets.
-
-Mark the COUNTER finished, set indentation, and return as soon as
-the overall count exceeds zero.  If the counter is zero at the
-beginning of the line, Mark the counter finished and set
-indentation.  If we hit a beginning of line but the counter is
-negative, just return without marking finished."
-  (when (nth 4 (syntax-ppss))
-    ;; If the point is inside a comment, goto the beginning of the comment.
-    (goto-char (nth 8 (syntax-ppss))))
-  (save-excursion
-    (let ((line-beginning-position (line-beginning-position)))
-      (while (and (<= (oref counter count) 0) (not (bolp)))
-        (forward-comment (- (point)))
-        (backward-char)
-        (when (< (point) line-beginning-position)
-          (goto-char line-beginning-position))
-        (cond ((eq (char-syntax (char-after)) ?\()
-               (cl-incf (oref counter count)))
-              ((eq (char-syntax (char-after)) ?\))
-               (cl-decf (oref counter count))))))
-    ;; We are at the beginning of the line, or just before an
-    ;; unmatching open bracket.
-    (cond
-     ;; If the net-bracket-count is zero, use this indentation
-     ((= (oref counter count) 0)
-      (oset counter finished t)
-      (if (oref counter use-base)
-          ;; Indenting a line that is neither close bracket nor the
-          ;; first element of a block or a list.  Found the previous
-          ;; line.  So align with the previous line, without effect of
-          ;; continued expression at the previous line.
-          (kotlin-mode--add-indent counter (kotlin-mode--base-indentation))
-        ;; Indenting close bracket or the first element of a block or
-        ;; a list.  So align with this line, optionally with extra
-        ;; indentation.
-        (kotlin-mode--add-indent counter (current-indentation))))
-     ;; If we've now counted more open-brackets than close-brackets,
-     ;; use the indentation of the content immediately following the
-     ;; final open-bracket.
-     ;;
-     ;; Example:
-     ;;
-     ;; Suppose indenting "bar2()" in the following example:
-     ;;
-     ;; foo(  bar1(),
-     ;;       bar2())
-     ;;
-     ;; We are at just before the open bracket of "foo".  So skip the
-     ;; open bracket and spaces, then align "bar2()" with "bar1()".
-     ((> (oref counter count) 0)
-      (oset counter finished t)
-      (forward-char)
-      (skip-syntax-forward "(")
-      (skip-syntax-forward "-")
-      (kotlin-mode--add-indent counter (current-column))))))
-
-(defun kotlin-mode--count-leading-close-brackets (counter)
-  "Adjust COUNTER when indenting close brackets.
-
-This function should be called at the line being indented.
-
-Example:
-Suppose indenting the closing bracket of \"bar\" in the following example:
-
-fun foo() {
-    bar {
-      baz()
-    } // Indenting here
-}
-
-This function decrements the counter, so that
-`kotlin-mode--count-to-line-start' should not stop at the line
-\"baz()\", but goto the line \"bar {\", so that the close bracket
-aligned with \"bar {\"."
-
-  (save-excursion
-    (skip-syntax-forward "-")
-    (when (looking-at "\\s)")
-      (oset counter use-base nil)
-      (kotlin-mode--subtract-count counter (skip-syntax-forward ")")))))
-
-(defun kotlin-mode--count-trailing-open-brackets (counter)
-  "Adjust COUNTER when indenting the first element of a block or list.
-
-This function should be called before calling
-`kotlin-mode--count-to-line-start', with the point at the end of
-the previous line of the line being indented.
-
-If the bracket count is at zero, and there are open-brackets at
-the end of the line, do not count them, but add a single
-indentation level. If bracket count is at zero, we are not
-indenting close brackets.
-
-Example:
-
-Suppose indenting \"baz()\" in the following example:
-
-fun foo() {
-    bar {
-        baz()
-    }
-}
-
-This function is called with the point at the end of the line
-\"bar {\". This function skips \"{\" backward and add indentation
-amount `kotlin-tab-width', say 4.  Then
-`kotlin-mode--count-to-line-start' seeks to the
-beginning-of-line.  So the final indentation is 8, that is the
-sum of indentation of bar and extra indentation.
-
-On the other hand, when indenting \"baz2()\" in the following
-line, keep cursor and indentation level as is because
-\"bar(baz1(),\" does not end with open brackets.  Then
-`kotlin-mode--count-to-line-start' stops at the close bracket of
-\"bar(\".  So \"baz2()\" is aligned with \"baz1()\".
-
-fun foo() {
-    bar(baz1(),
-        baz2())
-}"
-  (when (and (= (oref counter count) 0)
-             (not (= (skip-syntax-backward "(") 0)))
-    (kotlin-mode--add-indent counter kotlin-tab-width)
-    (oset counter use-base nil)))
-
-(defun kotlin-mode--add-count (counter val)
-  (cl-incf (oref counter count) val))
-
-(defun kotlin-mode--subtract-count (counter val)
-  (cl-decf (oref counter count) val))
-
-(defun kotlin-mode--add-indent (counter val)
-  (cl-incf (oref counter indent) val))
-
-(defun kotlin-mode--finished (counter)
-  (oref counter finished))
+    (kotlin-mode--line-begins 
"\\([.=:]\\|->\\|\\(\\(private\\|public\\|protected\\|internal\\)[ 
\t]*\\)?[sg]et\\b\\)"))))
 
 (defun kotlin-mode--in-comment-block ()
   "Return whether the cursor is within a standard comment block structure
@@ -574,46 +401,73 @@ fun foo() {
           (setq keep-going nil))))
       in-comment-block)))
 
-(defun kotlin-mode--indent-line ()
-  "Indent current line as kotlin code."
-  (interactive)
+(defun kotlin-mode--first-line-p ()
+  "Determine if point is on the first line."
   (save-excursion
     (beginning-of-line)
-    (if (bobp)
-        (kotlin-mode--beginning-of-buffer-indent)
-      (let* ((bracket-counter (make-instance 'kotlin-mode--bracket-counter))
-             ;; Find bracket-based indentation first
-             (cur-indent
-              (progn
-                (kotlin-mode--count-leading-close-brackets bracket-counter)
-                (save-excursion
-                  (kotlin-mode--prev-line)
-                  (end-of-line)
-                  (kotlin-mode--count-trailing-open-brackets bracket-counter)
-                  (kotlin-mode--count-to-line-start bracket-counter)
-                  (while (and (not (kotlin-mode--finished bracket-counter))
-                              (not (bobp)))
-                    (kotlin-mode--prev-line)
-                    (end-of-line)
-                    (kotlin-mode--count-to-line-start bracket-counter))
-                  (oref bracket-counter indent)))))
+    (bobp)
+    )
+  )
 
-        (cond
-         ((kotlin-mode--line-continuation)
-          ;; Add extra indentation if the line continues the previous one
-          (cl-incf cur-indent kotlin-tab-width))
+(defun kotlin-mode--line-closes-block-p ()
+  "Return whether or not the start of the line closes its containing block."
+  (save-excursion
+    (back-to-indentation)
+    (memq (following-char) kotlin-mode--closing-brackets)
+    ))
+
+(defun kotlin-mode--get-opening-char-indentation (parser-state-index)
+  "Determine the indentation of the line that starts the current block.
 
-         ((kotlin-mode--in-comment-block)
-          ;; Add one space of extra indentation if inside a comment block
-          (cl-incf cur-indent)))
+Caller must pass in PARSER-STATE-INDEX, which refers to the index
+of the list returned by `syntax-ppss'.
 
-        (indent-line-to cur-indent))))
-  ;; bol < point < indentation-start
-  (when (<= (point-at-bol) (point) (save-excursion (back-to-indentation) 
(point)))
-    (back-to-indentation)))
+If it does not exist, will return nil."
+  (save-excursion
+    (back-to-indentation)
+    (let ((opening-pos (nth parser-state-index (syntax-ppss))))
+      (when opening-pos
+        (goto-char opening-pos)
+        (current-indentation)))
+    )
+  )
+
+(defun kotlin-mode--indent-for-continuation ()
+  "Return the expected indentation for a continuation."
+  (kotlin-mode--prev-line)
+  (if (kotlin-mode--line-continuation)
+      (kotlin-mode--indent-for-continuation)
+    (+ kotlin-tab-width (current-indentation)))
+  )
+
+(defun kotlin-mode--indent-for-code ()
+  "Return the level that this line of code should be indented to."
+  (let ((indent-opening-block (kotlin-mode--get-opening-char-indentation 1)))
+    (cond
+     ((kotlin-mode--line-continuation) (save-excursion 
(kotlin-mode--indent-for-continuation)))
+     ((booleanp indent-opening-block) 0)
+     ((kotlin-mode--line-closes-block-p) indent-opening-block)
+     (t (+ indent-opening-block kotlin-tab-width)))
+    ))
+
+(defun kotlin-mode--indent-for-comment ()
+  "Return the level that this line of comment should be indented to."
+  (let ((opening-indentation (kotlin-mode--get-opening-char-indentation 8)))
+    (if opening-indentation
+        (1+ opening-indentation)
+      0)
+    ))
 
-(defun kotlin-mode--beginning-of-buffer-indent ()
-  (indent-line-to 0))
+(defun kotlin-mode--indent-line ()
+  "Indent the current line of Kotlin code."
+  (interactive)
+  (indent-line-to (cond
+                   ((kotlin-mode--first-line-p) 0)
+                   ((kotlin-mode--in-comment-block) 
(kotlin-mode--indent-for-comment))
+                   (t (kotlin-mode--indent-for-code))
+                   ))
+  (syntax-ppss-flush-cache (point-at-bol))
+  )
 
 ;;;###autoload
 (define-derived-mode kotlin-mode prog-mode "Kotlin"
diff --git a/test/kotlin-mode-test.el b/test/kotlin-mode-test.el
index fe96b6218a..9749006f2f 100644
--- a/test/kotlin-mode-test.el
+++ b/test/kotlin-mode-test.el
@@ -53,6 +53,34 @@ return a + b
     return a + b
 }")))))
 
+(ert-deftest kotlin-mode--lambda-body-indent-test ()
+  (with-temp-buffer
+    (let ((text "fun test(args: Array<String>) {
+args.forEach(arg ->
+println(arg)
+)
+"))
+      (insert text)
+      (goto-char (point-min))
+      (kotlin-mode)
+      (setq-local indent-tabs-mode nil)
+      (setq-local tab-width 4)
+      (setq-local kotlin-tab-width 4)
+
+      (forward-line)
+      (kotlin-mode--indent-line)
+      (forward-line)
+      (kotlin-mode--indent-line)
+      (forward-line)
+      (kotlin-mode--indent-line)
+
+      (should (equal (buffer-string) "fun test(args: Array<String>) {
+    args.forEach(arg ->
+        println(arg)
+    )
+"))
+      )))
+
 (ert-deftest kotlin-mode--chained-methods ()
   (with-temp-buffer
     (let ((text "names.filter { it.empty }
diff --git a/test/sample.kt b/test/sample.kt
index 06a8063dfc..7dd47b5eee 100644
--- a/test/sample.kt
+++ b/test/sample.kt
@@ -31,10 +31,12 @@ fun printSum(a: Int, b: Int) {
     print(veryLongResultVariableName)
 }
 
-fun functionMultiLineArgs(first: Int,
-                          second: Int,
-                          third: Int,
-                          fourth: Int) {
+fun functionMultiLineArgs(
+    first: Int,
+    second: Int,
+    third: Int,
+    fourth: Int
+) {
     print("(${first}, ${second}, ${third}, ${fourth})")
 }
 
@@ -54,9 +56,9 @@ fun main(args: Array<String>) {
 
 fun max(a: Int, b: Int): Int {
     if (a > b)
-    return a
+        return a
     else
-    return b
+        return b
 }
 
 fun max(a: Int, b: Int) = if (a > b) a else b
@@ -81,16 +83,16 @@ fun getStringLength(obj: Any): Int? {
 
 fun main(args: Array<String>) {
     for (arg in args)
-    print(arg)
+        print(arg)
 }
 
 for (i in args.indices)
-print(args[i])
+    print(args[i])
 
 fun main(args: Array<String>) {
     var i = 0
     while (i < args.size)
-    print(args[i++])
+        print(args[i++])
 }
 
 fun cases(obj: Any) {
@@ -104,19 +106,19 @@ fun cases(obj: Any) {
 }
 
 if (x in 1..y-1)
-print("OK")
+    print("OK")
 
 if (x !in 0..array.lastIndex)
-print("Out")
+    print("Out")
 
 for (x in 1..5)
-print(x)
+    print(x)
 
 for (name in names)
-println(name)
+    println(name)
 
 if (text in names) // names.contains(text) is called
-print("Yes")
+    print("Yes")
 
 names.filter { it.startsWith("A") }
     .sortedBy { it }
@@ -225,7 +227,7 @@ inline fun <reified T: Any> Gson.fromJson(json): T = 
this.fromJson(json, T::clas
 loop@ for (i in 1..100) {
     for (j in 1..100) {
         if (x)
-        break@loop
+            break@loop
     }
 }
 
@@ -362,7 +364,7 @@ var stringRepresentation: String
     }
 
 var setterVisibility: String = "abc"
-private set // the setter is private and has the default implementation
+    private set // the setter is private and has the default implementation
 
 var setterWithAnnotation: Any? = null
 @Inject set // annotate the setter with Inject
@@ -370,7 +372,7 @@ var setterWithAnnotation: Any? = null
 var counter = 0 // the initializer value is written directly to the backing 
field
     set(value) {
         if (value >= 0)
-        field = value
+            field = value
     }
 
 val isEmpty: Boolean
@@ -444,7 +446,7 @@ class D : A, B {
 private fun foo() {} // visible inside example.kt
 
 public var bar: Int = 5 // property is visible everywhere
-private set         // setter is visible only in example.kt
+    private set         // setter is visible only in example.kt
 
 internal val baz = 6    // visible inside the same module
 
@@ -589,7 +591,8 @@ window.addMouseListener(
         override fun mouseEntered(e: MouseEvent) {
             // ...
         }
-})
+    }
+)
 
 val adHoc = object {
     var x: Int = 0
@@ -610,7 +613,8 @@ fun countClicks(window: JComponent) {
             override fun mouseEntered(e: MouseEvent) {
                 enterCount++
             }
-    })
+        }
+    )
     // ...
 }
 
@@ -648,7 +652,7 @@ infix fun Int.shl(x: Int): Int {
 fun <T> asList(vararg ts: T): List<T> {
     val result = ArrayList<T>()
     for (t in ts) // ts is an Array
-    result.add(t)
+        result.add(t)
     return result
 }
 
@@ -670,7 +674,7 @@ val result = lock(lock, ::toBeSynchronized)
 fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
     val result = arrayListOf<R>()
     for (item in this)
-    result.add(transform(item))
+        result.add(transform(item))
     return result
 }
 
@@ -678,13 +682,14 @@ val doubled = ints.map { it -> it * 2 }
 
 strings.filter { it.length == 5 }.sortBy { it }.map { it.toUpperCase() }
 
-max(strings, { a, b -> a.length < b.length })
+max(strings, { a, b -> a.length < b.length
+})
 
 fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {
     var max: T? = null
     for (it in collection)
-    if (max == null || less(max, it))
-    max = it
+        if (max == null || less(max, it))
+        max = it
     return max
 }
 
@@ -728,12 +733,10 @@ inline fun <reified T> TreeNode.findParentOfType(): T? {
 class Test {
     fun tryAdd(a: Int?, b: Int?) : Int? {
         var result: Int? = null
-        a?.let {
-            lhs ->
-                b?.let {
-                    rhs ->
-                        result = lhs + rhs
-                }
+        a?.let { lhs ->
+            b?.let { rhs ->
+                result = lhs + rhs
+            }
         }
         return result
     }



reply via email to

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