[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
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [nongnu] elpa/kotlin-mode 8d5b1cada0 1/2: Making a number of improvements to the indentation logic,
ELPA Syncer <=