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

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

[nongnu] elpa/kotlin-mode d6720fe9bc 123/162: Merge pull request #40 fro


From: ELPA Syncer
Subject: [nongnu] elpa/kotlin-mode d6720fe9bc 123/162: Merge pull request #40 from martinblake/master
Date: Sat, 29 Jan 2022 08:25:29 -0500 (EST)

branch: elpa/kotlin-mode
commit d6720fe9bc2ce447f213c470bd18fad8e04dc18b
Merge: 0e542ae2f7 5e9aff2ae9
Author: Gregg Hernandez <greggory.hz@gmail.com>
Commit: GitHub <noreply@github.com>

    Merge pull request #40 from martinblake/master
    
    Indentation Improvements
---
 kotlin-mode.el           | 244 +++++++++++++++++++++++++++++++++--------------
 test/kotlin-mode-test.el |  74 ++++++++++----
 test/sample.kt           | 124 ++++++++++++++----------
 3 files changed, 305 insertions(+), 137 deletions(-)

diff --git a/kotlin-mode.el b/kotlin-mode.el
index 143cc880e4..e5c7b92373 100644
--- a/kotlin-mode.el
+++ b/kotlin-mode.el
@@ -28,6 +28,8 @@
 (require 'comint)
 (require 'rx)
 (require 'cc-cmds)
+(require 'cl)
+(require 'eieio)
 
 (defgroup kotlin nil
   "A Kotlin major mode."
@@ -128,7 +130,6 @@
     (define-key map (kbd "C-c C-r") 'kotlin-send-region)
     (define-key map (kbd "C-c C-c") 'kotlin-send-block)
     (define-key map (kbd "C-c C-b") 'kotlin-send-buffer)
-    (define-key map (kbd "<tab>") 'c-indent-line-or-region)
     map)
   "Keymap for kotlin-mode")
 
@@ -173,7 +174,7 @@
     "when" "is" "in" "as" "return"))
 
 (defconst kotlin-mode--context-variables-keywords
-  '("this" "super"))
+  '("field" "it" "this" "super"))
 
 (defvar kotlin-mode--keywords
   (append kotlin-mode--misc-keywords
@@ -193,7 +194,7 @@
     "annotation" "internal" "const" "in" "out")) ;; "in" "out"
 
 (defconst kotlin-mode--property-keywords
-  '("by")) ;; "by" "get" "set"
+  '("by" "get" "set")) ;; "by" "get" "set"
 
 (defconst kotlin-mode--initializer-keywords
   '("init" "constructor"))
@@ -257,11 +258,11 @@
 
     ;; Properties
     ;; by/get/set are valid identifiers being used as variable
-    ;; TODO: Highlight keywords in the property declaration statement
-    ;; (,(rx-to-string
-    ;;    `(and bow (group (or ,@kotlin-mode--property-keywords)) eow)
-    ;;    t)
-    ;;  1 font-lock-keyword-face)
+    ;; TODO: Highlight only within the property declaration statement
+    (,(rx-to-string
+       `(and bow (group (or ,@kotlin-mode--property-keywords)) eow)
+       t)
+     1 font-lock-keyword-face)
 
     ;; Constructor/Initializer blocks
     (,(rx-to-string
@@ -324,70 +325,175 @@
         (while (and (looking-at "^[ \t]*$") (not (bobp)))
           (forward-line -1)))))
 
+(defun kotlin-mode--line-begins (pattern)
+  "Return whether the current line begins with the given pattern"
+  (save-excursion
+    (beginning-of-line)
+    (looking-at (format "^[ \t]*%s" pattern))))
+
+(defun kotlin-mode--line-ends (pattern)
+  "Return whether the current line ends with the given pattern"
+  (save-excursion
+    (beginning-of-line)
+    (looking-at (format ".*%s[ \t]*$" pattern))))
+
+(defun kotlin-mode--line-contains (pattern)
+  "Return whether the current line contains the given pattern"
+  (save-excursion
+    (beginning-of-line)
+    (looking-at (format ".*%s.*" pattern))))
+
+(defun kotlin-mode--line-continuation()
+  "Return whether this line continues a statement in the previous line"
+  (or
+   (kotlin-mode--line-begins "\\([\.=:]\\|->\\|[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.")
+
+(cl-defmethod count-to-line-start ((counter kotlin-mode--bracket-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.
+   Return as soon as the overall count exceeds zero."
+  (save-excursion
+    (while (and (<= (oref counter count) 0) (not (bolp)))
+      (backward-char)
+      (cond ((looking-at "\\s\(")
+             (oset counter count (+ (oref counter count) 1)))
+            ((looking-at "\\s\)")
+             (oset counter count (- (oref counter count) 1)))))
+    (cond
+     ;; If the net-bracket-count is zero, use this indentation
+     ((= (oref counter count) 0)
+      (oset counter finished t)
+      (if (oref counter use-base)
+          (add-indent counter (kotlin-mode--base-indentation))
+        (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.
+     ((> (oref counter count) 0)
+      (oset counter finished t)
+      (forward-char)
+      (skip-syntax-forward "(")
+      (skip-syntax-forward "-")
+      (let (position)
+        (setq position (point))
+        (add-indent counter (- position (re-search-backward "^"))))))))
+
+(cl-defmethod count-leading-close-brackets
+    ((counter kotlin-mode--bracket-counter))
+  "Count any close-bracket at the start of the current line."
+  (if (looking-at "\\s\)")
+      (oset counter use-base nil))
+  (subtract-count counter (skip-syntax-forward ")")))
+
+(cl-defmethod count-trailing-open-brackets
+    ((counter kotlin-mode--bracket-counter))
+  "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 (= (oref counter count) 0)
+      (cond ((not (= (skip-syntax-backward "(") 0))
+             (add-indent counter kotlin-tab-width)
+             (oset counter use-base nil)))))
+
+(cl-defmethod add-count ((counter kotlin-mode--bracket-counter) val)
+  (oset counter count (+ (oref counter count) val)))
+
+(cl-defmethod subtract-count ((counter kotlin-mode--bracket-counter) val)
+  (oset counter count (- (oref counter count) val)))
+
+(cl-defmethod add-indent ((counter kotlin-mode--bracket-counter) val)
+  (oset counter indent (+ (oref counter indent) val)))
+
+(cl-defmethod finished ((counter kotlin-mode--bracket-counter))
+  (oref counter finished))
+
+
+(defun kotlin-mode--in-comment-block ()
+  "Return whether the cursor is within a standard comment block structure
+   of the following format:
+   /**
+    * Description here
+    */ "
+  (save-excursion
+    (let ((in-comment-block nil)
+          (keep-going (not (kotlin-mode--line-ends "\\*\\*/"))))
+      (while keep-going
+        (kotlin-mode--prev-line)
+        (cond
+         ((bobp)
+          (setq keep-going nil))
+         ((kotlin-mode--line-contains "\\*/")
+          (setq keep-going nil))
+         ((kotlin-mode--line-begins "/\\*")
+          (setq keep-going nil)
+          (setq in-comment-block t))))
+      in-comment-block)))
+
 (defun kotlin-mode--indent-line ()
-  "Indent current line as kotlin code"
+  "Indent current line as kotlin code."
   (interactive)
   (beginning-of-line)
-  (if (bobp) ; 1.)
-      (progn
-        (kotlin-mode--beginning-of-buffer-indent))
-    (let ((not-indented t) cur-indent)
-      (cond ((looking-at "^[ \t]*\\.") ; line starts with .
-             (save-excursion
-               (kotlin-mode--prev-line)
-               (cond ((looking-at "^[ \t]*\\.")
-                      (setq cur-indent (current-indentation)))
-
-                     (t
-                      (setq cur-indent (+ (current-indentation) (* 2 
kotlin-tab-width)))))
-               (if (< cur-indent 0)
-                   (setq cur-indent 0))))
-
-            ((looking-at "^[ \t]*}") ; line starts with }
-             (save-excursion
-               (kotlin-mode--prev-line)
-               (while (and (or (looking-at "^[ \t]*$") (looking-at "^[ 
\t]*\\.")) (not (bobp)))
-                 (kotlin-mode--prev-line))
-               (cond ((or (looking-at ".*{[ \t]*$") (looking-at ".*{.*->[ 
\t]*$"))
-                      (setq cur-indent (current-indentation)))
-                     (t
-                      (setq cur-indent (- (current-indentation) 
kotlin-tab-width)))))
-             (if (< cur-indent 0)
-                 (setq cur-indent 0)))
-
-            ((looking-at "^[ \t]*)") ; line starts with )
-             (save-excursion
-               (kotlin-mode--prev-line)
-               (setq cur-indent (- (current-indentation) kotlin-tab-width)))
-             (if (< cur-indent 0)
-                 (setq cur-indent 0)))
-
-            (t
-             (save-excursion
-               (while not-indented
-                 (kotlin-mode--prev-line)
-                 (cond ((looking-at ".*{[ \t]*$") ; line ends with {
-                        (setq cur-indent (+ (current-indentation) 
kotlin-tab-width))
-                        (setq not-indented nil))
-
-                       ((looking-at "^[ \t]*}") ; line starts with }
-                        (setq cur-indent (current-indentation))
-                        (setq not-indented nil))
-
-                       ((looking-at ".*{.*->[ \t]*$") ; line ends with ->
-                        (setq cur-indent (+ (current-indentation) 
kotlin-tab-width))
-                        (setq not-indented nil))
-
-                       ((looking-at ".*([ \t]*$") ; line ends with (
-                        (setq cur-indent (+ (current-indentation) 
kotlin-tab-width))
-                        (setq not-indented nil))
-
-                       ((looking-at "^[ \t]*).*$") ; line starts with )
-                        (setq cur-indent (current-indentation))
-                        (setq not-indented nil))
-
-                       ((bobp) ; 5.)
-                        (setq not-indented nil)))))))
+  (if (bobp)
+      (kotlin-mode--beginning-of-buffer-indent)
+    (let ((cur-indent 0))
+      ;; Find bracket-based indentation first
+      (let ((bracket-counter (kotlin-mode--bracket-counter)))
+        (save-excursion
+          (skip-syntax-forward "-")
+          (count-leading-close-brackets bracket-counter))
+        (save-excursion
+          (progn (kotlin-mode--prev-line) (end-of-line))
+          (count-trailing-open-brackets bracket-counter)
+          (count-to-line-start bracket-counter)
+          (while (and (not (finished bracket-counter)) (not (bobp)))
+            (progn (kotlin-mode--prev-line) (end-of-line))
+            (count-to-line-start bracket-counter)
+            )
+          (incf cur-indent (oref bracket-counter indent))))
+
+      (cond ((kotlin-mode--line-continuation)
+             ;; Add extra indentation if the line continues the previous one
+             (incf cur-indent kotlin-tab-width))
+            ((kotlin-mode--in-comment-block)
+             ;; Add one space of extra indentation if inside a comment block
+             (incf cur-indent)))
+
       (if cur-indent
           (indent-line-to cur-indent)
         (indent-line-to 0)))))
diff --git a/test/kotlin-mode-test.el b/test/kotlin-mode-test.el
index c75137cd3b..5329d00afa 100644
--- a/test/kotlin-mode-test.el
+++ b/test/kotlin-mode-test.el
@@ -1,3 +1,7 @@
+;; Tests assume a tab is represented by 4 spaces
+(setq-default indent-tabs-mode nil)
+(setq-default tab-width 4)
+
 (load-file "kotlin-mode.el")
 
 ;(require 'kotlin-mode)
@@ -44,31 +48,61 @@ return a + b
 
       (kotlin-mode--indent-line)
       (should (equal (buffer-string) "fun sum(a: Int, b: Int): Int {
-       return a + b
+    return a + b
 }")))))
 
-;; (ert-deftest kotlin-mode--chained-methods ()
-;;   (with-temp-buffer
-;;     (let ((text "names.filter { it.empty }
-;; .sortedBy { it }
-;; .map { it.toUpperCase() }
-;; .forEach { print(it) }"))
+(ert-deftest kotlin-mode--chained-methods ()
+  (with-temp-buffer
+    (let ((text "names.filter { it.empty }
+.sortedBy { it }
+.map { it.toUpperCase() }
+.forEach { print(it) }"))
+
+      (insert text)
+      (beginning-of-buffer)
+
+      (kotlin-mode--indent-line)
 
-;;       (insert text)
-;;       (beginning-of-buffer)
+      (next-line)
+      (kotlin-mode--indent-line)
 
-;;       (kotlin-mode--indent-line)
+      (next-line)
+      (kotlin-mode--indent-line)
+
+      (next-line)
+      (kotlin-mode--indent-line)
+
+      (should (equal (buffer-string) "names.filter { it.empty }
+    .sortedBy { it }
+    .map { it.toUpperCase() }
+    .forEach { print(it) }")))))
+
+(defun next-non-empty-line ()
+  "Moves to the next non-empty line"
+  (forward-line)
+  (while (and (looking-at "^[ \t]*$") (not (eobp)))
+    (forward-line)))
+
+(ert-deftest kotlin-mode--sample-test ()
+  (with-temp-buffer
+      (insert-file-contents "test/sample.kt")
+      (beginning-of-buffer)
+      (while (not (eobp))
+        (let ((expected-line (thing-at-point 'line)))
 
-;;       (next-line)
-;;       (kotlin-mode--indent-line)
+          ;; Remove existing indentation
+          (beginning-of-line)
+          (delete-region (point) (progn (skip-chars-forward " \t") (point)))
 
-;;       (next-line)
-;;       (kotlin-mode--indent-line)
+          ;; Indent the line
+          (kotlin-mode--indent-line)
 
-;;       (next-line)
-;;       (kotlin-mode--indent-line)
+          ;; Check that the correct indentation is re-applied
+          (should (equal expected-line (thing-at-point 'line)))
 
-;;       (should (equal (buffer-string) "names.filter { it.empty }
-;;     .sortedBy { it }
-;;     .map { it.toUpperCase() }
-;;     .forEach { print(it) }")))))
\ No newline at end of file
+          ;; Go to the next non-empty line
+          (next-non-empty-line)
+          )
+        )
+      )
+  )
diff --git a/test/sample.kt b/test/sample.kt
index 069c879711..06a8063dfc 100644
--- a/test/sample.kt
+++ b/test/sample.kt
@@ -6,9 +6,14 @@ import bar.Bar as bBar
 
 // a single line comment
 
-/*
-* a multiline comment
-*/
+/**
+ * a multiline comment
+ */
+
+/****************************************************************
+ Multiline comment
+ without leading "*"
+****************************************************************/
 
 fun sum(a: Int, b: Int): Int {
     return a + b
@@ -21,7 +26,16 @@ fun printSum(a: Int, b: Int): Unit {
 }
 
 fun printSum(a: Int, b: Int) {
-    print(a + b)
+    val veryLongResultVariableName: Int
+        = (a + b)
+    print(veryLongResultVariableName)
+}
+
+fun functionMultiLineArgs(first: Int,
+                          second: Int,
+                          third: Int,
+                          fourth: Int) {
+    print("(${first}, ${second}, ${third}, ${fourth})")
 }
 
 val a: Int = 1
@@ -105,14 +119,14 @@ if (text in names) // names.contains(text) is called
 print("Yes")
 
 names.filter { it.startsWith("A") }
-        .sortedBy { it }
-        .map { it.toUpperCase() }
-        .forEach { print(it) }
+    .sortedBy { it }
+    .map { it.toUpperCase() }
+    .forEach { print(it) }
 
 fun f() {
     things.f()
-            .g()
-            .h()
+        .g()
+        .h()
 }
 
 data class Customer(val name: String, val email: String)
@@ -191,18 +205,19 @@ class Turtle {
 }
 
 val myTurtle = Turtle()
-with(myTurtle) { //draw a 100 pix square
-penDown()
-for(i in 1..4) {
-    forward(100.0)
-    turn(90.0)
-}
-penUp()
+with(myTurtle) {
+    //draw a 100 pix square
+    penDown()
+    for(i in 1..4) {
+        forward(100.0)
+        turn(90.0)
+    }
+    penUp()
 }
 
 val stream = Files.newInputStream(Paths.get("/some/file.txt"))
-stream.buffered().reader().use { reader ->
-    println(reader.readText())
+stream.buffered().reader().use {
+    reader -> println(reader.readText())
 }
 
 inline fun <reified T: Any> Gson.fromJson(json): T = this.fromJson(json, 
T::class.java)
@@ -287,7 +302,9 @@ class Derived() : Base() {
     override fun v() {}
 }
 
-open class AnotherDerived() : Base() {
+open class AnotherDerived()
+    : Base()
+{
     final override fun v() {}
 }
 
@@ -310,7 +327,7 @@ interface B {
 }
 
 class C() : A(), B {
-    // The compiler requires f() to be overridden:
+    // The compiler requires f() to be overridden
     override fun f() {
         super<A>.f() // call to A.f()
         super<B>.f() // call to B.f()
@@ -339,10 +356,10 @@ fun eval(expr: Expr): Double = when(expr) {
 }
 
 var stringRepresentation: String
-get() = this.toString()
-set(value) {
-    setDataFromString(value) // parses the string and assigns values to other 
properties
-}
+    get() = this.toString()
+    set(value) {
+        setDataFromString(value) // parses the string and assigns values to 
other properties
+    }
 
 var setterVisibility: String = "abc"
 private set // the setter is private and has the default implementation
@@ -351,13 +368,13 @@ var setterWithAnnotation: Any? = null
 @Inject set // annotate the setter with Inject
 
 var counter = 0 // the initializer value is written directly to the backing 
field
-set(value) {
-    if (value >= 0)
-    field = value
-}
+    set(value) {
+        if (value >= 0)
+        field = value
+    }
 
 val isEmpty: Boolean
-get() = this.size == 0
+    get() = this.size == 0
 
 const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
 
@@ -392,7 +409,7 @@ interface MyInterface {
     val property: Int // abstract
 
     val propertyWithImplementation: String
-    get() = "foo"
+        get() = "foo"
 
     fun foo() {
         print(property)
@@ -462,7 +479,7 @@ fun Any?.toString(): String {
 }
 
 val <T> List<T>.lastIndex: Int
-get() = size - 1
+    get() = size - 1
 
 class MyClass {
     companion object { }  // will be called "Companion"
@@ -563,14 +580,15 @@ enum class ProtocolState {
     abstract fun signal(): ProtocolState
 }
 
-window.addMouseListener(object : MouseAdapter() {
-    override fun mouseClicked(e: MouseEvent) {
-        // ...
-    }
+window.addMouseListener(
+    object : MouseAdapter() {
+        override fun mouseClicked(e: MouseEvent) {
+            // ...
+        }
 
-    override fun mouseEntered(e: MouseEvent) {
-        // ...
-    }
+        override fun mouseEntered(e: MouseEvent) {
+            // ...
+        }
 })
 
 val adHoc = object {
@@ -583,14 +601,15 @@ fun countClicks(window: JComponent) {
     var clickCount = 0
     var enterCount = 0
 
-    window.addMouseListener(object : MouseAdapter() {
-        override fun mouseClicked(e: MouseEvent) {
-            clickCount++
-        }
+    window.addMouseListener(
+        object : MouseAdapter() {
+            override fun mouseClicked(e: MouseEvent) {
+                clickCount++
+            }
 
-        override fun mouseEntered(e: MouseEvent) {
-            enterCount++
-        }
+            override fun mouseEntered(e: MouseEvent) {
+                enterCount++
+            }
     })
     // ...
 }
@@ -634,7 +653,7 @@ fun <T> asList(vararg ts: T): List<T> {
 }
 
 tailrec fun findFixPoint(x: Double = 1.0): Double
-= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
+    = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
 
 fun <T> lock(lock: Lock, body: () -> T): T {
     lock.lock()
@@ -707,8 +726,16 @@ inline fun <reified T> TreeNode.findParentOfType(): T? {
 }
 
 class Test {
-    fun f() {
-
+    fun tryAdd(a: Int?, b: Int?) : Int? {
+        var result: Int? = null
+        a?.let {
+            lhs ->
+                b?.let {
+                    rhs ->
+                        result = lhs + rhs
+                }
+        }
+        return result
     }
 }
 
@@ -718,3 +745,4 @@ fun itpl() {
     print("${foo}bar");
     print("${`weird$! identifier`}bar");
 }
+



reply via email to

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