From c7d5c67ebc36edeccbdab2f88edf32011244e489 Mon Sep 17 00:00:00 2001 From: taku0 Date: Sun, 3 Nov 2019 17:48:46 +0900 Subject: [PATCH 1/9] Improve detection of line continuation Improve regression test to ignore known bug. Add pathological regression test. --- kotlin-mode.el | 355 +++++++- test/kotlin-mode-test.el | 75 +- test/pathological.kt | 1663 ++++++++++++++++++++++++++++++++++++++ test/sample.kt | 79 +- 4 files changed, 2080 insertions(+), 92 deletions(-) create mode 100644 test/pathological.kt diff --git a/kotlin-mode.el b/kotlin-mode.el index 6b715a5..cd13e79 100644 --- a/kotlin-mode.el +++ b/kotlin-mode.el @@ -329,53 +329,348 @@ (kotlin-mode--match-interpolation limit)))))) (defun kotlin-mode--prev-line () - "Moves up to the nearest non-empty line" - (if (not (bobp)) - (progn - (forward-line -1) - (while (and (looking-at "^[ \t]*$") (not (bobp))) - (forward-line -1))))) - -(defun kotlin-mode--prev-line-begins (pattern) - "Return whether the previous line begins with the given pattern" - (save-excursion - (kotlin-mode--prev-line) - (looking-at (format "^[ \t]*%s" pattern)))) - -(defun kotlin-mode--prev-line-ends (pattern) - "Return whether the previous line ends with the given pattern" - (save-excursion - (kotlin-mode--prev-line) - (looking-at (format ".*%s[ \t]*$" pattern)))) + "Moves up to the nearest non-empty line." + (beginning-of-line) + ;; `forward-comment' skips spaces and newlines as well. + (forward-comment (- (point)))) (defun kotlin-mode--line-begins (pattern) - "Return whether the current line begins with the given pattern" + "Return whether the current line begins with the given PATTERN. + +Ignore spaces at the beginning of the line." (save-excursion (beginning-of-line) (looking-at (format "^[ \t]*%s" pattern)))) +(defun kotlin-mode--line-begins-excluding-comment (pattern) + "Return whether the current line begins with the given PATTERN. + +Ignore comments and spaces at the beginning of the line." + (let ((line-end-position (line-end-position))) + (save-excursion + (beginning-of-line) + (when (nth 4 (syntax-ppss)) + ;; If the point is inside a comment, goto the beginning of the + ;; comment. + (goto-char (nth 8 (syntax-ppss)))) + (forward-comment (point-max)) + (when (< line-end-position (point)) + (goto-char line-end-position)) + (looking-at pattern)))) + (defun kotlin-mode--line-ends (pattern) - "Return whether the current line ends with the given pattern" + "Return whether the current line ends with the given PATTERN. + +Ignore spaces at the end of the line." (save-excursion (beginning-of-line) (looking-at (format ".*%s[ \t]*$" pattern)))) +(defun kotlin-mode--line-ends-excluding-comment (pattern) + "Return whether the current line ends with the given PATTERN. + +Ignore comments at the end of the line." + (let ((end-position + ;; last point where is neither spaces nor comment + (max + (line-beginning-position) + (save-excursion + (end-of-line) + (when (nth 4 (syntax-ppss)) + ;; If the point is inside a comment, goto the beginning + ;; of the comment. + (goto-char (nth 8 (syntax-ppss)))) + (forward-comment (- (point))) + (point))))) + (save-excursion + (save-restriction + (beginning-of-line) + (narrow-to-region (point) end-position) + (looking-at (format ".*%s$" pattern)))))) + (defun kotlin-mode--line-contains (pattern) - "Return whether the current line contains the given 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() +(defun kotlin-mode--line-continuation () "Return whether this line continues a statement in the previous line" - (or - (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\\)")))) + (let ((case-fold-search nil)) + (cond + ;; Tokens that end a statement + ((save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment + (rx (group + (or + ".*" + (seq word-start + (or "return" "continue" "break") + word-end)))))) + nil) + + ;; Modifiers, that cannot end a statement. + ((save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment + (rx (group (seq word-start + (or + "public" "private" "protected" + "internal" "enum" "sealed" "annotation" + "data" "inner" "tailrec" "operator" "inline" + "infix" "external" "suspend" "override" + "abstract" "final" "open" "const" "lateinit" + "vararg" "noinline" "crossinline" "reified" + "expect" "actual") + word-end))))) + t) + + ;; Tokens that start a statement that have lower priority than modifiers. + ((kotlin-mode--line-begins-excluding-comment + (rx (group (seq word-start + (or + "public" "private" "protected" "internal" + "enum" "sealed" "annotation" "data" "inner" + "tailrec" "operator" "inline" "infix" + "external" "override" "abstract" "final" + "open" "const" "lateinit" "vararg" "noinline" + "crossinline" "reified" "expect" "actual" + "package" "import" "interface" "val" "var" + "typealias" "constructor" "companion" "init" + "is" "in" "out" "for" "while" "do") + word-end)))) + nil) + + ((and (kotlin-mode--line-begins-excluding-comment + (rx (group (seq word-start "class" word-end)))) + (not + (save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment (rx (group "::")))))) + nil) + + ;; Tokens that cannot end a statement + ((save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment + (rx (group + (or + (any "-%*./:=+&|<@") + "->" + (seq word-start + "as?") + (seq "!is" + "!in" + word-end) + (seq word-start + (or + "package" "import" "class" "interface" "fun" + "object" "val" "var" "typealias" "constructor" + "by" "companion" "init" "where" "if" "else" + "when" "try" "catch" "finally" "for" "do" "while" + "throw" "as" "is" "in" "out") + word-end)))))) + t) + + ;; Tokens that cannot start a statement + ((kotlin-mode--line-begins-excluding-comment + (rx (group + (or + (any ".:=" + (seq word-start "as?") + (seq word-start + (or "get" "set" "as" "by" "where") + word-end))))) + t) + + (t nil)))) + +(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()) +}" + ;; Ignore comments and spaces at the end of the line. + ;; Example: + ;; fun foo() { // ignore comments here + ;; bar() + ;; } + (when (nth 4 (syntax-ppss)) + ;; If the point is inside a comment, goto the beginning of the + ;; comment. + (goto-char (nth 8 (syntax-ppss)))) + (forward-comment (- (point))) + + (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)) (defun kotlin-mode--in-comment-block () "Return whether the cursor is within a standard comment block structure diff --git a/test/kotlin-mode-test.el b/test/kotlin-mode-test.el index 9749006..897a8f8 100644 --- a/test/kotlin-mode-test.el +++ b/test/kotlin-mode-test.el @@ -181,25 +181,56 @@ println(arg) (forward-line))) (ert-deftest kotlin-mode--sample-test () - (with-temp-buffer - (insert-file-contents "test/sample.kt") - (goto-char (point-min)) - (kotlin-mode) - (setq-local indent-tabs-mode nil) - (setq-local tab-width 4) - (setq-local kotlin-tab-width 4) - (while (not (eobp)) - (let ((expected-line (thing-at-point 'line))) - - ;; Remove existing indentation - (beginning-of-line) - (delete-region (point) (progn (skip-chars-forward " \t") (point))) - - ;; Indent the line - (kotlin-mode--indent-line) - - ;; Check that the correct indentation is re-applied - (should (equal expected-line (thing-at-point 'line))) - - ;; Go to the next non-empty line - (next-non-empty-line))))) + (dolist (filename '("test/sample.kt" "test/pathological.kt")) + (with-temp-buffer + (insert-file-contents filename) + (goto-char (point-min)) + (kotlin-mode) + (setq-local indent-tabs-mode nil) + (setq-local tab-width 4) + (setq-local kotlin-tab-width 4) + + (while (not (eobp)) + (back-to-indentation) + (let (;; (thing-at-point 'line) returns string with property. + (expected-line (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + actual-line + (original-indent (current-column)) + (known-bug (looking-at ".*//.*KNOWN_BUG"))) + ;; Remove existing indentation, or indent to column 1 if + ;; expected indentation is column 0. + (if (= original-indent 0) + (indent-line-to 1) + (delete-horizontal-space)) + + ;; Indent the line + (kotlin-mode--indent-line) + + (setq actual-line (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + + ;; Check that the correct indentation is re-applied + (if known-bug + (if (equal expected-line actual-line) + (message "%s:%s:info: KNOWN_BUG is fixed somehow" + filename + (line-number-at-pos)) + (back-to-indentation) + (message "%s:%s:warn: (known bug) expected indentation to column %d but %d" + filename + (line-number-at-pos) + original-indent + (current-column))) + (should + (equal + (format "%s:%s: %s" filename (line-number-at-pos) expected-line) + (format "%s:%s: %s" filename (line-number-at-pos) actual-line)))) + + ;; Restore to original indentation for KNOWN_BUG line. + (indent-line-to original-indent) + + ;; Go to the next non-empty line + (next-non-empty-line)))))) diff --git a/test/pathological.kt b/test/pathological.kt new file mode 100644 index 0000000..ec3421f --- /dev/null +++ b/test/pathological.kt @@ -0,0 +1,1663 @@ +#!/usr/bin/kotlinc -script + +// fileAnnotation +@file + : + [ + A(a = 1) // No Comma here + B + . + C + . + D // No Comma here + E + ] + +@file + : + Foo + . + Bar + . + Baz + +// packageHeader +package foo // Line breaks are prohibited after "package". + .bar // Line breaks are prohibited after dot. + .baz + +// importList, importHeader +import a // Line breaks are prohibited after "import". + .b // Line breaks are prohibited after dot. + .c + .* + +// importAlias +import a + . b + . c + as d + +// typeAlias +@A +public + typealias + A + < + B, // KNOWN_BUG + C // KNOWN_BUG + > // KNOWN_BUG + = // KNOWN_BUG + D + +// classDeclaration + +@A +public + sealed + class + A + < + A, // KNOWN_BUG + B // KNOWN_BUG + > // KNOWN_BUG + // primaryConstructor + public + constructor // KNOWN_BUG + ( + // classParameters + val + x + : + Foo + = + foo, + var + y + : + Foo + = + foo + ) + : + // delegationSepcifiers + Base + by + x + + x, + B, // KNOWN_BUG + C, + D + . + (Int) + -> + Int + where // KNOWN_BUG + @A // KNOWN_BUG + A // KNOWN_BUG + : + A, + B + : + B { + + public // KNOWN_BUG + interface + A { + fun a(): Int // KNOWN_BUG + } // KNOWN_BUG + + // typeParameters + data + class + Bar< + @A // KNOWN_BUG + out // KNOWN_BUG + A + : + A, + @A + in + A + : + A + > // KNOWN_BUG + { // brace on its own line // KNOWN_BUG + fun a() { + } + } + + inner + enum + class + A(val x: Int) { + // enumClassBody // KNOWN_BUG + A(1), B(1), // KNOWN_BUG + C(1) { + override fun a() = 2 + }, D(1), + E(1); + + fun a(): Int = x + } // KNOWN_BUG + + // anonymousInitializer + init { + a() + } + + // companionObject + public + companion + object + A + : + B { + fun foo() { // KNOWN_BUG + } + } // KNOWN_BUG + + // functionDeclaration + public + fun + < + A // KNOWN_BUG + > + A + . // KNOWN_BUG + foo + // functionValueParameters // KNOWN_BUG + ( // KNOWN_BUG + a: Int = 1 + ) + : // KNOWN_BUG + A + where + A : A { + a() // KNOWN_BUG + } // KNOWN_BUG + + fun + foo(x) + = + x + 1 + + // propertyDeclaration + public + val + < + A // KNOWN_BUG + > // KNOWN_BUG + A + . // KNOWN_BUG + @A + a // KNOWN_BUG + : // KNOWN_BUG + A + where + A + : + A + = + a + get() = 1 + set(value) { + foo(value) + } + + // delegated property + var + x : Int + by + foo + + // multiVariableDeclaration + public + var + ( + x: Int, + y: Int + ) + + // getter/setter with default implementation + var x = 1 + @A get // KNOWN_BUG + @A set + + // objectDeclaration // KNOWN_BUG + public // KNOWN_BUG + object + Foo + : + Bar { + fun foo() { // KNOWN_BUG + } + } // KNOWN_BUG + + // secondaryConstructor + public + constructor + ( + ) + : + this + ( // KNOWN_BUG + 1 + ) { + a() // KNOWN_BUG + } // KNOWN_BUG + + public // KNOWN_BUG + constructor + ( + ) + : + super + ( // KNOWN_BUG + 1 + ) + + // dynamic type // KNOWN_BUG + var x: dynamic = 1 // KNOWN_BUG + + // nullableType + var + x + : + A + < + X, // KNOWN_BUG + *, // KNOWN_BUG + out + Y, + in + Z + > // KNOWN_BUG + . // KNOWN_BUG + B + . + C + < + X // KNOWN_BUG + > + ? // KNOWN_BUG + ? + ? + = + null + + var + x + : + ( + Int + ) + ? + = + null + + // functionType + var + f + : + // parehthesized nullable receiver type + ( + A + ) + ? + . + ( + Int + ) + -> + ( + Int + ) + -> + C +} // KNOWN_BUG + +// statements // KNOWN_BUG +fun foo() { // KNOWN_BUG + //explicit semicolons + a(); + b(); + c(); + + // annotation + @A + // label + aaa@ + a() // KNOWN_BUG + + // forStatement // KNOWN_BUG + for ( // KNOWN_BUG + @A + a + : + A + in + aaa + ) { + a() + } + + for + ( + @A + (x, y) + in + aaa + ) + { + a() + } + + for ( + a in aaa + ) + a() // KNOWN_BUG + + // whileStatement // KNOWN_BUG + while ( // KNOWN_BUG + a() + ) { + a() + } + + while + ( + a() + ) + { + a() + } + + while ( + a() + ) + a() // KNOWN_BUG + + while ( // KNOWN_BUG + a() + ) + ; // KNOWN_BUG + + // doWhileStatement // KNOWN_BUG + do { // KNOWN_BUG + a() + } while ( + a() + ) + + do + { // KNOWN_BUG + a() + } + while // KNOWN_BUG + ( + a() + ) + + do + a() + while (a()) + + do + while (a()) + + // assignment + (x, y) = // Line breaks are prohibited before assignment operators. + a() + + aaa[x] = + 1 + + a + . + b + ?. + c + . + d = + e + + a + . + b += + 1 + + a + . + b -= + 1 + + a + . + b *= + 1 + + a + . + b %= + 1 + + // expression + // various operators + val x = + a + || + b + && + c + + val x = + a == // Line breaks are prohibited before equality operators + b + + val x = + a != + b + + val x = + a === + b + + val x = + a !== + g + + val x = + a < // Line breaks are prohibited before comparison operators + b + + val x = + a > + b // KNOWN_BUG + + val x = // KNOWN_BUG + a <= + b + + val x = + a >= + b + + val x = + a in // Line breaks are prohibited before in/is operators + b + + val x = + a + !in // KNOWN_BUG + b // KNOWN_BUG + + when (a()) { + 1 -> a() + // Line breaks are prohibited before in/is operators, So the following + // line should not be indented. + in aaa -> a() + } + + val x = + a is + b + + val x = + a !is + b + + when (a()) { + 1 -> a() + // Line breaks are prohibited before in/is operators, So the following + // line should not be indented. + is X -> a() + } + + val x = + a + ?: + b + + // infixFunctionCall + val x = + a shl // Line breaks are allowed after infix function. + b // KNOWN_BUG + + // This is not a infix function call; line breaks are // KNOWN_BUG + // prohibited before infix function. // KNOWN_BUG + val x = // KNOWN_BUG + a + f (b) // So this line should not indented. + + val x = + a .. // Line breaks are prohibited before range operator. + b + + val x = + a + // Line breaks are prohibited before additive operators. + b - + c + + a() + +a() // So this line should not be indented. + -a() // Nor should this line. + + val x = + a * // Line breaks are prohibited before multiplicative operators. + b / + c % + d + + val x = + a + as + A + as? + B + + // prefixUnaryExpression + val x = + @a + a@ // label // KNOWN_BUG + + // KNOWN_BUG + - + ++ + a + + val x = + -- + a + + val x = + ! + a // KNOWN_BUG + + // postfixUnaryExpression // KNOWN_BUG + val x = // KNOWN_BUG + a++ // Line breaks are prohibited before postfix operators. + + val x = + a + ++ a // So this line should not be indented. + + val x = + a-- + + val x = + a + -- a // This line too. + + val x = + a!! + + val x = + f< // Line breaks are prohibited before type arguments. + A // KNOWN_BUG + >( // Line breaks are prohibited before function arguments. + x + )[ // Line breaks are prohibited before subscript. + x + ] + . // KNOWN_BUG + a + . + b + + // lambda arguments + val x = f() + @A + a@ + { // KNOWN_BUG + a() + } + + val x = f() @A a@ { // KNOWN_BUG + a() + } + + val x = f + @A + a@ + { // KNOWN_BUG + a() + } + + val x = f @A a@ { // KNOWN_BUG + a() + } + + val x = + f // Line breaks are prohibited before function arguments. + (1 + 1).also { print(it) } // So this line should not be indented. + + val x = + a // Line breaks are prohibited before function arguments. + [g()].forEach { print(it) } // So this line should not be indented. + + // parenthesizedExpression + val x = ( + a() + ) + + // collectionLiteral + @A(x = [ + 1, 2, 3, + 4, 5, 6, + 7, 8, 9 + ]) + a() + + // CharacterLiteral + val x = + 'a' + + val x = + '\'' + + val x = + '"' + + // stringLiteral + val x = "abc\"def${ + foo("abc") // KNOWN_BUG + }ghi${ // KNOWN_BUG + "aaa\${ // dollar sign cannot be escaped // KNOWN_BUG + 1 // KNOWN_BUG + }bbb" // KNOWN_BUG + }jkl" // KNOWN_BUG + + val x = """a + "b" // KNOWN_BUG + c${ + foo("""a // KNOWN_BUG + b // KNOWN_BUG + c // KNOWN_BUG + """) // KNOWN_BUG + }d // KNOWN_BUG + e // KNOWN_BUG + f${ + """aaa\${ // KNOWN_BUG + 1 // KNOWN_BUG + }bbb""" // KNOWN_BUG + }ghi""" // KNOWN_BUG + + val x = + a("'") + + // lambdaLiteral + val f: () -> Unit = { + a() + a() + a() + } + + val f: () -> Unit = { -> + a() // KNOWN_BUG + a() // KNOWN_BUG + a() + } + + val f: () -> Unit = { + -> // KNOWN_BUG + a() + a() // KNOWN_BUG + a() + } + + val f: () -> Unit = { x, + @A + y + : + Int, + ( + z1, + z2 + ) + : + AAA + -> + a() // KNOWN_BUG + a() // KNOWN_BUG + a() + } + + val f: () -> Unit = { + x, + y, + z + -> // KNOWN_BUG + a() + a() // KNOWN_BUG + a() + } + + val f = + fun + A + . + ( + x: Int + ) + : + AAA + where + A + : + A, + B // KNOWN_BUG + : // KNOWN_BUG + B { + a() // KNOWN_BUG + } // KNOWN_BUG + + // functionLiteral + val f = fun + { // KNOWN_BUG + a() + } + + // objectLiteral // KNOWN_BUG + val x = // KNOWN_BUG + object + : + A, + B // KNOWN_BUG + by + b, + C { + fun foo() { // KNOWN_BUG + } + } // KNOWN_BUG + + val x = object // KNOWN_BUG + { // KNOWN_BUG + fun foo() { + } + } + + // thisExpression // KNOWN_BUG + val x = // KNOWN_BUG + this + + val x = + this@Foo + + // superExpression + val x = + super + + val x = + super@Foo + + val x = + super< + Int // KNOWN_BUG + >@Foo + + // ifExpression // KNOWN_BUG + val x = if ( // KNOWN_BUG + a + ) { + a() + } + + val x = if + ( + a + ) + { + a() + } + + val x = if ( + a + ) + a() // KNOWN_BUG + + val x = if ( // KNOWN_BUG + a + ) + ; // KNOWN_BUG + + val x = if ( // KNOWN_BUG + a + ) { + a() + } else { + a() + } + + val x = if + ( + a + + ) + { + a() + } + else + { // KNOWN_BUG + a() + } + + val x = if ( // KNOWN_BUG + a + ) + a() // KNOWN_BUG + else // KNOWN_BUG + a() + + val x = if ( + a + ) + ; // KNOWN_BUG + else // KNOWN_BUG + ; + + val x = if ( + a + ) + else + ; + + // whenExpression + + val x = when ( + @A + val + a + = + a() + ) { + a(), b(), + c(), d() + -> + { // KNOWN_BUG + a() + } + + in // KNOWN_BUG + a() + -> + { // KNOWN_BUG + } + + is // KNOWN_BUG + A + -> + { // KNOWN_BUG + } + + a() // KNOWN_BUG + -> + a() + + else + -> + { // KNOWN_BUG + } + } + + val x = when + ( + a() + ) + { + a -> 1 + } + + // tryExpression + val x = try { + a() + } catch(@A e: E) { + a() + } catch(@A e: E) { + a() + } finally { + a() + } + + val x = try + { // KNOWN_BUG + a() + } + catch // KNOWN_BUG + ( + @A e: E + ) + { + a() + } + finally + { // KNOWN_BUG + a() + } + + val x = try { // KNOWN_BUG + a() + } finally { + a() + } + + val x = try + { // KNOWN_BUG + a() + } + finally // KNOWN_BUG + { // KNOWN_BUG + a() + } + + // jumpExpression // KNOWN_BUG + val x = // KNOWN_BUG + throw + a() + + val x = + return a() // Line breaks are prohibited after return. + + val x = + return // Line breaks are prohibited after return. + a() // So this line should not be indented. + + val x = + return@A a() // Line breaks are prohibited after return. + + val x = + return@A // Line breaks are prohibited after return. + a() // So this line should not be indented. + + val x = + continue + + val x = + continue@A + + val x = + break + + val x = + break@A + + // callableReference + val x = + Foo + :: + foo + + val x = + Foo + :: + class + + // typeModifier // KNOWN_BUG + val f: + suspend + () -> Unit + = + suspend + { // KNOWN_BUG + a() + } +} + +class Foo: Base { + // memberModifier + public + override + fun f() { + } // KNOWN_BUG + + public + lateinit + var x: Int + + + // visibilityModifier + override + public + fun f() { + } // KNOWN_BUG + + override + private + fun f() { + } // KNOWN_BUG + + override + internal + fun f() { + } // KNOWN_BUG + + override + protected + fun f() { + } // KNOWN_BUG + + // functionModifier + public + tailrec + infix + inline + fun A.f(): A { + return a() // KNOWN_BUG + } // KNOWN_BUG + + public + operator + fun A.unaryPlus(): A { + return a() // KNOWN_BUG + } // KNOWN_BUG + + public + suspend + fun foo() { + a() // KNOWN_BUG + } // KNOWN_BUG +} + +public + external + fun foo() { + a() // KNOWN_BUG +} // KNOWN_BUG + +class Foo { + // propertyModifier + public + const + val + x = 1 +} + +// inheritanceModifier +public + abstract + class Foo { + fun foo() { + } // KNOWN_BUG +} // KNOWN_BUG + +public + final + class Foo { + fun foo() { // KNOWN_BUG + } +} // KNOWN_BUG + +public + open + class Foo { + fun foo() { // KNOWN_BUG + } +} // KNOWN_BUG + +class Foo { + // parameterModifier + fun foo( + @A + crossinline + body: () -> Unit + ) { + } + + fun foo( + @A + noinline + body: () -> Unit + ) { + } + + fun foo( + @A + vararg + a: A + ) { + } + + // reificationModifier + inline fun < + @A + reified // KNOWN_BUG + T + > foo() { // KNOWN_BUG + a() + } +} + +// platformModifier +public + expect + class Foo { + fun foo() // KNOWN_BUG +} // KNOWN_BUG + +public + actual + class Foo { + fun foo() // KNOWN_BUG +} // KNOWN_BUG + +// various identifiers + +val `abc def 'ghi "aaa ${ aaa \` = // backquotes cannot be escaped + a() // KNOWN_BUG + +// UNICODE_CLASS_ND +val _0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९০১২৩৪৫৬৭৮৯੦੧੨੩੪੫੬੭੮੯૦૧૨૩૪૫૬૭૮૯୦୧୨୩୪୫୬୭୮୯௦௧௨௩௪௫௬௭௮௯౦౧౨౩౪౫౬౭౮౯೦೧೨೩೪೫೬೭೮೯൦൧൨൩൪൫൬൭൮൯෦෧෨෩෪෫෬෭෮෯๐๑๒๓๔๕๖๗๘๙໐໑໒໓໔໕໖໗໘໙༠༡༢༣༤༥༦༧༨༩၀၁၂၃၄၅၆၇၈၉႐႑႒႓႔႕႖႗႘႙០១២៣៤៥៦៧៨៩᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹0123456789𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩 = + 1 + +// Some characters are not supported while they are in the unicode category Nd. +// val _𐴰 = +// 1 +// val _𐴱 = +// 1 +// val _𐴲 = +// 1 +// val _𐴳 = +// 1 +// val _𐴴 = +// 1 +// val _𐴵 = +// 1 +// val _𐴶 = +// 1 +// val _𐴷 = +// 1 +// val _𐴸 = +// 1 +// val _𐴹 = +// 1 +val _𑁦 = + 1 +val _𑁧 = + 1 +val _𑁨 = + 1 +val _𑁩 = + 1 +val _𑁪 = + 1 +val _𑁫 = + 1 +val _𑁬 = + 1 +val _𑁭 = + 1 +val _𑁮 = + 1 +val _𑁯 = + 1 +val _𑃰 = + 1 +val _𑃱 = + 1 +val _𑃲 = + 1 +val _𑃳 = + 1 +val _𑃴 = + 1 +val _𑃵 = + 1 +val _𑃶 = + 1 +val _𑃷 = + 1 +val _𑃸 = + 1 +val _𑃹 = + 1 +val _𑄶 = + 1 +val _𑄷 = + 1 +val _𑄸 = + 1 +val _𑄹 = + 1 +val _𑄺 = + 1 +val _𑄻 = + 1 +val _𑄼 = + 1 +val _𑄽 = + 1 +val _𑄾 = + 1 +val _𑄿 = + 1 +val _𑇐 = + 1 +val _𑇑 = + 1 +val _𑇒 = + 1 +val _𑇓 = + 1 +val _𑇔 = + 1 +val _𑇕 = + 1 +val _𑇖 = + 1 +val _𑇗 = + 1 +val _𑇘 = + 1 +val _𑇙 = + 1 +val _𑋰 = + 1 +val _𑋱 = + 1 +val _𑋲 = + 1 +val _𑋳 = + 1 +val _𑋴 = + 1 +val _𑋵 = + 1 +val _𑋶 = + 1 +val _𑋷 = + 1 +val _𑋸 = + 1 +val _𑋹 = + 1 +val _𑑐 = + 1 +val _𑑑 = + 1 +val _𑑒 = + 1 +val _𑑓 = + 1 +val _𑑔 = + 1 +val _𑑕 = + 1 +val _𑑖 = + 1 +val _𑑗 = + 1 +val _𑑘 = + 1 +val _𑑙 = + 1 +val _𑓐 = + 1 +val _𑓑 = + 1 +val _𑓒 = + 1 +val _𑓓 = + 1 +val _𑓔 = + 1 +val _𑓕 = + 1 +val _𑓖 = + 1 +val _𑓗 = + 1 +val _𑓘 = + 1 +val _𑓙 = + 1 +val _𑙐 = + 1 +val _𑙑 = + 1 +val _𑙒 = + 1 +val _𑙓 = + 1 +val _𑙔 = + 1 +val _𑙕 = + 1 +val _𑙖 = + 1 +val _𑙗 = + 1 +val _𑙘 = + 1 +val _𑙙 = + 1 +val _𑛀 = + 1 +val _𑛁 = + 1 +val _𑛂 = + 1 +val _𑛃 = + 1 +val _𑛄 = + 1 +val _𑛅 = + 1 +val _𑛆 = + 1 +val _𑛇 = + 1 +val _𑛈 = + 1 +val _𑛉 = + 1 +val _𑜰 = + 1 +val _𑜱 = + 1 +val _𑜲 = + 1 +val _𑜳 = + 1 +val _𑜴 = + 1 +val _𑜵 = + 1 +val _𑜶 = + 1 +val _𑜷 = + 1 +val _𑜸 = + 1 +val _𑜹 = + 1 +val _𑣠 = + 1 +val _𑣡 = + 1 +val _𑣢 = + 1 +val _𑣣 = + 1 +val _𑣤 = + 1 +val _𑣥 = + 1 +val _𑣦 = + 1 +val _𑣧 = + 1 +val _𑣨 = + 1 +val _𑣩 = + 1 +val _𑱐 = + 1 +val _𑱑 = + 1 +val _𑱒 = + 1 +val _𑱓 = + 1 +val _𑱔 = + 1 +val _𑱕 = + 1 +val _𑱖 = + 1 +val _𑱗 = + 1 +val _𑱘 = + 1 +val _𑱙 = + 1 +// val _𑵐 = +// 1 +// val _𑵑 = +// 1 +// val _𑵒 = +// 1 +// val _𑵓 = +// 1 +// val _𑵔 = +// 1 +// val _𑵕 = +// 1 +// val _𑵖 = +// 1 +// val _𑵗 = +// 1 +// val _𑵘 = +// 1 +// val _𑵙 = +// 1 +// val _𑶠 = +// 1 +// val _𑶡 = +// 1 +// val _𑶢 = +// 1 +// val _𑶣 = +// 1 +// val _𑶤 = +// 1 +// val _𑶥 = +// 1 +// val _𑶦 = +// 1 +// val _𑶧 = +// 1 +// val _𑶨 = +// 1 +// val _𑶩 = +// 1 +val _𖩠 = + 1 +val _𖩡 = + 1 +val _𖩢 = + 1 +val _𖩣 = + 1 +val _𖩤 = + 1 +val _𖩥 = + 1 +val _𖩦 = + 1 +val _𖩧 = + 1 +val _𖩨 = + 1 +val _𖩩 = + 1 +val _𖭐 = + 1 +val _𖭑 = + 1 +val _𖭒 = + 1 +val _𖭓 = + 1 +val _𖭔 = + 1 +val _𖭕 = + 1 +val _𖭖 = + 1 +val _𖭗 = + 1 +val _𖭘 = + 1 +val _𖭙 = + 1 +val _𝟎 = + 1 +val _𝟏 = + 1 +val _𝟐 = + 1 +val _𝟑 = + 1 +val _𝟒 = + 1 +val _𝟓 = + 1 +val _𝟔 = + 1 +val _𝟕 = + 1 +val _𝟖 = + 1 +val _𝟗 = + 1 +val _𝟘 = + 1 +val _𝟙 = + 1 +val _𝟚 = + 1 +val _𝟛 = + 1 +val _𝟜 = + 1 +val _𝟝 = + 1 +val _𝟞 = + 1 +val _𝟟 = + 1 +val _𝟠 = + 1 +val _𝟡 = + 1 +val _𝟢 = + 1 +val _𝟣 = + 1 +val _𝟤 = + 1 +val _𝟥 = + 1 +val _𝟦 = + 1 +val _𝟧 = + 1 +val _𝟨 = + 1 +val _𝟩 = + 1 +val _𝟪 = + 1 +val _𝟫 = + 1 +val _𝟬 = + 1 +val _𝟭 = + 1 +val _𝟮 = + 1 +val _𝟯 = + 1 +val _𝟰 = + 1 +val _𝟱 = + 1 +val _𝟲 = + 1 +val _𝟳 = + 1 +val _𝟴 = + 1 +val _𝟵 = + 1 +val _𝟶 = + 1 +val _𝟷 = + 1 +val _𝟸 = + 1 +val _𝟹 = + 1 +val _𝟺 = + 1 +val _𝟻 = + 1 +val _𝟼 = + 1 +val _𝟽 = + 1 +val _𝟾 = + 1 +val _𝟿 = + 1 +// val _𞅀 = +// 1 +// val _𞅁 = +// 1 +// val _𞅂 = +// 1 +// val _𞅃 = +// 1 +// val _𞅄 = +// 1 +// val _𞅅 = +// 1 +// val _𞅆 = +// 1 +// val _𞅇 = +// 1 +// val _𞅈 = +// 1 +// val _𞅉 = +// 1 +// val _𞋰 = +// 1 +// val _𞋱 = +// 1 +// val _𞋲 = +// 1 +// val _𞋳 = +// 1 +// val _𞋴 = +// 1 +// val _𞋵 = +// 1 +// val _𞋶 = +// 1 +// val _𞋷 = +// 1 +// val _𞋸 = +// 1 +// val _𞋹 = +// 1 +val _𞥐 = + 1 +val _𞥑 = + 1 +val _𞥒 = + 1 +val _𞥓 = + 1 +val _𞥔 = + 1 +val _𞥕 = + 1 +val _𞥖 = + 1 +val _𞥗 = + 1 +val _𞥘 = + 1 +val _𞥙 = + 1 + +// UNICODE_CLASS_LL +// UNICODE_CLASS_LM +// UNICODE_CLASS_LO +// UNICODE_CLASS_LT +// UNICODE_CLASS_LU +val aʰªDžA = + 1 + +// UNICODE_CLASS_NL (rejected for some reason) +// val ᛮ = +// 1 diff --git a/test/sample.kt b/test/sample.kt index 7dd47b5..ddd5f28 100644 --- a/test/sample.kt +++ b/test/sample.kt @@ -56,8 +56,8 @@ fun main(args: Array) { fun max(a: Int, b: Int): Int { if (a > b) - return a - else + return a // KNOWN_BUG + else // KNOWN_BUG return b } @@ -83,16 +83,16 @@ fun getStringLength(obj: Any): Int? { fun main(args: Array) { for (arg in args) - print(arg) + print(arg) // KNOWN_BUG } for (i in args.indices) - print(args[i]) + print(args[i]) // KNOWN_BUG -fun main(args: Array) { +fun main(args: Array) { // KNOWN_BUG var i = 0 while (i < args.size) - print(args[i++]) + print(args[i++]) // KNOWN_BUG } fun cases(obj: Any) { @@ -106,21 +106,21 @@ fun cases(obj: Any) { } if (x in 1..y-1) - print("OK") + print("OK") // KNOWN_BUG -if (x !in 0..array.lastIndex) - print("Out") +if (x !in 0..array.lastIndex) // KNOWN_BUG + print("Out") // KNOWN_BUG -for (x in 1..5) - print(x) +for (x in 1..5) // KNOWN_BUG + print(x) // KNOWN_BUG -for (name in names) - println(name) +for (name in names) // KNOWN_BUG + println(name) // KNOWN_BUG -if (text in names) // names.contains(text) is called - print("Yes") +if (text in names) // names.contains(text) is called // KNOWN_BUG + print("Yes") // KNOWN_BUG -names.filter { it.startsWith("A") } +names.filter { it.startsWith("A") } // KNOWN_BUG .sortedBy { it } .map { it.toUpperCase() } .forEach { print(it) } @@ -227,7 +227,7 @@ inline fun 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 // KNOWN_BUG } } @@ -364,15 +364,15 @@ 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 // KNOWN_BUG -var setterWithAnnotation: Any? = null -@Inject set // annotate the setter with Inject +var setterWithAnnotation: Any? = null // KNOWN_BUG + @Inject set // annotate the setter with Inject // KNOWN_BUG -var counter = 0 // the initializer value is written directly to the backing field +var counter = 0 // the initializer value is written directly to the backing field // KNOWN_BUG set(value) { if (value >= 0) - field = value + field = value // KNOWN_BUG } val isEmpty: Boolean @@ -446,9 +446,9 @@ 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 // KNOWN_BUG -internal val baz = 6 // visible inside the same module +internal val baz = 6 // visible inside the same module // KNOWN_BUG open class Outer { private val a = 1 @@ -555,12 +555,12 @@ fun > sort(list: List) { } fun cloneWhenGreater(list: List, threshold: T): List -where T : Comparable, -T : Cloneable { - return list.filter { it > threshold }.map { it.clone() } -} + where T : Comparable, + T : Cloneable { // KNOWN_BUG + return list.filter { it > threshold }.map { it.clone() } // KNOWN_BUG +} // KNOWN_BUG -enum class Direction { +enum class Direction { // KNOWN_BUG NORTH, SOUTH, WEST, EAST } @@ -608,13 +608,12 @@ fun countClicks(window: JComponent) { object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { clickCount++ - } + } // KNOWN_BUG override fun mouseEntered(e: MouseEvent) { enterCount++ - } - } - ) + } // KNOWN_BUG + }) // ... } @@ -652,8 +651,8 @@ infix fun Int.shl(x: Int): Int { fun asList(vararg ts: T): List { val result = ArrayList() for (t in ts) // ts is an Array - result.add(t) - return result + result.add(t) // KNOWN_BUG + return result // KNOWN_BUG } tailrec fun findFixPoint(x: Double = 1.0): Double @@ -674,8 +673,8 @@ val result = lock(lock, ::toBeSynchronized) fun List.map(transform: (T) -> R): List { val result = arrayListOf() for (item in this) - result.add(transform(item)) - return result + result.add(transform(item)) // KNOWN_BUG + return result // KNOWN_BUG } val doubled = ints.map { it -> it * 2 } @@ -688,9 +687,9 @@ max(strings, { a, b -> a.length < b.length fun max(collection: Collection, less: (T, T) -> Boolean): T? { var max: T? = null for (it in collection) - if (max == null || less(max, it)) - max = it - return max + if (max == null || less(max, it)) // KNOWN_BUG + max = it // KNOWN_BUG + return max // KNOWN_BUG } val sum: (Int, Int) -> Int = { x, y -> x + y } From e8969a6214c77885b381dfe5a873a7e41d2a55f2 Mon Sep 17 00:00:00 2001 From: taku0 Date: Sun, 3 Nov 2019 18:05:56 +0900 Subject: [PATCH 2/9] Move lexical level routines to kotlin-mode-lexer.el --- kotlin-mode-lexer.el | 188 ++++++++++++++++++++++++++++++++++++++- kotlin-mode.el | 205 ++++--------------------------------------- 2 files changed, 204 insertions(+), 189 deletions(-) diff --git a/kotlin-mode-lexer.el b/kotlin-mode-lexer.el index 29576be..b34f02e 100644 --- a/kotlin-mode-lexer.el +++ b/kotlin-mode-lexer.el @@ -1,8 +1,10 @@ ;;; kotlin-mode-lexer.el --- Major mode for kotlin, lexer -*- lexical-binding: t; -*- +;; Copyright © 2015 Shodai Yokoyama ;; Copyright © 2019 taku0 -;; Author: taku0 (http://github.com/taku0) +;; Author: Shodai Yokoyama (quantumcars@gmail.com) +;; taku0 (http://github.com/taku0) ;; Keywords: languages ;; Package-Requires: ((emacs "24.3")) @@ -357,6 +359,190 @@ If PARSER-STATE is given, it is used instead of (syntax-ppss)." (t nil)))) +;; Syntax table + +(defvar kotlin-mode-syntax-table + (let ((st (make-syntax-table))) + + ;; Strings + (modify-syntax-entry ?\" "\"" st) + (modify-syntax-entry ?\' "\"" st) + (modify-syntax-entry ?` "\"" st) + + ;; `_' and `@' as being a valid part of a symbol + (modify-syntax-entry ?_ "_" st) + (modify-syntax-entry ?@ "_" st) + + ;; b-style comment + (modify-syntax-entry ?/ ". 124b" st) + (modify-syntax-entry ?* ". 23n" st) + (modify-syntax-entry ?\n "> b" st) + (modify-syntax-entry ?\r "> b" st) + st)) + +;; Line level movements and predicates + +(defun kotlin-mode--prev-line () + "Moves up to the nearest non-empty line." + (beginning-of-line) + ;; `forward-comment' skips spaces and newlines as well. + (forward-comment (- (point)))) + +(defun kotlin-mode--line-begins-p (pattern) + "Return whether the current line begins with the given PATTERN. + +Ignore spaces at the beginning of the line." + (save-excursion + (beginning-of-line) + (looking-at (format "^[ \t]*%s" pattern)))) + +(defun kotlin-mode--line-begins-excluding-comment-p (pattern) + "Return whether the current line begins with the given PATTERN. + +Ignore comments and spaces at the beginning of the line." + (let ((line-end-position (line-end-position))) + (save-excursion + (beginning-of-line) + (when (nth 4 (syntax-ppss)) + ;; If the point is inside a comment, goto the beginning of the + ;; comment. + (goto-char (nth 8 (syntax-ppss)))) + (forward-comment (point-max)) + (when (< line-end-position (point)) + (goto-char line-end-position)) + (looking-at pattern)))) + +(defun kotlin-mode--line-ends-p (pattern) + "Return whether the current line ends with the given PATTERN. + +Ignore spaces at the end of the line." + (save-excursion + (beginning-of-line) + (looking-at (format ".*%s[ \t]*$" pattern)))) + +(defun kotlin-mode--line-ends-excluding-comment-p (pattern) + "Return whether the current line ends with the given PATTERN. + +Ignore comments at the end of the line." + (let ((end-position + ;; last point where is neither spaces nor comment + (max + (line-beginning-position) + (save-excursion + (end-of-line) + (when (nth 4 (syntax-ppss)) + ;; If the point is inside a comment, goto the beginning + ;; of the comment. + (goto-char (nth 8 (syntax-ppss)))) + (forward-comment (- (point))) + (point))))) + (save-excursion + (save-restriction + (beginning-of-line) + (narrow-to-region (point) end-position) + (looking-at (format ".*%s$" pattern)))))) + +(defun kotlin-mode--line-contains-p (pattern) + "Return whether the current line contains the given PATTERN." + (save-excursion + (beginning-of-line) + (looking-at (format ".*%s.*" pattern)))) + +;; Line continuation + +(defun kotlin-mode--line-continuation () + "Return whether this line continues a statement in the previous line" + (let ((case-fold-search nil)) + (cond + ;; Tokens that end a statement + ((save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment-p + (rx (group + (or + ".*" + (seq word-start + (or "return" "continue" "break") + word-end)))))) + nil) + + ;; Modifiers, that cannot end a statement. + ((save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment-p + (rx (group (seq word-start + (or + "public" "private" "protected" + "internal" "enum" "sealed" "annotation" + "data" "inner" "tailrec" "operator" "inline" + "infix" "external" "suspend" "override" + "abstract" "final" "open" "const" "lateinit" + "vararg" "noinline" "crossinline" "reified" + "expect" "actual") + word-end))))) + t) + + ;; Tokens that start a statement that have lower priority than modifiers. + ((kotlin-mode--line-begins-excluding-comment-p + (rx (group (seq word-start + (or + "public" "private" "protected" "internal" + "enum" "sealed" "annotation" "data" "inner" + "tailrec" "operator" "inline" "infix" + "external" "override" "abstract" "final" + "open" "const" "lateinit" "vararg" "noinline" + "crossinline" "reified" "expect" "actual" + "package" "import" "interface" "val" "var" + "typealias" "constructor" "companion" "init" + "is" "in" "out" "for" "while" "do") + word-end)))) + nil) + + ((and (kotlin-mode--line-begins-excluding-comment-p + (rx (group (seq word-start "class" word-end)))) + (not + (save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment-p (rx (group "::")))))) + nil) + + ;; Tokens that cannot end a statement + ((save-excursion + (kotlin-mode--prev-line) + (kotlin-mode--line-ends-excluding-comment-p + (rx (group + (or + (any "-%*./:=+&|<@") + "->" + (seq word-start + "as?") + (seq "!is" + "!in" + word-end) + (seq word-start + (or + "package" "import" "class" "interface" "fun" + "object" "val" "var" "typealias" "constructor" + "by" "companion" "init" "where" "if" "else" + "when" "try" "catch" "finally" "for" "do" "while" + "throw" "as" "is" "in" "out") + word-end)))))) + t) + + ;; Tokens that cannot start a statement + ((kotlin-mode--line-begins-excluding-comment-p + (rx (group + (or + (any ".:=" + (seq word-start "as?") + (seq word-start + (or "get" "set" "as" "by" "where") + word-end))))) + t) + + (t nil)))) + (provide 'kotlin-mode-lexer) ;;; kotlin-mode-lexer.el ends here diff --git a/kotlin-mode.el b/kotlin-mode.el index cd13e79..549eed2 100644 --- a/kotlin-mode.el +++ b/kotlin-mode.el @@ -1,11 +1,12 @@ ;;; kotlin-mode.el --- Major mode for kotlin -*- lexical-binding: t; -*- -;; Copyright © 2015 Shodai Yokoyama +;; Copyright © 2015 Shodai Yokoyama ;; Author: Shodai Yokoyama (quantumcars@gmail.com) ;; Version: 2.0.0 ;; Keywords: languages ;; Package-Requires: ((emacs "24.3")) +;; URL: https://github.com/Emacs-Kotlin-Mode-Maintainers/kotlin-mode ;; 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 @@ -22,7 +23,7 @@ ;;; Commentary: -;; +;; Major-mode for Kotlin programming language. ;;; Code: @@ -59,6 +60,8 @@ :type 'string :group 'kotlin) +;; REPL + (defun kotlin-do-and-repl-focus (f &rest args) (apply f args) (pop-to-buffer kotlin-repl-buffer)) @@ -126,6 +129,8 @@ (pop-to-buffer kotlin-repl-buffer)) +;; Keymap + (defvar kotlin-mode-map (let ((map (make-keymap))) (define-key map (kbd "C-c C-z") 'kotlin-repl) @@ -136,27 +141,6 @@ map) "Keymap for kotlin-mode") -(defvar kotlin-mode-syntax-table - (let ((st (make-syntax-table))) - - ;; Strings - (modify-syntax-entry ?\" "\"" st) - (modify-syntax-entry ?\' "\"" st) - (modify-syntax-entry ?` "\"" st) - - ;; `_' and `@' as being a valid part of a symbol - (modify-syntax-entry ?_ "_" st) - (modify-syntax-entry ?@ "_" st) - - ;; b-style comment - (modify-syntax-entry ?/ ". 124b" st) - (modify-syntax-entry ?* ". 23n" st) - (modify-syntax-entry ?\n "> b" st) - (modify-syntax-entry ?\r "> b" st) - st)) - -(defconst kotlin-mode--closing-brackets '(?} ?\) ?\])) - ;;; Font Lock (defconst kotlin-mode--misc-keywords @@ -328,171 +312,14 @@ t) (kotlin-mode--match-interpolation limit)))))) -(defun kotlin-mode--prev-line () - "Moves up to the nearest non-empty line." - (beginning-of-line) - ;; `forward-comment' skips spaces and newlines as well. - (forward-comment (- (point)))) - -(defun kotlin-mode--line-begins (pattern) - "Return whether the current line begins with the given PATTERN. - -Ignore spaces at the beginning of the line." - (save-excursion - (beginning-of-line) - (looking-at (format "^[ \t]*%s" pattern)))) - -(defun kotlin-mode--line-begins-excluding-comment (pattern) - "Return whether the current line begins with the given PATTERN. - -Ignore comments and spaces at the beginning of the line." - (let ((line-end-position (line-end-position))) - (save-excursion - (beginning-of-line) - (when (nth 4 (syntax-ppss)) - ;; If the point is inside a comment, goto the beginning of the - ;; comment. - (goto-char (nth 8 (syntax-ppss)))) - (forward-comment (point-max)) - (when (< line-end-position (point)) - (goto-char line-end-position)) - (looking-at pattern)))) - -(defun kotlin-mode--line-ends (pattern) - "Return whether the current line ends with the given PATTERN. - -Ignore spaces at the end of the line." - (save-excursion - (beginning-of-line) - (looking-at (format ".*%s[ \t]*$" pattern)))) - -(defun kotlin-mode--line-ends-excluding-comment (pattern) - "Return whether the current line ends with the given PATTERN. - -Ignore comments at the end of the line." - (let ((end-position - ;; last point where is neither spaces nor comment - (max - (line-beginning-position) - (save-excursion - (end-of-line) - (when (nth 4 (syntax-ppss)) - ;; If the point is inside a comment, goto the beginning - ;; of the comment. - (goto-char (nth 8 (syntax-ppss)))) - (forward-comment (- (point))) - (point))))) - (save-excursion - (save-restriction - (beginning-of-line) - (narrow-to-region (point) end-position) - (looking-at (format ".*%s$" 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" - (let ((case-fold-search nil)) - (cond - ;; Tokens that end a statement - ((save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment - (rx (group - (or - ".*" - (seq word-start - (or "return" "continue" "break") - word-end)))))) - nil) - - ;; Modifiers, that cannot end a statement. - ((save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment - (rx (group (seq word-start - (or - "public" "private" "protected" - "internal" "enum" "sealed" "annotation" - "data" "inner" "tailrec" "operator" "inline" - "infix" "external" "suspend" "override" - "abstract" "final" "open" "const" "lateinit" - "vararg" "noinline" "crossinline" "reified" - "expect" "actual") - word-end))))) - t) - - ;; Tokens that start a statement that have lower priority than modifiers. - ((kotlin-mode--line-begins-excluding-comment - (rx (group (seq word-start - (or - "public" "private" "protected" "internal" - "enum" "sealed" "annotation" "data" "inner" - "tailrec" "operator" "inline" "infix" - "external" "override" "abstract" "final" - "open" "const" "lateinit" "vararg" "noinline" - "crossinline" "reified" "expect" "actual" - "package" "import" "interface" "val" "var" - "typealias" "constructor" "companion" "init" - "is" "in" "out" "for" "while" "do") - word-end)))) - nil) - - ((and (kotlin-mode--line-begins-excluding-comment - (rx (group (seq word-start "class" word-end)))) - (not - (save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment (rx (group "::")))))) - nil) - - ;; Tokens that cannot end a statement - ((save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment - (rx (group - (or - (any "-%*./:=+&|<@") - "->" - (seq word-start - "as?") - (seq "!is" - "!in" - word-end) - (seq word-start - (or - "package" "import" "class" "interface" "fun" - "object" "val" "var" "typealias" "constructor" - "by" "companion" "init" "where" "if" "else" - "when" "try" "catch" "finally" "for" "do" "while" - "throw" "as" "is" "in" "out") - word-end)))))) - t) - - ;; Tokens that cannot start a statement - ((kotlin-mode--line-begins-excluding-comment - (rx (group - (or - (any ".:=" - (seq word-start "as?") - (seq word-start - (or "get" "set" "as" "by" "where") - word-end))))) - t) - - (t nil)))) +;; Indentation (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) + ((kotlin-mode--in-comment-block-p) (- (current-indentation) 1)) (t (current-indentation)))) @@ -669,10 +496,10 @@ fun foo() { (defun kotlin-mode--add-indent (counter val) (cl-incf (oref counter indent) val)) -(defun kotlin-mode--finished (counter) +(defun kotlin-mode--finished-p (counter) (oref counter finished)) -(defun kotlin-mode--in-comment-block () +(defun kotlin-mode--in-comment-block-p () "Return whether the cursor is within a standard comment block structure of the following format: /** @@ -681,18 +508,18 @@ fun foo() { (save-excursion (let ((in-comment-block nil) (keep-going (and - (not (kotlin-mode--line-begins "\\*\\*+/")) - (not (kotlin-mode--line-begins "/\\*")) + (not (kotlin-mode--line-begins-p "\\*\\*+/")) + (not (kotlin-mode--line-begins-p "/\\*")) (nth 4 (syntax-ppss))))) (while keep-going (kotlin-mode--prev-line) (cond - ((kotlin-mode--line-begins "/\\*") + ((kotlin-mode--line-begins-p "/\\*") (setq keep-going nil) (setq in-comment-block t)) ((bobp) (setq keep-going nil)) - ((kotlin-mode--line-contains "\\*/") + ((kotlin-mode--line-contains-p "\\*/") (setq keep-going nil)))) in-comment-block))) @@ -834,6 +661,8 @@ If it does not exist, will return nil." (defun kotlin-mode--beginning-of-buffer-indent () (indent-line-to 0)) +;; the Kotlin mode + ;;;###autoload (define-derived-mode kotlin-mode prog-mode "Kotlin" "Major mode for editing Kotlin." From 0a41f8b7fd4492e8d74f7acd2aaefdd24b93cf4d Mon Sep 17 00:00:00 2001 From: taku0 Date: Sun, 3 Nov 2019 18:41:29 +0900 Subject: [PATCH 3/9] Fix ckeckdoc errors --- kotlin-mode-lexer.el | 4 +-- kotlin-mode.el | 76 +++++++++++++++++++++++++++----------------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/kotlin-mode-lexer.el b/kotlin-mode-lexer.el index b34f02e..8aa2634 100644 --- a/kotlin-mode-lexer.el +++ b/kotlin-mode-lexer.el @@ -383,7 +383,7 @@ If PARSER-STATE is given, it is used instead of (syntax-ppss)." ;; Line level movements and predicates (defun kotlin-mode--prev-line () - "Moves up to the nearest non-empty line." + "Move up to the nearest non-empty line." (beginning-of-line) ;; `forward-comment' skips spaces and newlines as well. (forward-comment (- (point)))) @@ -451,7 +451,7 @@ Ignore comments at the end of the line." ;; Line continuation (defun kotlin-mode--line-continuation () - "Return whether this line continues a statement in the previous line" + "Return whether this line continues a statement in the previous line." (let ((case-fold-search nil)) (cond ;; Tokens that end a statement diff --git a/kotlin-mode.el b/kotlin-mode.el index 549eed2..5ca0ddb 100644 --- a/kotlin-mode.el +++ b/kotlin-mode.el @@ -63,17 +63,22 @@ ;; REPL (defun kotlin-do-and-repl-focus (f &rest args) + "Call function F with ARGS and pop to the REPL buffer." (apply f args) (pop-to-buffer kotlin-repl-buffer)) (defun kotlin-send-region (start end) - "Send current region to Kotlin interpreter." + "Send current region to Kotlin interpreter. + +START and END define region within current buffer." (interactive "r") (comint-send-region kotlin-repl-buffer start end) (comint-send-string kotlin-repl-buffer "\n")) (defun kotlin-send-region-and-focus (start end) - "Send current region to Kotlin interpreter and switch to it." + "Send current region to Kotlin interpreter and switch to it. + +START and END define region within current buffer." (interactive "r") (kotlin-do-and-repl-focus 'kotlin-send-region start end)) @@ -100,6 +105,7 @@ (kotlin-do-and-repl-focus 'kotlin-send-block)) (defun kotlin-send-line () + "Send current line to Kotlin interpreter." (interactive) (kotlin-send-region (line-beginning-position) @@ -124,8 +130,8 @@ kotlin-args-repl)) (set (make-local-variable 'comint-preoutput-filter-functions) - (cons (lambda (string) - (replace-regexp-in-string "\x1b\\[.[GJK]" "" string)) nil))) + (list (lambda (string) + (replace-regexp-in-string "\x1b\\[.[GJK]" "" string))))) (pop-to-buffer kotlin-repl-buffer)) @@ -139,7 +145,7 @@ (define-key map (kbd "C-c C-c") 'kotlin-send-block) (define-key map (kbd "C-c C-b") 'kotlin-send-buffer) map) - "Keymap for kotlin-mode") + "Keymap for `kotlin-mode'.") ;;; Font Lock @@ -296,9 +302,14 @@ ;; String interpolation (kotlin-mode--match-interpolation 0 font-lock-variable-name-face t)) - "Default highlighting expression for `kotlin-mode'") + "Default highlighting expression for `kotlin-mode'.") (defun kotlin-mode--match-interpolation (limit) + "Find template expression before LIMIT. + +Template expressions must be propertized by `kotlin-mode--syntax-propertize'. +If a template expression is found, move to that point, set `match-data', +and return non-nil. Return nil otherwise." (let ((pos (next-single-char-property-change (point) 'kotlin-property--interpolation @@ -315,8 +326,9 @@ ;; Indentation (defun kotlin-mode--base-indentation () - "Return the indentation level of the current line based on brackets only, - i.e. ignoring 'continuation' indentation." + "Return the indentation level of the current line based on brackets only. + +Ignore 'continuation' indentation." (cond ((kotlin-mode--line-continuation) (- (current-indentation) kotlin-tab-width)) ((kotlin-mode--in-comment-block-p) @@ -348,15 +360,16 @@ 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." + "Count the brackets on the current line backwards. + +Scan from the cursor position toward the beginning of the line. +Increment the count +1 for open-brackets, -1 for close-brackets. + +When the overall count exceeds zero, mark the COUNTER finished, set +indentation, and return immediately. 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)))) @@ -441,7 +454,7 @@ 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 +indentation level. If bracket count is at zero, we are not indenting close brackets. Example: @@ -454,12 +467,11 @@ fun foo() { } } -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. +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 the 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 @@ -488,23 +500,28 @@ fun foo() { (oset counter use-base nil))) (defun kotlin-mode--add-count (counter val) + "Increment count of COUNTER by VAL." (cl-incf (oref counter count) val)) (defun kotlin-mode--subtract-count (counter val) + "Decrement count of COUNTER by VAL." (cl-decf (oref counter count) val)) (defun kotlin-mode--add-indent (counter val) + "Increment indentation of COUNTER by VAL." (cl-incf (oref counter indent) val)) (defun kotlin-mode--finished-p (counter) + "Return t if COUNTER is finished." (oref counter finished)) (defun kotlin-mode--in-comment-block-p () - "Return whether the cursor is within a standard comment block structure - of the following format: - /** - * Description here - */" + "Return whether the cursor is within a standard comment block structure. + +Return non-nil if the comment has the following format: +/** + * Description here + */" (save-excursion (let ((in-comment-block nil) (keep-going (and @@ -659,6 +676,7 @@ If it does not exist, will return nil." (defun kotlin-mode--beginning-of-buffer-indent () + "Indent current line assuming the point is at the beginning of the buffer." (indent-line-to 0)) ;; the Kotlin mode From 115a7dd7e348c3c9ebd9f4ca66c17edf4449b237 Mon Sep 17 00:00:00 2001 From: taku0 Date: Thu, 7 Nov 2019 19:09:28 +0900 Subject: [PATCH 4/9] Rewrite indentation logic --- kotlin-mode-indent.el | 1955 +++++++++++++++++++++++++++++++ kotlin-mode-lexer.el | 2399 +++++++++++++++++++++++++++++++++++--- kotlin-mode.el | 435 +------ test/kotlin-mode-test.el | 125 +- test/pathological.kt | 1245 +++++++++++++------- test/sample.kt | 89 +- 6 files changed, 5213 insertions(+), 1035 deletions(-) create mode 100644 kotlin-mode-indent.el diff --git a/kotlin-mode-indent.el b/kotlin-mode-indent.el new file mode 100644 index 0000000..0ca7802 --- /dev/null +++ b/kotlin-mode-indent.el @@ -0,0 +1,1955 @@ +;;; kotlin-mode-indent.el --- Major mode for kotlin, indentation -*- lexical-binding: t; -*- + +;; Copyright (C) 2019 taku0 + +;; Authors: taku0 (http://github.com/taku0) +;; Keywords: languages +;; Package-Requires: ((emacs "24.3")) +;; Version: 0.0.1 +;; URL: https://github.com/Emacs-Kotlin-Mode-Maintainers/kotlin-mode + +;; 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 . + +;;; Commentary: + +;; Routines for indentation + +;;; Code: + +(require 'rx) +(require 'cl-lib) + +(require 'kotlin-mode-lexer) + +;;; Customizations + +(defcustom kotlin-mode--basic-offset 4 + "Amount of indentation for block contents. + +Example: + +class Foo { + func foo() {} // offset of this line +}" + :type 'integer + :group 'kotlin + :safe 'integerp) + +(defcustom kotlin-mode--parenthesized-expression-offset 4 + "Amount of indentation inside parentheses and square brackets. + +Example: + +foo( + 1 // offset of this line +)" + :type 'integer + :group 'kotlin + :safe 'integerp) + +(defcustom kotlin-mode--multiline-statement-offset 4 + "Amount of indentation for continuations of expressions. + +Example: + +val x = 1 + + 2 // offset of this line" + :type 'integer + :group 'kotlin + :safe 'integerp) + +(defcustom kotlin-mode--prepend-asterisk-to-comment-line t + "Automatically insert a asterisk to each comment line if non-nil. + +Example: if the enter key is pressed when the point is after A below, + +/* + * A + */ + +an asterisk is inserted to the newline: + +/* + * A + * + */" + :type 'boolean + :group 'kotlin + :safe 'booleanp) + +(defcustom kotlin-mode--insert-space-after-asterisk-in-comment t + "Automatically insert a space after asterisk in comment if non-nil. + +Example: if an asterisk is inserted before A below, + +/* +A + */ + +a space is inserted after asterisk: + +/* + * A + */" + :type 'boolean + :group 'kotlin + :safe 'booleanp) + +(defcustom kotlin-mode--auto-close-multiline-comment t + "If non-nil, `indent-new-comment-line' automatically close multiline comment. + +Example: when the enter key is pressed after unclosed comment below, + +/** + +a closing delimiter is inserted automatically: + +/** + * // cursor is here + */" + :type 'boolean + :group 'kotlin + :safe 'booleanp) + +(defcustom kotlin-mode--fix-comment-close t + "Fix \"* /\" in incomplete multiline comment to \"*/\" if non-nil. + +Example: + +/* + * + * // when a slash is inserted here + +/* + * + */ // it become like this + + +/* + * + * / // rather than like this." + :type 'boolean + :group 'kotlin + :safe 'booleanp) + +(defcustom kotlin-mode--break-line-before-comment-close t + "If non-nil, break line before the closing delimiter of multiline comments. + +Example: if line break is inserted before A below, + +/** A */ + +it becomes like this: + +/** + * A + */ + +rather than like this: + +/** + * A */" + :type 'boolean + :group 'kotlin + :safe 'booleanp) + +(defcustom kotlin-mode--indent-nonempty-line-in-multiline-string nil + "If non-nil, indent nonempty line in multiline string. + +`indent-according-to-mode' is no-op otherwise." + :type 'boolean + :group 'kotlin + :safe 'booleanp) + +(defcustom kotlin-mode--highlight-anchor nil + "Highlight anchor point for indentation if non-nil. + +Intended for debugging." + :type 'boolean + :group 'kotlin + :safe 'booleanp) + +;;; Constants and variables + +(defconst kotlin-mode--statement-parent-tokens + '(\( \[ { anonymous-function-parameter-arrow when-expression-arrow + bare-else \(\)-before-control-structure-body \; implicit-\;) + "Parent tokens for statements. + +Parent tokens are tokens before the beginning of statements.") + +(defconst kotlin-mode--expression-parent-tokens + (append kotlin-mode--statement-parent-tokens + '(\, "where" string-chunk-before-template-expression)) + "Parent tokens for expressions. + +Parent tokens are tokens before the beginning of expressions.") + +(defvar-local kotlin-mode--anchor-overlay nil) +(defvar-local kotlin-mode--anchor-overlay-timer nil) + +;;; Indentation struct + +(defclass kotlin-mode--indentation () + ((position :initarg :position + :type number + :accessor kotlin-mode--indentation-position + :documentation "the position of the anchor point, such as +the start of the previous line or the start of the class declaration.") + (offset :initarg :offset + :type number + :initform 0 + :accessor kotlin-mode--indentation-offset + :documentation "the offset from the anchor point. For +example, when indenting the first line of a class body, its anchor +point is the start of the class declaration and its offset is +`kotlin-mode--basic-offset'.")) + "Indentation.") + +;;; Indentation logic + +(defun kotlin-mode--indent-line () + "Indent the current line." + (let* ((indentation (save-excursion (kotlin-mode--calculate-indent))) + (indentation-column + (save-excursion + (goto-char (kotlin-mode--indentation-position indentation)) + (+ (current-column) (kotlin-mode--indentation-offset indentation)))) + (current-indent + (save-excursion (back-to-indentation) (current-column)))) + (if (<= (current-column) current-indent) + ;; The point is on the left margin. + ;; Move the point to the new leftmost non-comment char. + (indent-line-to indentation-column) + ;; Keep current relative position from leftmost non-comment char. + (save-excursion (indent-line-to indentation-column))) + (when kotlin-mode--highlight-anchor + (kotlin-mode--highlight-anchor indentation)))) + +(defun kotlin-mode--calculate-indent () + "Return the indentation of the current line." + (back-to-indentation) + + (let ((parser-state (syntax-ppss))) + (cond + ((nth 4 parser-state) + ;; If the 4th element of `(syntax-ppss)' is non-nil, the point is on + ;; the 2nd or following lines of a multiline comment, because: + ;; + ;; - The 4th element of `(syntax-ppss)' is nil on the comment starter. + ;; - We have called `back-to-indentation`. + (kotlin-mode--calculate-indent-of-multiline-comment)) + + ((eq (nth 3 parser-state) t) + (kotlin-mode--calculate-indent-of-multiline-string)) + + ((looking-at "//") + (kotlin-mode--calculate-indent-of-single-line-comment)) + + (t + (kotlin-mode--calculate-indent-of-code))))) + +(defun kotlin-mode--calculate-indent-of-multiline-comment () + "Return the indentation of the current line inside a multiline comment." + (back-to-indentation) + (let ((comment-beginning-position (nth 8 (syntax-ppss))) + (starts-with-asterisk (eq (char-after) ?*))) + (forward-line -1) + (back-to-indentation) + (cond + ;; 2nd line of the comment + ((<= (point) comment-beginning-position) + ;; The point was on the 2nd line of the comment. + (goto-char comment-beginning-position) + (forward-char) + ;; If there are extra characters or spaces after asterisks, align with + ;; the first non-space character or end of line. Otherwise, align with + ;; the first asterisk. + (when (and + (looking-at + (rx (seq (zero-or-more "*") (one-or-more (not (any "\n*")))))) + (not (and kotlin-mode--prepend-asterisk-to-comment-line + starts-with-asterisk))) + (skip-chars-forward "*") + (skip-syntax-forward " ")) + (make-instance 'kotlin-mode--indentation :position (point) :offset 0)) + + ;; The point was on the 3rd or following lines of the comment. + + ;; Before closing delimiter + ((= (save-excursion + ;; Back to the original line. + (forward-line) + (back-to-indentation) + (point)) + (save-excursion + ;; Goto just before the closing delimiter. + (goto-char comment-beginning-position) + (if (forward-comment 1) + (progn + ;; slash + (backward-char) + (skip-chars-backward "*") + (point)) + -1))) + ;; Before the closing delimiter. Align with the first asterisk of the + ;; opening delimiter. + ;; + ;; TODO: If there are multiple asterisks on the closing + ;; delimiter, and the middle lines have no leading asterisks, + ;; align with the slash of the opening delimiter + ;; + ;; Example: + ;; + ;; /********* + ;; aaa + ;; **********/ + (goto-char comment-beginning-position) + (forward-char) + (make-instance 'kotlin-mode--indentation :position (point) :offset 0)) + + ;; Otherwise, align with a non-empty preceding line. + + ;; The previous line is empty + ((and (bolp) (eolp)) + ;; Seek a non-empty-line. + (while (and (bolp) (eolp) (not (bobp))) + (forward-line -1)) + (forward-line) + (kotlin-mode--calculate-indent-of-multiline-comment)) + + ;; The previous line is not empty + (t + ;; Align to this line. + (make-instance 'kotlin-mode--indentation :position (point) :offset 0))))) + +(defun kotlin-mode--calculate-indent-of-multiline-string () + "Return the indentation of the current line inside a multiline string." + (back-to-indentation) + (let ((string-beginning-position + (save-excursion (kotlin-mode--beginning-of-string)))) + (if (looking-at "\"\"\"") + ;; Indenting the closing delimiter. Align with the start of containing + ;; expression with extra indentation. + ;; + ;; Example: + ;; + ;; val someString = """ + ;; Hello. + ;; """ + ;; + ;; When aligning to opening delimiter, align without offset: + ;; foo(""" + ;; aaa + ;; """) + ;; + ;; Possible alternative indentation: + ;; val someString = """ + ;; Hello. + ;; """ + (progn + (goto-char string-beginning-position) + (let ((indentation + (kotlin-mode--calculate-indent-of-expression + kotlin-mode--multiline-statement-offset))) + (if (= (kotlin-mode--indentation-position indentation) + string-beginning-position) + (make-instance 'kotlin-mode--indentation + :position string-beginning-position + :offset 0) + indentation))) + ;; Other than the closing delimiter. + (if (and (not (eolp)) + (not kotlin-mode--indent-nonempty-line-in-multiline-string)) + ;; The user prefers to keep indentations inside multiline string. + (make-instance 'kotlin-mode--indentation :position (point) :offset 0) + ;; The user prefers to indent lines inside multiline string. + (beginning-of-line) + (backward-char) + (kotlin-mode--goto-non-template-expression-bol) + (back-to-indentation) + (cond + ;; 2nd line of string + ((<= (point) string-beginning-position) + ;; The point was on the 2nd line of the string. Align + ;; with the start of containing expression with extra + ;; indentation. + (goto-char string-beginning-position) + (kotlin-mode--calculate-indent-of-expression + kotlin-mode--multiline-statement-offset)) + + ;; The point was on the 3rd or following lines of the string. + ;; Align with a non-empty preceding line. + + ;; The previous line is empty + ((and (bolp) (eolp)) + ;; Seek a non-empty-line. + (while (and (bolp) (eolp) (not (bobp))) + (forward-line -1)) + (forward-line) + (kotlin-mode--calculate-indent-of-multiline-string)) + + ;; The previous line is not empty + (t + ;; Align to this line. + (make-instance 'kotlin-mode--indentation + :position (point) + :offset 0))))))) + +(defun kotlin-mode--goto-non-template-expression-bol () + "Back to the beginning of line that is not inside a template expression." + (let ((chunk-beginning-position (nth 8 (syntax-ppss))) + (matching-bracket t)) + (while (and matching-bracket + (< (line-beginning-position) chunk-beginning-position)) + (setq matching-bracket + (get-text-property + chunk-beginning-position 'kotlin-property--matching-bracket)) + (when matching-bracket + (goto-char matching-bracket) + (setq chunk-beginning-position (nth 8 (syntax-ppss))))) + (beginning-of-line))) + +(defun kotlin-mode--calculate-indent-of-single-line-comment () + "Return the indentation of the current line inside a single-line comment." + (cond + ;; When the comment is beginning of the buffer, indent to the column 0. + ((save-excursion + (beginning-of-line) + (bobp)) + (make-instance 'kotlin-mode--indentation :position (point-min) :offset 0)) + + ;; If the previous line is also a single-line comment, align with it. + ((save-excursion + (forward-line -1) + (skip-syntax-forward " ") + (looking-at "//")) + (forward-line -1) + (skip-syntax-forward " ") + (make-instance 'kotlin-mode--indentation :position (point) :offset 0)) + + ;; Otherwise, indent like a expression. + (t + (kotlin-mode--calculate-indent-of-code)))) + +(defun kotlin-mode--calculate-indent-of-code () + "Return the indentation of the current line outside comments or string." + (back-to-indentation) + (let* ((previous-token (save-excursion (kotlin-mode--backward-token))) + (previous-type (kotlin-mode--token-type previous-token)) + (previous-text (kotlin-mode--token-text previous-token)) + (next-token (save-excursion (kotlin-mode--forward-token))) + (next-type (kotlin-mode--token-type next-token)) + (next-text (kotlin-mode--token-text next-token)) + (next-is-on-current-line + (<= (kotlin-mode--token-start next-token) (line-end-position)))) + (cond + ;; Beginning of the buffer + ((eq previous-type 'outside-of-buffer) + (make-instance 'kotlin-mode--indentation :position (point-min) :offset 0)) + + ;; Before } on the current line. + ;; Align with the head of the statement. + ((and next-is-on-current-line (eq next-type '})) + (goto-char (kotlin-mode--token-end next-token)) + (backward-list) + (kotlin-mode--calculate-indent-after-open-curly-brace 0)) + + ;; Before ) or ] on the current line. + ;; Align with the head of the expression. + ((and next-is-on-current-line + (memq next-type '(\) \)-before-control-structure-body \]))) + (goto-char (kotlin-mode--token-end next-token)) + (backward-list) + (kotlin-mode--calculate-indent-of-expression 0)) + + ;; Before close angle bracket for generics (>) on the current line. + ((and next-is-on-current-line + (equal next-text ">") + (save-excursion + (goto-char (kotlin-mode--token-end next-token)) + (eq (kotlin-mode--token-type (kotlin-mode--backward-token-or-list)) + '<>))) + (goto-char (kotlin-mode--token-end next-token)) + (kotlin-mode--backward-token-or-list) + (kotlin-mode--calculate-indent-of-expression 0)) + + ;; Before end of a template expression on the current line. + ((and next-is-on-current-line + (eq next-type 'string-chunk-after-template-expression)) + (goto-char (get-text-property + (kotlin-mode--token-start next-token) + 'kotlin-property--matching-bracket)) + ;; Skip "${" + (forward-char 2) + (kotlin-mode--backward-string-chunk) + (kotlin-mode--calculate-indent-after-beginning-of-template-expression + 0)) + + ;; Before { on the current line + ((and next-is-on-current-line (eq next-type '{)) + (kotlin-mode--calculate-indent-after-open-curly-brace 0)) + + ;; Before "else" on the current line + ((and next-is-on-current-line (equal next-text "else")) + (kotlin-mode--calculate-indent-of-else)) + + ;; Before "where" on the current line + ((and next-is-on-current-line (equal next-text "where")) + (kotlin-mode--find-parent-and-align-with-next + kotlin-mode--statement-parent-tokens + kotlin-mode--multiline-statement-offset)) + + ;; Before "catch" or "finally" on the current line + ((and next-is-on-current-line (member next-text '("catch" "finally"))) + (kotlin-mode--find-parent-and-align-with-next + kotlin-mode--statement-parent-tokens + 0 + '() + '("catch" "finally") + 0)) + + ;; Before "=" on the current line + ;; + ;; fun foo(): T + ;; where + ;; T: A, + ;; T: B + ;; = bar() + ((and next-is-on-current-line (equal next-text "=")) + (kotlin-mode--find-parent-and-align-with-next + (remove '\, (remove "where" kotlin-mode--expression-parent-tokens)) + kotlin-mode--multiline-statement-offset)) + + ;; Before "," on the current line + ((and next-is-on-current-line (eq next-type '\,)) + (kotlin-mode--calculate-indent-of-prefix-comma)) + + ;; After "," + ((eq previous-type '\,) + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--calculate-indent-after-comma)) + + ;; After "catch". Align with "try". + ((equal previous-text "catch") + (kotlin-mode--find-parent-and-align-with-next + kotlin-mode--statement-parent-tokens + kotlin-mode--multiline-statement-offset)) + + ;; After { + ((eq previous-type '{) + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--calculate-indent-after-open-curly-brace + kotlin-mode--basic-offset)) + + ;; After ( or [ + ((memq previous-type '(\( \[)) + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--calculate-indent-of-expression + kotlin-mode--parenthesized-expression-offset + kotlin-mode--parenthesized-expression-offset)) + + ;; After open angle bracket for generics (<) + ((and (equal previous-text "<") + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (eq (kotlin-mode--token-type (kotlin-mode--forward-token-or-list)) + '<>))) + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--calculate-indent-of-expression + kotlin-mode--parenthesized-expression-offset + kotlin-mode--parenthesized-expression-offset)) + + ;; After beginning of a template expression + ((eq previous-type 'string-chunk-before-template-expression) + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--calculate-indent-after-beginning-of-template-expression + kotlin-mode--parenthesized-expression-offset)) + + ;; After ; or implicit-\; + ((memq previous-type '(\; implicit-\;)) + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--calculate-indent-after-semicolon)) + + ;; After "->" of lambda parameters + ((eq previous-type 'anonymous-function-parameter-arrow) + (goto-char (cdr (kotlin-mode--find-containing-brackets + (kotlin-mode--token-start previous-token)))) + (kotlin-mode--calculate-indent-after-open-curly-brace + kotlin-mode--basic-offset)) + + ;; Before "->" of lambda parameters on the current line + ((and next-is-on-current-line + (eq next-type 'anonymous-function-parameter-arrow)) + (if (eq (kotlin-mode--token-type (kotlin-mode--backward-token-or-list)) + '{) + (kotlin-mode--calculate-indent-after-open-curly-brace + kotlin-mode--basic-offset) + (goto-char (cdr (kotlin-mode--find-containing-brackets + (kotlin-mode--token-start next-token)))) + (kotlin-mode--align-with-next-token (kotlin-mode--forward-token)))) + + ;; After "->" of when expression + ((eq previous-type 'when-expression-arrow) + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--find-parent-and-align-with-next + (cl-remove-if + (lambda (e) + (memq e '(when-expression-arrow + bare-else \(\)-before-control-structure-body))) + kotlin-mode--statement-parent-tokens) + kotlin-mode--basic-offset)) + + ;; Before "->" of when expression on the current line + ((and next-is-on-current-line (eq next-type 'when-expression-arrow)) + (if (equal (kotlin-mode--token-text previous-token) "else") + (kotlin-mode--align-with-token + previous-token + kotlin-mode--basic-offset) + (kotlin-mode--find-parent-and-align-with-next + (cl-remove-if + (lambda (e) + (memq e '(when-expression-arrow + bare-else \(\)-before-control-structure-body))) + kotlin-mode--statement-parent-tokens) + kotlin-mode--basic-offset))) + + ;; After "where" + ;; + ;; class Foo + ;; where + ;; T: A // align with "where" with offset + ;; + ;; class Foo where + ;; T: A // align with "class" with offset + ((equal previous-text "where") + (goto-char (kotlin-mode--token-start previous-token)) + (if (kotlin-mode--bol-other-than-comments-p) + (kotlin-mode--align-with-token + previous-token + kotlin-mode--multiline-statement-offset) + (kotlin-mode--find-parent-and-align-with-next + kotlin-mode--statement-parent-tokens + kotlin-mode--multiline-statement-offset))) + + ;; After if, when, for, or while + ((member previous-text '("if" "when" "for" "while")) + (kotlin-mode--find-parent-and-align-with-next + kotlin-mode--statement-parent-tokens + kotlin-mode--multiline-statement-offset)) + + ;; After do + ((equal previous-text "do") + (kotlin-mode--align-with-token + previous-token + (if (equal next-text "while") 0 kotlin-mode--basic-offset))) + + ;; After else + ((equal previous-text "else") + (kotlin-mode--align-with-token + previous-token + kotlin-mode--basic-offset)) + + ;; After "if ()", "while ()", or "for ()" + ((eq previous-type '\)-before-control-structure-body) + (kotlin-mode--backward-token-or-list) + (kotlin-mode--find-parent-and-align-with-next + kotlin-mode--statement-parent-tokens + kotlin-mode--basic-offset + '() + '("else") + kotlin-mode--basic-offset)) + + ;; Before ; on the current line + ((and next-is-on-current-line (eq next-type '\;)) + (kotlin-mode--find-parent-and-align-with-next + (remove '\; (remove 'implicit-\; kotlin-mode--statement-parent-tokens)) + 0 + '(\; implicit-\;))) + + ;; Before "in" on the current line + ((and next-is-on-current-line (equal next-text "in")) + (kotlin-mode--calculate-indent-before-in)) + + ;; After "in" + ;; + ;; Examples: + ;; for ( + ;; x + ;; in + ;; xs + ;; ) {} + ;; + ;; for (x + ;; in + ;; xs) {[]} + ;; + ;; when (x) { + ;; in + ;; xs -> y in + ;; ys + ;; in + ;; zs -> 1 + ;; } + ;; + ;; Foo< + ;; in + ;; X, + ;; @AAA in + ;; X + ;; > + ;; + ;; val x = 1 in + ;; array + ((equal previous-text "in") + (let* ((type-and-parent + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--find-parent-of-in))) + (type (car type-and-parent))) + (if (or (memq type '(for <>)) + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (not (kotlin-mode--bol-other-than-comments-p)))) + ;; for-statement and type parameters, or "in" is not at + ;; the beginning of the line. + (progn + ;; Indent like a expression + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--calculate-indent-of-expression + kotlin-mode--multiline-statement-offset)) + ;; After "in" at the beginning of the line. + ;; + ;; Example: + ;; + ;; when (x) { + ;; in + ;; xs -> 1 + ;; in + ;; ys -> 1 + ;; } + ;; + ;; Pretend a semicolon exists before "in" + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--align-with-next-token + (kotlin-mode--backward-token) + kotlin-mode--multiline-statement-offset)))) + + ;; Before "while" on the current line + ((and next-is-on-current-line (equal next-text "while")) + (let ((do-token (save-excursion (kotlin-mode--find-do-for-while)))) + (if do-token + (kotlin-mode--align-with-token do-token) + (kotlin-mode--calculate-indent-after-semicolon)))) + + ;; Inside annotation + ((or (and (eq previous-type 'annotation) + (or + ;; @file + ;; : // ← here + (eq next-type ':) + ;; @file:A + ;; .B // ← here + ;; @file:A + ;; // ← here + (member next-text '("." "<")))) + ;; @file: + ;; [ // ← here + (and (eq previous-type ':) + (eq next-type '\[) + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (eq (kotlin-mode--token-type (kotlin-mode--backward-token)) + 'annotation))) + ;; @file: + ;; A // ← here + ;; @file:A + ;; . + ;; B // ← here + ;; @file:A + ;; < + ;; B + ;; > // ← here + (and (or (eq next-type 'atom) (equal next-text ">")) + (eq (kotlin-mode--token-type + (save-excursion + (kotlin-mode--extend-annotation-token-backward + next-token))) + 'annotation))) + (let ((start-of-annotation + (cond + ((eq previous-type 'annotation) + (kotlin-mode--token-start previous-token)) + + ((eq previous-type ':) + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--token-start (kotlin-mode--backward-token)))) + + (t (kotlin-mode--token-start + (save-excursion (kotlin-mode--extend-annotation-token-backward + next-token)))))) + (start-of-previous-line + (save-excursion + (kotlin-mode--backward-token-or-list t) + (kotlin-mode--goto-non-comment-bol-with-same-nesting-level t) + (point)))) + (goto-char (max start-of-annotation start-of-previous-line)) + (kotlin-mode--align-with-current-line + (if (< start-of-annotation start-of-previous-line) + ;; 3rd or following lines of the annotation. + ;; Align with previous line without offset. + 0 + ;; 2nd line of the annotation. + ;; Align with the start of the annotation with offset. + kotlin-mode--multiline-statement-offset)))) + + ;; After annotations or labels + ((memq previous-type '(annotation label)) + (goto-char (kotlin-mode--token-start previous-token)) + ;; Align with preceding annotation or label at the beginning of + ;; a line, or indent like an expression if the annotations are in + ;; middle of a line. + (let ((token previous-token)) + (while (and (memq (kotlin-mode--token-type token) '(annotation label)) + (not (kotlin-mode--bol-other-than-comments-p))) + (setq token (kotlin-mode--backward-token))) + (unless (memq (kotlin-mode--token-type token) '(annotation label)) + ;; The annotations are in middle of a line. + (goto-char (kotlin-mode--token-end token)))) + (if (kotlin-mode--bol-other-than-comments-p) + ;; The annotations are at the beginning of a line. + (kotlin-mode--align-with-current-line) + ;; The annotations are in middle of a line. + (kotlin-mode--align-with-next-token + (kotlin-mode--find-parent-of-expression)))) + + ;; Otherwise, it is continuation of the previous line + (t + (goto-char (kotlin-mode--token-end previous-token)) + (kotlin-mode--backward-token-or-list) + (kotlin-mode--calculate-indent-of-expression + kotlin-mode--multiline-statement-offset))))) + +(defun kotlin-mode--find-parent-and-align-with-next + (parents + &optional + offset + stop-at-eol-token-types + stop-at-bol-token-types + bol-offset) + "Find the parent and return indentation based on it. + +A parent is, for example, the open bracket of the containing block or +semicolon of the preceding statement. + +PARENTS is a list of token types that precedes the expression or the statement. +OFFSET is the offset. If it is omitted, assumed to be 0. +See `kotlin-mode--backward-sexps-until' for the details of +STOP-AT-EOL-TOKEN-TYPES and STOP-AT-BOL-TOKEN-TYPES. +If scanning stops at STOP-AT-EOL-TOKEN-TYPES, align with the next token with +BOL-OFFSET. +If scanning stops at STOP-AT-BOL-TOKEN-TYPES, align with that token with +BOL-OFFSET. +If STOP-AT-BOL-TOKEN-TYPES or STOP-AT-BOL-TOKEN-TYPES is the symbol +`any', it matches all tokens. +The point is assumed to be on the previous line." + (save-excursion + (let* ((parent (kotlin-mode--backward-sexps-until + parents + stop-at-eol-token-types + stop-at-bol-token-types)) + (parent-end (kotlin-mode--token-end parent)) + (stopped-at-parent + (or (memq (kotlin-mode--token-type parent) parents) + (member (kotlin-mode--token-text parent) parents) + (eq (kotlin-mode--token-type parent) 'outside-of-buffer))) + (stopped-at-eol + (and + (not stopped-at-parent) + stop-at-eol-token-types + (or + (eq stop-at-eol-token-types 'any) + (memq (kotlin-mode--token-type parent) + stop-at-eol-token-types) + (member (kotlin-mode--token-text parent) + stop-at-eol-token-types))))) + (if stopped-at-parent + (kotlin-mode--align-with-next-token parent offset) + (when stopped-at-eol + (goto-char parent-end) + (forward-comment (point-max))) + (kotlin-mode--align-with-current-line bol-offset))))) + +(defun kotlin-mode--calculate-indent-of-expression + (&optional + offset + bol-offset) + "Return indentation of the current expression. + +the point is assumed to be on the previous line. + +OFFSET is the offset. If it is omitted, assumed to be 0. +If scanning stops at eol, align with the next token with BOL-OFFSET." + (save-excursion + (let* ((pos (point)) + (parent-of-previous-line + (save-excursion + (kotlin-mode--goto-non-comment-bol-with-same-nesting-level) + (kotlin-mode--backward-token))) + (parent-of-expression (kotlin-mode--find-parent-of-expression))) + ;; Skip annotations + (goto-char (kotlin-mode--token-end parent-of-expression)) + (kotlin-mode--forward-annotations) + (kotlin-mode--goto-non-comment-bol-with-same-nesting-level) + (when (or (< (point) (kotlin-mode--token-end parent-of-expression)) + (< pos (point))) + (goto-char (kotlin-mode--token-end parent-of-expression))) + (setq parent-of-expression (kotlin-mode--backward-token)) + (if (<= (kotlin-mode--token-start parent-of-previous-line) + (kotlin-mode--token-start parent-of-expression)) + ;; let x = + ;; 1 + // indenting here + ;; 2 + + ;; 3 + ;; + ;; Aligns with the parent of the expression with offset. + (kotlin-mode--align-with-next-token parent-of-expression offset) + ;; let x = + ;; 1 + + ;; 2 + // indenting here + ;; 3 // or here + ;; + ;; Aligns with the previous line. + (kotlin-mode--align-with-next-token parent-of-previous-line + bol-offset))))) + +(defun kotlin-mode--forward-annotations () + "Skip forward comments, whitespaces, labels, and annotations." + (let (token) + (while (progn + (forward-comment (point-max)) + (setq token (kotlin-mode--forward-token)) + (memq (kotlin-mode--token-type token) '(annotation label)))) + (goto-char (kotlin-mode--token-start token)))) + +(defun kotlin-mode--backward-annotations () + "Skip backward comments, whitespaces, labels, and annotations." + (let (token) + (while (progn + (forward-comment (- (point))) + (setq token (kotlin-mode--backward-token)) + (memq (kotlin-mode--token-type token) '(annotation label)))) + (goto-char (kotlin-mode--token-end token)))) + +(defun kotlin-mode--calculate-indent-after-open-curly-brace (offset) + "Return indentation after open curly braces. + +Assuming the point is on the open brace. +OFFSET is the offset of the contents. +This function is also used for close curly braces." + ;; If the statement is multiline expression, aligns with the start of + ;; the line on which the open brace is: + ;; + ;; foo() + ;; .then { x -> + ;; foo() + ;; foo() + ;; } + ;; .then { x -> + ;; foo() + ;; foo() + ;; } + ;; + ;; rather than + ;; + ;; foo() + ;; .then { x -> + ;; foo() + ;; foo() + ;; } + ;; .then { x -> + ;; foo() + ;; foo() + ;; } + ;; + ;; Otherwise, aligns with the start of the whole statement: + ;; + ;; class Foo: + ;; Bar { + ;; fun foo() {} + ;; } + ;; + ;; rather than + ;; + ;; class Foo: + ;; Bar { + ;; fun foo() {} + ;; } + ;; + ;; POSSIBLE IMPROVEMENT: those behavior should be configurable. + ;; + ;; FIXME: curly braces after binary operator is a part of a + ;; multiline expression: + ;; + ;; class Foo: + ;; Bar by xs someInfixOperator { x -> + ;; // this is not the body of the class. + ;; } { + ;; // The body of the class. + ;; } + (let* ((brace-type-and-keyword-token (kotlin-mode--curly-brace-type)) + (brace-type (car brace-type-and-keyword-token)) + (keyword-token (cdr brace-type-and-keyword-token))) + (cond + ((memq brace-type + '(object-literal + lambda-literal + if-expression + try-expression + catch-block + finally-block + when-expression)) + (goto-char (kotlin-mode--token-start keyword-token)) + (kotlin-mode--backward-annotations) + (unless (eq brace-type 'lambda-literal) + (forward-comment (point-max))) + (kotlin-mode--calculate-indent-of-expression offset offset)) + + ((eq brace-type 'else-block) + (goto-char (kotlin-mode--token-start keyword-token)) + (kotlin-mode--calculate-indent-of-else offset)) + + ((eq brace-type 'when-entry) + (goto-char (kotlin-mode--token-start keyword-token)) + (if (equal (kotlin-mode--token-text + (save-excursion (kotlin-mode--backward-token))) + "else") + (kotlin-mode--align-with-token (kotlin-mode--backward-token) offset) + (kotlin-mode--find-parent-and-align-with-next + (remove 'when-expression-arrow kotlin-mode--statement-parent-tokens) + offset))) + + ((memq brace-type '(getter setter)) + (goto-char (kotlin-mode--token-start keyword-token)) + (kotlin-mode--try-backward-modifiers) + (kotlin-mode--align-with-next-token (kotlin-mode--backward-token) offset)) + + (t + (goto-char (kotlin-mode--token-start keyword-token)) + (kotlin-mode--find-parent-and-align-with-next + kotlin-mode--statement-parent-tokens + offset))))) + +(defun kotlin-mode--curly-brace-type () + "Return information about curly brace. + +Return a cons (TYPE . KEYWORD-TOKEN) where TYPE is a symbol, and KEYWORD-TOKEN +is the keyword token: +- TYPE: class-declaration, KEYWORD-TOKEN: \"class\" +- TYPE: interface-declaration, KEYWORD-TOKEN: \"interface\" +- TYPE: object-declaration, KEYWORD-TOKEN: \"object\" +- TYPE: enum-entry, KEYWORD-TOKEN: identifier +- TYPE: object-literal, KEYWORD-TOKEN: \"object\" +- TYPE: anonymous-initializer, KEYWORD-TOKEN: \"init\" +- TYPE: function-declaration, KEYWORD-TOKEN: \"fun\" +- TYPE: getter, KEYWORD-TOKEN: \"get\" +- TYPE: setter, KEYWORD-TOKEN: \"set\" +- TYPE: secondary-constructor, KEYWORD-TOKEN: \"constructor\" +- TYPE: for-statement, KEYWORD-TOKEN: \"for\" +- TYPE: while-statement, KEYWORD-TOKEN: \"while\" +- TYPE: do-while-statement, KEYWORD-TOKEN: \"do\" +- TYPE: if-expression, KEYWORD-TOKEN: \"if\" +- TYPE: else-block, KEYWORD-TOKEN: \"else\" +- TYPE: when-entry, KEYWORD-TOKEN: \"->\" +- TYPE: try-expression, KEYWORD-TOKEN: \"try\" +- TYPE: catch-block, KEYWORD-TOKEN: \"catch\" +- TYPE: finally-block, KEYWORD-TOKEN: \"finally\" +- TYPE: lambda-literal, KEYWORD-TOKEN: \"{\" +- TYPE: when-expression, KEYWORD-TOKEN: \"when\" + +Assuming the point is just before the open curly brace." + ;; Other braces may appear between the keyword and the open brace: + ;; + ;; class Foo: Bar by if (aaa) {bbb} else {ccc} { + ;; } + ;; + ;; So we maintain stack of braces and match them with keywords. + (save-excursion + (let* ((pos (point)) + (open-brace-points (list pos)) + (containing-brackets (kotlin-mode--find-containing-brackets (point))) + previous-token + previous-type + previous-text + brace-type + result) + ;; Special handling for enumEntry. + (goto-char (cdr containing-brackets)) + (when (eq (car containing-brackets) '{) + (let ((token (kotlin-mode--backward-sexps-until + (append kotlin-mode--statement-parent-tokens + '("enum"))))) + (when (equal (kotlin-mode--token-text token) "enum") + (goto-char pos) + (when (eq (kotlin-mode--token-type + (kotlin-mode--backward-sexps-until + kotlin-mode--statement-parent-tokens)) + '{) + ;; This must be a enumEntry. + (goto-char pos) + (setq previous-token (kotlin-mode--backward-token-or-list)) + (when (eq (kotlin-mode--token-type previous-token) '\(\)) + (setq previous-token (kotlin-mode--backward-token-or-list))) + (setq result (cons 'enum-entry previous-token)))))) + (unless result + ;; Other cases + (goto-char pos) + (while open-brace-points + (setq previous-token (kotlin-mode--backward-token-or-list)) + (setq previous-type (kotlin-mode--token-type previous-token)) + (setq previous-text (kotlin-mode--token-text previous-token)) + + (cond + ;; whenEntry. + ;; Stop immediately. + ((eq previous-type 'when-expression-arrow) + (setq result (cons 'when-entry previous-token)) + (setq open-brace-points nil)) + + ;; Hit beginning of the statement without seeing keyword. + ;; Assume it is a lambda expression and stop the loop. + ((or (eq previous-type 'outside-of-buffer) + (memq previous-type kotlin-mode--statement-parent-tokens) + (member previous-text kotlin-mode--statement-parent-tokens)) + (setq result (cons 'lambda-literal + (progn + (goto-char pos) + (kotlin-mode--forward-token)))) + (setq open-brace-points nil)) + + ;; Found another braces. + ;; Push it to the stack and continue the loop. + ((eq previous-type '{}) + (push (point) open-brace-points)) + + ;; Simple cases. + ;; Those cannot be a part of an expression. + ;; Stop immediately. + ((member previous-text + '("class" "interface" "init" "fun" "get" "set" + "for" "while" "do")) + (setq brace-type (assoc-default + previous-text + '(("class" . class-declaration) + ("interface" . interface-declaration) + ("init" . anonymous-initializer) + ;; FIXME handle anonymous function + ("fun" . function-declaration) + ("get" . getter) + ("set" . setter) + ("for" . for-statement) + ("while" . while-statement) + ("do" . do-while-statement)))) + (setq result (cons brace-type previous-token)) + (setq open-brace-points nil)) + + ;; Semi-simple cases. + ;; Those can be a part of an expression and the braces are mandatory. + ((member previous-text '("try" "catch" "finally" "when")) + (pop open-brace-points) + (when (null open-brace-points) + (setq brace-type (assoc-default + previous-text + '(("try" . try-expression) + ("catch" . catch-block) + ("finally" . finally-block) + ("when" . when-expression)))) + (setq result (cons brace-type previous-token)))) + + ;; If and else. + ;; Those can be a part of an expression and the braces are optional. + ((member previous-text '("if" "else")) + (save-excursion + (goto-char (kotlin-mode--token-end previous-token)) + (when (equal previous-text "if") + (kotlin-mode--forward-token-or-list)) + + (forward-comment (point-max)) + (when (= (point) (car open-brace-points)) + (pop open-brace-points) + (when (null open-brace-points) + (setq brace-type + (if (equal previous-text "if") + 'if-expression + 'else-block)) + (setq result (cons brace-type previous-token)))))) + + ;; Object declaration or object literal. + ((equal previous-text "object") + (setq brace-type + (cond + ((save-excursion + (equal (kotlin-mode--token-text + (kotlin-mode--backward-token)) + "companion")) + 'object-declaration) + ((save-excursion + (goto-char (kotlin-mode--token-end previous-token)) + (eq (kotlin-mode--token-type (kotlin-mode--forward-token)) + 'atom)) + 'object-declaration) + (t 'object-literal))) + (pop open-brace-points) + (when (or (null open-brace-points) + (eq brace-type 'object-declaration)) + (setq result (cons brace-type previous-token)) + (setq open-brace-points nil))) + + ;; Primary constructor or secondary constructor. + ((equal previous-text "constructor") + (let ((parent-token + (kotlin-mode--backward-sexps-until + (append kotlin-mode--statement-parent-tokens '("class"))))) + (if (equal (kotlin-mode--token-text parent-token) "class") + ;; primary constructor + (setq result (cons 'class-declaration parent-token)) + ;; secondary constructor + (setq result (cons 'secondary-constructor previous-token))) + (setq open-brace-points nil)))))) + result))) + +(defun kotlin-mode--calculate-indent-of-else (&optional offset) + "Return indentation for \"else\" token with OFFSET." + ;; Align it with "if" token. + ;; Since if-else can be nested, we keep nesting level to find matching "if". + (let ((nesting-level 1) + previous-token) + (while (< 0 nesting-level) + (setq previous-token (kotlin-mode--backward-token-or-list)) + (cond + ((equal (kotlin-mode--token-text previous-token) "else") + (setq nesting-level (1+ nesting-level))) + + ((equal (kotlin-mode--token-text previous-token) "if") + (if (save-excursion + ;; "else if" on the same line + (and + (equal (kotlin-mode--token-text (kotlin-mode--backward-token)) + "else") + (= (progn (kotlin-mode--goto-non-comment-bol) + (point)) + (progn (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--goto-non-comment-bol) + (point))))) + ;; Skip "else-if" without changing nesting-level. + (kotlin-mode--backward-token) + (setq nesting-level (1- nesting-level)))) + + ((memq (kotlin-mode--token-type previous-token) + '({ \( outside-of-buffer)) + ;; Unmatched if-else + (setq nesting-level 0))))) + (kotlin-mode--forward-token) + (kotlin-mode--calculate-indent-of-expression (or offset 0) (or offset 0))) + +(defun kotlin-mode--calculate-indent-of-prefix-comma () + "Return indentation for prefix comma. + +Example: + +foo( 1 + , 2 + , 3 +) + +class Foo: A + , B + , C + +class D + where A: AAA + , B: BBB + , C: CCC + +This is also known as Utrecht-style in the Haskell community." + (let ((parent (kotlin-mode--find-parent-of-list-element t))) + (if (eq (kotlin-mode--token-type parent) '\,) + ;; The comma was the 2nd or the following commas. + ;; Align with the previous comma. + (kotlin-mode--align-with-current-line) + ;; The comma was the 1st comma. + ;; Align with the end of the parent. + (goto-char (kotlin-mode--token-end parent)) + (backward-char) + (make-instance 'kotlin-mode--indentation :position (point) :offset 0)))) + +(defun kotlin-mode--calculate-indent-after-comma () + "Return indentation after comma. + +Assuming the point is on the comma." + (kotlin-mode--align-with-next-token + (kotlin-mode--find-parent-of-list-element nil))) + +(defun kotlin-mode--find-parent-of-list-element (&optional utrecht-style) + "Move point backward to the parent token of the comma under the point. + +If UTRECHT-STYLE is non-nil, stop at a comma at bol. Otherwise, stop at a +comma at eol." + ;; Various examples: + ;; + ;; val x = foo( // simple case + ;; 1, + ;; 2, + ;; 3 + ;; ) + ;; + ;; val x = xs[ + ;; 1, + ;; 2, + ;; 3 + ;; ] + ;; + ;; class Foo: A1(), + ;; B1 by object: C1, + ;; C2 { + ;; }, + ;; @AAA D1 + ;; where A: B, + ;; A: C, + ;; A: D { + ;; } + ;; + ;; when (foo) { + ;; object: B1, + ;; B2 { + ;; }, + ;; object: B3 by object: B4 { + ;; }, + ;; B5 { + ;; } -> + ;; 1 + ;; } + ;; + ;; fun foo(x: T): Int + ;; where + ;; T: A, + ;; T: B, + ;; T: C { + ;; } + ;; + ;; val A.foo: Int + ;; where + ;; T: A, + ;; T: B, + ;; T: C + ;; get() = aaa + ;; + ;; enum class Foo(x: Int) { + ;; A(1), B(2) {}, + ;; C(3) + ;; } + (let ((parent (kotlin-mode--backward-sexps-until + (append + (remove '\, kotlin-mode--expression-parent-tokens) + '("<" : {})) + (if utrecht-style nil '(\,)) + (if utrecht-style '(\,) nil)))) + (cond + ((equal (kotlin-mode--token-text parent) "<") + (if (save-excursion + (eq (kotlin-mode--token-type (kotlin-mode--forward-token-or-list)) + '<>)) + parent + (kotlin-mode--find-parent-of-list-element utrecht-style))) + + ((eq (kotlin-mode--token-type parent) '{}) + (let* ((brace-type-and-keyword-token (kotlin-mode--curly-brace-type)) + (keyword-token (cdr brace-type-and-keyword-token))) + (goto-char (kotlin-mode--token-start keyword-token))) + (kotlin-mode--find-parent-of-list-element utrecht-style)) + + ((eq (kotlin-mode--token-type parent) ':) + (if (kotlin-mode--colon-before-delegation-specifiers-p parent) + parent + (kotlin-mode--find-parent-of-list-element utrecht-style))) + + (t + parent)))) + +(defun kotlin-mode--find-parent-of-expression () + "Move point backward to the parent token of the expression under the point." + ;; TODO Unify with kotlin-mode--find-parent-of-list-element + (let ((parent (kotlin-mode--backward-sexps-until + (append kotlin-mode--expression-parent-tokens '("<" : {}))))) + (cond + ((equal (kotlin-mode--token-text parent) "<") + (if (save-excursion + (eq (kotlin-mode--token-type (kotlin-mode--forward-token-or-list)) + '<>)) + parent + (kotlin-mode--find-parent-of-expression))) + + ((eq (kotlin-mode--token-type parent) '{}) + (let* ((brace-type-and-keyword-token (kotlin-mode--curly-brace-type)) + (keyword-token (cdr brace-type-and-keyword-token))) + (goto-char (kotlin-mode--token-start keyword-token))) + (kotlin-mode--find-parent-of-expression)) + + ((eq (kotlin-mode--token-type parent) ':) + (if (kotlin-mode--colon-before-delegation-specifiers-p parent) + parent + (kotlin-mode--find-parent-of-expression))) + + (t + parent)))) + +(defun kotlin-mode--colon-before-delegation-specifiers-p (token) + "Return non-nil if TOKEN is a colon before delegationSpecifiers." + (and + (eq (kotlin-mode--token-type token) ':) + (save-excursion + (goto-char (kotlin-mode--token-start token)) + (let (previous-token) + ;; Try backward primaryConstructor. + (forward-comment (- (point))) + (when (eq (char-before) ?\)) + (kotlin-mode--backward-token-or-list) + (setq previous-token (kotlin-mode--backward-token)) + (if (equal (kotlin-mode--token-text previous-token) "constructor") + (kotlin-mode--try-backward-modifiers) + (goto-char (kotlin-mode--token-end previous-token)))) + (kotlin-mode--try-backward-type-parameters) + (setq previous-token (kotlin-mode--backward-token-or-list)) + (or + (equal (kotlin-mode--token-text previous-token) "object") + (and + (eq (kotlin-mode--token-type previous-token) 'atom) + (member (kotlin-mode--token-text (kotlin-mode--backward-token-or-list)) + '("class" "interface" "object")))))))) + +(defun kotlin-mode--calculate-indent-after-beginning-of-template-expression + (offset) + "Return indentation after the beginning of a template expression. + +It has offset specified with OFFSET. + +Assuming the point is before the string chunk." + (let ((pos (point))) + (kotlin-mode--forward-string-chunk) + (if (< pos (line-beginning-position)) + ;; The chunk has multiple lines. Align with this line with offset. + (progn + (back-to-indentation) + (make-instance 'kotlin-mode--indentation + :position (point) + :offset offset)) + ;; The chunk is single line. Indent like a expression. + (goto-char pos) + (kotlin-mode--calculate-indent-of-expression offset offset)))) + +(defun kotlin-mode--calculate-indent-after-semicolon () + "Return indentation after semicolon. + +Assuming the point is after the semicolon." + (while (save-excursion + (memq (kotlin-mode--token-type (kotlin-mode--backward-token)) + '(\; implicit-\;))) + (kotlin-mode--backward-token)) + (kotlin-mode--find-parent-and-align-with-next + (cl-remove-if + (lambda (e) + (member e '(\; implicit-\; bare-else + \(\)-before-control-structure-body when-expression-arrow))) + kotlin-mode--statement-parent-tokens) + 0 + '(\; implicit-\;))) + +(defun kotlin-mode--calculate-indent-before-in () + "Return indentation after \"in\" token. + +Assuming the point is before the token. +It is also assumed that the point is not just after \"{\" or \"<\"." + (let* ((type-and-parent (kotlin-mode--find-parent-of-in)) + (type (car type-and-parent)) + (parent (cdr type-and-parent))) + (if (memq type '(for when <>)) + (kotlin-mode--align-with-next-token parent) + (kotlin-mode--calculate-indent-after-semicolon)))) + +(defun kotlin-mode--find-parent-of-in () + "Return parent token of \"in\" token. + +Return a cons (TYPE . PARENT) where TYPE is one of symbol `for', +`when', `<>', or `other' and PARENT is the parent token, one of +`;', `implicit-;', `(', `{', or \"<\". + +Assuming the point is before the token." + ;; Examples: + ;; for ( + ;; x + ;; in // Align with token after "(" + ;; xs + ;; ) {} + ;; + ;; for (x + ;; in + ;; xs) {[]} + ;; + ;; when (x) { + ;; in xs -> 1 + ;; in ys -> 1 + ;; } + ;; + ;; Foo< + ;; in X, + ;; @AAA + ;; in X + ;; > + ;; + ;; val x = 1 // Line breaks are prohibited before infix "in" operator. + ;; in array + (let ((parent (save-excursion (kotlin-mode--backward-sexps-until + '(\; implicit-\; \( { "<"))))) + (cond + ;; Inside "for ()" + ((and + (eq (kotlin-mode--token-type parent) '\() + (save-excursion + (goto-char (kotlin-mode--token-start parent)) + (equal (kotlin-mode--token-text (kotlin-mode--backward-token)) "for"))) + (cons 'for parent)) + + ;; Inside "when () {}" + ((and + (eq (kotlin-mode--token-type parent) '{) + (save-excursion + (goto-char (kotlin-mode--token-start parent)) + (eq (kotlin-mode--token-type (kotlin-mode--backward-token-or-list)) + '\(\)) + (equal (kotlin-mode--token-text (kotlin-mode--backward-token)) + "when"))) + (cons 'when parent)) + + ;; Inside type parameters + ((equal (kotlin-mode--token-text parent) "<") + (if (save-excursion + (goto-char (kotlin-mode--token-start parent)) + (eq (kotlin-mode--token-type + (kotlin-mode--forward-token-or-list)) + '<>)) + (cons '<> parent) + (save-excursion + (goto-char (kotlin-mode--token-start parent)) + (kotlin-mode--find-parent-of-in)))) + + (t + (cons 'other parent))))) + +(defun kotlin-mode--backward-sexps-until (token-types + &optional + stop-at-eol-token-types + stop-at-bol-token-types) + "Backward sexps until a token with one of given token types appears. + +Return the token. +When this function returns, the point is at the start of the token. + +TOKEN-TYPES is a list of guard token types. This function backs to a +token with one of those token types. +STOP-AT-EOL-TOKEN-TYPES is a list of token types that if we skipped +the end of a line just after a token with one of given token type, the +function returns. Typically, this is a list of token types that +separates list elements (e.g. ',', ';'). If STOP-AT-EOL-TOKEN-TYPES +is the symbol `any', it matches all tokens. +STOP-AT-BOL-TOKEN-TYPES is a list of token types that if we hit the +beginning of a line just before a token with one of given token types, +the function returns. Typically, this is a list of token types that +starts list element . If STOP-AT-BOL-TOKEN-TYPES is the symbol `any', +it matches all tokens." + (let* + ((parent (kotlin-mode--backward-token-or-list)) + (type (kotlin-mode--token-type parent)) + (text (kotlin-mode--token-text parent))) + (while (not + ;; Stop loop when... + (or + ;; Hit a guard token. + (memq type token-types) + (member text token-types) + + ;; Beginning of the buffer. + (eq type 'outside-of-buffer) + + ;; When this function is called on "," token before position (1), + ;; this function stops just before the "," token after "Foo". + ;; + ;; Foo, + ;; Bar, Baz, // (1) + ;; AAA + (and stop-at-eol-token-types + (or (eq stop-at-eol-token-types 'any) + (member type stop-at-eol-token-types) + (member text stop-at-eol-token-types)) + (save-excursion + (kotlin-mode--forward-token-or-list) + (forward-comment (- (point))) + (kotlin-mode--eol-other-than-comments-p))) + + ;; When this function is called on "," token before position + ;; (1), this function stops just before ", Bar". + ;; + ;; , Foo + ;; , Bar, Baz: + ;; , AAA // (1) + (and stop-at-bol-token-types + (and + (or + (eq stop-at-bol-token-types 'any) + (member type stop-at-bol-token-types) + (member text stop-at-bol-token-types)) + (kotlin-mode--bol-other-than-comments-p))))) + (setq parent (kotlin-mode--backward-token-or-list)) + (setq type (kotlin-mode--token-type parent)) + (setq text (kotlin-mode--token-text parent))) + parent)) + +(defun kotlin-mode--align-with-next-token (parent &optional offset) + "Return indentation with the next token of PARENT with OFFSET. + +Example: + +Suppose indenting \"B\": + +foo { + /* */ A() + B() +} + +The parent is \"{\". We align with the comment before \"A\"." + (let ((parent-end (kotlin-mode--token-end parent))) + (goto-char parent-end) + (forward-comment (point-max)) + (kotlin-mode--goto-non-comment-bol) + (when (< (point) parent-end) + (goto-char parent-end)) + (kotlin-mode--skip-whitespaces) + (make-instance 'kotlin-mode--indentation + :position (point) + :offset (or offset 0)))) + +(defun kotlin-mode--align-with-token (token &optional offset) + "Return indentation with the TOKEN with OFFSET. + +If the token is preceded by comments on the same line, align with that +comments instead. + +Example: + +Suppose indenting \"B\": + +foo { + /* */ A() + B() +} + +We align with the comment before \"A\"." + (goto-char (kotlin-mode--token-start token)) + (forward-comment (- (point))) + (kotlin-mode--align-with-next-token (kotlin-mode--backward-token) offset)) + +(defun kotlin-mode--align-with-current-line (&optional offset) + "Return indentation of the current line with OFFSET." + (kotlin-mode--goto-non-comment-bol) + (kotlin-mode--skip-whitespaces) + (make-instance 'kotlin-mode--indentation + :position (point) + :offset (or offset 0))) + +(defun kotlin-mode--goto-non-comment-bol-with-same-nesting-level + (&optional use-backward-token-simple) + "Back to the beginning of line that is not inside a comment. + +See `kotlin-mode--backward-token-or-list' for USE-BACKWARD-TOKEN-SIMPLE." + (while (not (kotlin-mode--bol-other-than-comments-p)) + (kotlin-mode--backward-token-or-list use-backward-token-simple))) + + +;;; indent-new-comment-line + +(defun kotlin-mode--indent-new-comment-line (&optional soft) + "Break the line at the point and indent the new line. + +If the point is inside a comment, continue the comment. If the comment is a +multiline comment, close the previous comment and start new one if +`comment-multi-line' is nil. +See `indent-new-comment-line' for SOFT." + (interactive) + (let* ((chunk (kotlin-mode--chunk-after)) + (comment-beginning-position (kotlin-mode--chunk-start chunk))) + (if soft (insert-and-inherit ?\n) (newline 1)) + (delete-horizontal-space) + + ;; Insert a prefix and indent the line. + (cond + ((not (kotlin-mode--chunk-comment-p chunk)) + (indent-according-to-mode)) + + ((kotlin-mode--chunk-single-line-comment-p chunk) + (insert-and-inherit + (save-excursion + (goto-char comment-beginning-position) + (looking-at "/+\\s *") + (match-string-no-properties 0))) + (indent-according-to-mode)) + + ((not comment-multi-line) + (insert-and-inherit + (save-excursion + (goto-char comment-beginning-position) + (looking-at "/\\*+\\s *") + (match-string-no-properties 0))) + ;; Clean up and close the previous line. + (save-excursion + (beginning-of-line) + (backward-char) + (delete-horizontal-space) + (insert-and-inherit " */")) + (indent-according-to-mode)) + + (t + (kotlin-mode--format-multiline-comment-line-after-newline chunk soft))) + ;; Clean up the previous line. + (save-excursion + (beginning-of-line) + (backward-char) + (delete-horizontal-space)))) + +(defun kotlin-mode--format-multiline-comment-line-after-newline (chunk soft) + "Insert prefix and indent current line in multiline comment. + +The point is assumed inside multiline comment and just after newline. + +The closing delimiter is also inserted and/or formatted depending on custom +variables `kotlin-mode--auto-close-multiline-comment' and +`kotlin-mode--break-line-before-comment-close'. + +CHUNK is the comment chunk. + +See `indent-new-comment-line' for SOFT." + (let ((comment-beginning-position (kotlin-mode--chunk-start chunk))) + (cond + ((save-excursion + (forward-line -1) + (<= (point) comment-beginning-position)) + ;; The point was on the 2nd line of the comment. + + ;; If the comment have only one line, delete a space after asterisk. + ;; + ;; Example: + ;; /** aaa */ + ;; ↓ + ;; /** + ;; * aaa + ;; */ + ;; + ;; /** aaa */ + ;; ↓ + ;; /** + ;; * aaa + ;; */ + ;; + ;; + ;; If the comment spans several lines, keep spaces. + ;; + ;; /** aaa + ;; */ + ;; ↓ + ;; /** + ;; * aaa + ;; */ + (when (= (line-beginning-position) + (save-excursion + (goto-char comment-beginning-position) + (forward-comment 1) + (line-beginning-position))) + (save-excursion + (goto-char comment-beginning-position) + (forward-char) + (skip-chars-forward "*") + (when (looking-at " [ \t]*$") + (delete-char 1)))) + + ;; If the point is just before the closing delimiter, break the line. + (when (and kotlin-mode--break-line-before-comment-close + (= (point) + (save-excursion + (goto-char comment-beginning-position) + (if (forward-comment 1) + (progn + (backward-char) + (skip-chars-backward "*") + (point)) + -1)))) + (save-excursion + (if soft (insert-and-inherit ?\n) (newline 1)) + (indent-according-to-mode))) + + ;; Invoke `kotlin-mode--indent-line`. + (indent-according-to-mode) + + ;; Insert or replace a space to an asterisk. + (when kotlin-mode--prepend-asterisk-to-comment-line + (let ((columns-from-end (- (line-end-position) (point)))) + (move-to-column + (save-excursion + (goto-char comment-beginning-position) + (forward-char) + (current-column))) + (insert-and-inherit "*") + (when (eq (char-after) ?\s) + (delete-char 1)) + (when (and + kotlin-mode--insert-space-after-asterisk-in-comment + (not (eq (char-after) ?\s))) + (insert-and-inherit " ")) + (goto-char (- (line-end-position) columns-from-end))))) + + ;; The point was on the 3nd or following lines of + ;; the comment. + ;; Use the prefix of the previous line. + + ((and + kotlin-mode--prepend-asterisk-to-comment-line + (save-excursion + (forward-line -1) + (looking-at "\\s *\\(\\*+\\s *\\)"))) + ;; The previous line has a prefix. Use it. + (insert-and-inherit (match-string-no-properties 1)) + (indent-according-to-mode)) + + (t + ;; Use the default indentation. + (indent-according-to-mode))) + + ;; Close incomplete multiline comment. + (when (and kotlin-mode--auto-close-multiline-comment + (kotlin-mode--incomplete-comment-p chunk)) + (save-excursion + (end-of-line) + (when comment-multi-line + (if soft (insert-and-inherit ?\n) (newline 1))) + (insert-and-inherit "*/") + (indent-according-to-mode))) + + ;; Make sure the closing delimiter is on its own line. + (when kotlin-mode--break-line-before-comment-close + (save-excursion + (goto-char comment-beginning-position) + (when (forward-comment 1) + (backward-char) + (skip-chars-backward "*") + (skip-syntax-backward " ") + (when (not (bolp)) + (if soft (insert-and-inherit ?\n) (newline 1)) + (indent-according-to-mode))))))) + +(defun kotlin-mode--post-self-insert () + "Miscellaneous logic for electric indentation." + (cond + ;; Indent electrically and insert a space when "*" is inserted at the + ;; beginning of a line inside a multiline comment. + ((and + kotlin-mode--prepend-asterisk-to-comment-line + (eq last-command-event ?*) + (kotlin-mode--chunk-comment-p (kotlin-mode--chunk-after)) + (save-excursion (backward-char) (skip-syntax-backward " ") (bolp))) + (when kotlin-mode--insert-space-after-asterisk-in-comment + (insert-and-inherit " ")) + (when electric-indent-mode + (indent-according-to-mode))) + + ;; Fixe "* /" at the end of a multiline comment to "*/". + ((and + kotlin-mode--fix-comment-close + (eq last-command-event ?/) + (let ((chunk (kotlin-mode--chunk-after)) + (pos (point))) + (and + (kotlin-mode--chunk-comment-p chunk) + (save-excursion + (beginning-of-line) + (and + (looking-at "^\\s *\\*\\s +/") + (eq (match-end 0) pos) + (kotlin-mode--incomplete-comment-p chunk)))))) + (backward-char) + (delete-horizontal-space) + (forward-char)) + + ;; Indent electrically when "}" is inserted at bol as the end of a string + ;; interpolation. + ((and + electric-indent-mode + (eq last-command-event ?\}) + (save-excursion (backward-char) (skip-syntax-backward " ") (bolp)) + (eq (kotlin-mode--chunk-start (kotlin-mode--chunk-after)) (1- (point)))) + (indent-according-to-mode)) + + ;; Indent electrically after newline inside strings and comments. + ;; Unlike `electric-indent-mode', the previous line is not indented. + ((and + electric-indent-mode + (eq last-command-event ?\n)) + (let ((chunk (kotlin-mode--chunk-after))) + (if (kotlin-mode--chunk-multiline-comment-p chunk) + (progn + (delete-horizontal-space) + (kotlin-mode--format-multiline-comment-line-after-newline + chunk + (not use-hard-newlines))) + (indent-according-to-mode))) + (save-excursion + (beginning-of-line) + (backward-char) + (delete-horizontal-space))))) + +(defun kotlin-mode--highlight-anchor (indentation) + "Highlight the anchor point of the INDENTATION." + (move-overlay + kotlin-mode--anchor-overlay + (kotlin-mode--indentation-position indentation) + (1+ (kotlin-mode--indentation-position indentation))) + + (overlay-put kotlin-mode--anchor-overlay 'face 'highlight) + + (when kotlin-mode--anchor-overlay-timer + (cancel-timer kotlin-mode--anchor-overlay-timer)) + + (let ((buffer (current-buffer))) + (setq kotlin-mode--anchor-overlay-timer + (run-at-time + "1 sec" + nil + (lambda () + (when (buffer-live-p buffer) + (with-current-buffer buffer + (delete-overlay kotlin-mode--anchor-overlay) + (setq kotlin-mode--anchor-overlay-timer nil)))))))) + +(provide 'kotlin-mode-indent) + +;;; kotlin-mode-indent.el ends here diff --git a/kotlin-mode-lexer.el b/kotlin-mode-lexer.el index 8aa2634..6e45212 100644 --- a/kotlin-mode-lexer.el +++ b/kotlin-mode-lexer.el @@ -53,6 +53,41 @@ ;; ;; This is not a official term; used only in kotlin-mode. +;;; Brackets + +(defvar-local kotlin-mode--bracket-positions '() + "List of position of brackets. + +Element of the list is a cons (TYPE . POSITION) where TYPE is one of +\(, ), {, }, [, or ], and POSITION is the position of the token. + +Elements are sorted by the position in descending order.") + +(defun kotlin-mode--clear-bracket-positions-after (position) + "Remove bracket positions after or equal to POSITION from cache." + (while (and kotlin-mode--bracket-positions + (<= position (cdar kotlin-mode--bracket-positions))) + (pop kotlin-mode--bracket-positions))) + +(defun kotlin-mode--find-containing-brackets (position) + "Return start position of innermost brackets containing POSITION. + +Return a cons (TYPE . POSITION) where TYPE is one of (, ), {, }, [, ], +or outside-of-buffer, and POSITION is the position of the token." + (let ((brackets kotlin-mode--bracket-positions) + (nesting-level 1)) + (while (and brackets (<= position (cdar brackets))) + (pop brackets)) + (while (and brackets (not (zerop nesting-level))) + (if (memq (caar brackets) '(\( \[ {)) + (setq nesting-level (1- nesting-level)) + (setq nesting-level (1+ nesting-level))) + (unless (zerop nesting-level) + (pop brackets))) + (if brackets + (car brackets) + (cons 'outside-of-buffer (point-min))))) + ;;; Text properties ;; See also doc/string_properties.png. @@ -95,7 +130,9 @@ Mark the beginning of and the end of single-line/multiline strings, character literals, backquoted identifiers between the position START and END as general string delimiters. -Intended for `syntax-propertize-function'." +Intended for `syntax-propertize-function'. + +Also track position of brackets in `kotlin-mode--bracket-positions'." (remove-text-properties start end '(syntax-table nil @@ -105,6 +142,7 @@ Intended for `syntax-propertize-function'." nil kotlin-property--interpolation nil)) + (kotlin-mode--clear-bracket-positions-after start) (let* ((chunk (kotlin-mode--chunk-after (syntax-ppss start)))) (cond ((kotlin-mode--chunk-multiline-string-p chunk) @@ -132,50 +170,70 @@ Mark the beginning of and the end of single-line/multiline strings and character literals between the current position and END as general string delimiters. -Assuming the cursor is not on strings, character-literal, +Assuming the point is not on strings, character-literal, backquoted identifier, nor comments. If NESTING-LEVEL is non-zero, nesting of brackets are tracked and the scan stops where the level becomes zero." (let ((found-matching-bracket nil) - (pattern (rx (or "\"\"\"" "\"" "//" "/*" "{" "}" "'" "`")))) + (pattern + (rx (or "\"\"\"" "\"" "//" "/*" "{" "}" "(" ")" "[" "]" "'" "`"))) + match-string + start) (while (and (not found-matching-bracket) (< (point) end) (search-forward-regexp pattern end t)) + (setq match-string (match-string-no-properties 0)) + (setq start (match-beginning 0)) (cond - ((member (match-string-no-properties 0) '("\"\"\"" "\"" "'" "`")) - (let ((start (match-beginning 0)) - (quotation (match-string-no-properties 0))) - (put-text-property start (1+ start) + ;; Quotes + ((member match-string '("\"\"\"" "\"" "'" "`")) + ;; Mark the start of the quotes as a generic string delimiter. + (put-text-property start (1+ start) + 'syntax-table + (string-to-syntax "|")) + ;; Scan until end of the string. + (kotlin-mode--syntax-propertize-end-of-string end match-string) + ;; Mark whole string, including template expressions, + ;; with `syntax-multiline'. The property is used by + ;; `kotlin-mode--syntax-propertize-extend-region'. + (put-text-property start (point) 'syntax-multiline t) + + (when (equal match-string "`") + ;; Backquotes cannot be escaped. So declare the backslashes in + ;; the identifier are normal characters, not escape-syntax characters. + (put-text-property (1+ start) (1- (point)) 'syntax-table - (string-to-syntax "|")) - (kotlin-mode--syntax-propertize-end-of-string end quotation) - (put-text-property start (point) 'syntax-multiline t) - - (when (equal quotation "`") - ;; Backquotes cannot be escaped. So declares the backslashes in - ;; the identifier are not a escape-syntax characters. - (put-text-property (1+ start) (1- (point)) - 'syntax-table - (string-to-syntax "w"))))) + (string-to-syntax "w")))) - ((equal "//" (match-string-no-properties 0)) - (goto-char (match-beginning 0)) + ;; Single-line comment + ((equal "//" match-string) + (goto-char start) (forward-comment (point-max))) - ((equal "/*" (match-string-no-properties 0)) - (goto-char (match-beginning 0)) + ;; Multiline comment + ((equal "/*" match-string) + (goto-char start) (forward-comment (point-max))) - ((and (equal "{" (match-string-no-properties 0)) + ;; Open curly bracket + ((and (equal "{" match-string) (/= nesting-level 0)) + (push (cons (intern match-string) start) kotlin-mode--bracket-positions) (setq nesting-level (1+ nesting-level))) - ((and (equal "}" (match-string-no-properties 0)) + ;; Close curly bracket + ((and (equal "}" match-string) (/= nesting-level 0)) + (push (cons (intern match-string) start) kotlin-mode--bracket-positions) (setq nesting-level (1- nesting-level)) (when (= nesting-level 0) - (setq found-matching-bracket t))))) + (setq found-matching-bracket t))) + + ;; Other brackets + ((member match-string '("(" ")" "[" "]" "{" "}")) + (push (cons (intern match-string) start) + kotlin-mode--bracket-positions)))) (unless found-matching-bracket (goto-char end)) found-matching-bracket)) @@ -183,7 +241,7 @@ the scan stops where the level becomes zero." (defun kotlin-mode--syntax-propertize-end-of-string (end quotation) "Move point to the end of single-line/multiline string. -Assuming the cursor is on a string, a character literal, or a backquoted +Assuming the point is on a string, a character literal, or a backquoted identifier. If the string go beyond END, stop there. The string should be terminated with QUOTATION." @@ -197,12 +255,15 @@ The string should be terminated with QUOTATION." (and "`" (+ (not (any "`\n"))) "`"))))) end t)) (cond + ;; End of the string ((and (equal quotation (match-string-no-properties 0)) (or (equal quotation "`") ; backquotes cannot be escaped (not (kotlin-mode--escaped-p (match-beginning 0))))) (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax "|"))) + + ;; Start of a template expression ((and (equal "${" (match-string-no-properties 0)) (member quotation '("\"\"\"" "\"")) ;; Dollar signs cannot be escaped, so we don't need to check it. @@ -216,30 +277,39 @@ The string should be terminated with QUOTATION." (backward-char) ;; { (backward-char) ;; $ (point)))) - ;; Declares the open bracket is a generic string delimiter. + ;; Keep the position of the open bracket. + (push (cons '{ (1+ start)) kotlin-mode--bracket-positions) + ;; Declare the open bracket is a generic string delimiter. (put-text-property (1- pos-after-open-bracket) pos-after-open-bracket 'syntax-table (string-to-syntax "|")) + ;; Try to skip (when (kotlin-mode--syntax-propertize-scan end 1) ;; Found the matching bracket. Going further. - ;; Declares the close bracket is a generic string delimiter. + ;; Declare the close bracket is a generic string delimiter. (put-text-property (1- (point)) (point) 'syntax-table (string-to-syntax "|")) - ;; Records the positions. + ;; Record the positions. (put-text-property (1- (point)) (point) 'kotlin-property--matching-bracket start) (put-text-property start pos-after-open-bracket 'kotlin-property--matching-bracket (1- (point))) + ;; Proceed. (kotlin-mode--syntax-propertize-end-of-string end quotation)))) - ((match-string-no-properties 0) + + ;; Template expression without braces + ((and (eq (aref (match-string-no-properties 0) 0) ?$) + (not (eq (aref (match-string-no-properties 0) 1) ?{))) (put-text-property (match-beginning 0) (1+ (match-beginning 0)) 'kotlin-property--interpolation (match-data)) (kotlin-mode--syntax-propertize-end-of-string end quotation)) + + ;; Others (t (kotlin-mode--syntax-propertize-end-of-string end quotation))) (goto-char end))) @@ -262,7 +332,6 @@ Return nil otherwise." (defclass kotlin-mode--chunk () ((type :initarg :type :type symbol - :accessor kotlin-mode--chunk-type :documentation "The type of the chunk. Valid values: @@ -274,12 +343,19 @@ Valid values: - backquoted-identifier") (start :initarg :start :type number - :accessor kotlin-mode--chunk-start :documentation "The start position of the chunk.")) "String-chunks, comments, character literals, or backquoted identifiers. It have the type and the start position.") +(defun kotlin-mode--chunk-type (chunk) + "Return the type of the CHUNK." + (and chunk (oref chunk type))) + +(defun kotlin-mode--chunk-start (chunk) + "Return the start position of the CHUNK." + (and chunk (oref chunk start))) + (defun kotlin-mode--chunk-comment-p (chunk) "Return non-nil if the CHUNK is a comment." (and chunk @@ -316,10 +392,19 @@ It have the type and the start position.") "Return non-nil if the CHUNK is a backquoted identifier." (and chunk (eq (kotlin-mode--chunk-type chunk) 'backquoted-identifier))) +(defun kotlin-mode--incomplete-comment-p (chunk) + "Return t if the CHUNK is incomplete comment. + +Return nil otherwise." + (and (kotlin-mode--chunk-comment-p chunk) + (save-excursion + (goto-char (kotlin-mode--chunk-start chunk)) + (not (forward-comment 1))))) + (defun kotlin-mode--chunk-after (&optional parser-state) - "Return the chunk at the cursor. + "Return the chunk at the point. -If the cursor is outside of strings and comments, return nil. +If the point is outside of strings and comments, return nil. If PARSER-STATE is given, it is used instead of (syntax-ppss)." (save-excursion @@ -332,14 +417,27 @@ If PARSER-STATE is given, it is used instead of (syntax-ppss)." ;; Syntax category "|" is attached to both single-line and multiline ;; string delimiters. So (nth 3 parser-state) may be t even for ;; single-line string delimiters. - (if (save-excursion (goto-char (nth 8 parser-state)) - (looking-at "\"\"\"")) + (save-excursion + (goto-char (nth 8 parser-state)) + (forward-char) + (kotlin-mode--beginning-of-string) + (cond + ((looking-at "\"\"\"") (make-instance 'kotlin-mode--chunk :type 'multiline-string - :start (nth 8 parser-state)) - (make-instance 'kotlin-mode--chunk - :type 'single-line-string - :start (nth 8 parser-state)))) + :start (nth 8 parser-state))) + ((looking-at "`") + (make-instance 'kotlin-mode--chunk + :type 'backquoted-identifier + :start (nth 8 parser-state))) + ((looking-at "'") + (make-instance 'kotlin-mode--chunk + :type 'character-literal + :start (nth 8 parser-state))) + (t + (make-instance 'kotlin-mode--chunk + :type 'single-line-string + :start (nth 8 parser-state)))))) ((eq (nth 4 parser-state) t) (make-instance 'kotlin-mode--chunk :type 'single-line-comment @@ -380,168 +478,2109 @@ If PARSER-STATE is given, it is used instead of (syntax-ppss)." (modify-syntax-entry ?\r "> b" st) st)) -;; Line level movements and predicates +;; Token -(defun kotlin-mode--prev-line () - "Move up to the nearest non-empty line." - (beginning-of-line) - ;; `forward-comment' skips spaces and newlines as well. - (forward-comment (- (point)))) +(defclass kotlin-mode--token () + ((type :initarg :type + :type symbol + :documentation "The type of the token. + +Token types is one of the following symbols: + +- operator (including as, as?, is, !is, in, !in, ., and ->) +- annotation + (e.g. @ABC, @ABC(def), @file:ABC, @[ABC DEF(ghi)], or @file:[ABC DEF(ghi)]) +- atom (including keywords, numbers, strings, and unknown tokens) +- label +- return (including return@identifier) +- continue (including continue@identifier) +- beak (including beak@identifier) +- this (including this@identifier) +- super (including super@identifier) +- [ +- ] +- { +- } +- ( +- ) +- )-before-control-structure-body +- , +- ; +- implicit-; +- < (as an angle bracket) +- > (as an angle bracket) +- : +- string-chunk-after-template-expression (part of a string ending with \"}\") +- string-chunk-before-template-expression (part of a string ending with \"${\") +- bare-else (\"else\" not followed by \"if\" on the same line, \"->\", or \"{\") +- outside-of-buffer +- anonymous-function-parameter-arrow +- when-expression-arrow + +Additionally, `kotlin-mode--backward-token-or-list' may return a parenthesized +expression as a token with one of the following types: +- () +- ()-before-control-structure-body +- [] +- {} +- <>" ) + (text :initarg :text + :type string + :documentation "The text of the token.") + (start :initarg :start + :type integer + :documentation "The start position of the token.") + (end :initarg :end + :type integer + :documentation "The point after the token.")) + "Token of Kotlin.") + +(defun kotlin-mode--token-type (token) + "Return the type of TOKEN." + (and token (oref token type))) + +(defun kotlin-mode--token-text (token) + "Return the text of TOKEN." + (and token (oref token text))) + +(defun kotlin-mode--token-start (token) + "Return the start position of TOKEN." + (and token (oref token start))) + +(defun kotlin-mode--token-end (token) + "Return the end position of TOKEN." + (and token (oref token end))) + +;; Token level movements and predicates. + +(defconst kotlin-mode--inheritance-modifier-keywords + '("open" "final" "abstract")) + +(defconst kotlin-mode--visibility-modifier-keywords + '("public" "private" "internal" "protected")) + +(defconst kotlin-mode--member-modifier-keywords + '("lateinit" "override")) + +(defconst kotlin-mode--companion-modifier-keywords + '("companion")) + +(defconst kotlin-mode--class-modifier-keywords + '("enum" "sealed" "annotation" "data" "inner")) + +(defconst kotlin-mode--property-modifier-keywords + '("const")) + +(defconst kotlin-mode--platform-modifier-keywords + '("expect" "actual")) + +(defconst kotlin-mode--parameter-modifier-keywords + '("vararg" "crossinline" "noinline")) + +(defconst kotlin-mode--function-modifier-keywords + '("tailrec" "operator" "infix" "inline" "external" "suspend")) + +(defconst kotlin-mode--modifier-keywords + (append kotlin-mode--inheritance-modifier-keywords + kotlin-mode--visibility-modifier-keywords + kotlin-mode--member-modifier-keywords + kotlin-mode--class-modifier-keywords + kotlin-mode--property-modifier-keywords + kotlin-mode--platform-modifier-keywords + kotlin-mode--parameter-modifier-keywords + kotlin-mode--function-modifier-keywords)) + +(defun kotlin-mode--implicit-semi-p () + "Return non-nil if the point is after the end of a statement." + (let ((previous-token (save-excursion + (kotlin-mode--extend-annotation-token-backward + (kotlin-mode--backward-token-simple)))) + (next-token (save-excursion + (kotlin-mode--extend-annotation-token-forward + (kotlin-mode--forward-token-simple))))) + ;; If the point is on the empty line, pretend an identifier is on the line. + (when (and + (< (kotlin-mode--token-end previous-token) (line-beginning-position)) + (< (line-end-position) (kotlin-mode--token-start next-token))) + (setq next-token (make-instance 'kotlin-mode--token + :type 'atom + :text "" + :start (point) + :end (point)))) + (cond + ;; Tokens that end a statement + ((memq (kotlin-mode--token-text previous-token) + '("return" "continue" "break")) + t) -(defun kotlin-mode--line-begins-p (pattern) - "Return whether the current line begins with the given PATTERN. + ;; .* in import declarations end a statement. + ((and (equal (kotlin-mode--token-text previous-token) "*") + (equal (kotlin-mode--token-text + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--backward-token-simple))) + ".")) + t) -Ignore spaces at the beginning of the line." - (save-excursion - (beginning-of-line) - (looking-at (format "^[ \t]*%s" pattern)))) - -(defun kotlin-mode--line-begins-excluding-comment-p (pattern) - "Return whether the current line begins with the given PATTERN. - -Ignore comments and spaces at the beginning of the line." - (let ((line-end-position (line-end-position))) - (save-excursion - (beginning-of-line) - (when (nth 4 (syntax-ppss)) - ;; If the point is inside a comment, goto the beginning of the - ;; comment. - (goto-char (nth 8 (syntax-ppss)))) - (forward-comment (point-max)) - (when (< line-end-position (point)) - (goto-char line-end-position)) - (looking-at pattern)))) + ;; Suppress implicit semicolon after "if ()", "while ()", "for ()" + ((kotlin-mode--close-parenthesis-before-control-structure-body-p + previous-token) + nil) -(defun kotlin-mode--line-ends-p (pattern) - "Return whether the current line ends with the given PATTERN. + ;; Annotations and modifiers, that cannot end a statement/declaration + ((or + ;; Annotations + (and + (eq (kotlin-mode--token-type previous-token) 'annotation) + ;; Exclude super@label. Note that this@label is handled by + ;; `kotlin-mode--backward-token-simple' + (not (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (and (eq (char-before) ?>) + (kotlin-mode--try-backward-type-parameters) + (equal (kotlin-mode--token-text + (kotlin-mode--backward-token-simple)) + "super"))))) + ;; Labels + (eq (kotlin-mode--token-type previous-token) 'label) + ;; Modifiers + (member (kotlin-mode--token-text previous-token) + kotlin-mode--modifier-keywords)) + nil) -Ignore spaces at the end of the line." - (save-excursion - (beginning-of-line) - (looking-at (format ".*%s[ \t]*$" pattern)))) + ;; Tokens that cannot end a statement + ;; + ;; TODO prefix ++ and -- + ;; TODO infix function call + ;; TODO prefix !, especially !! + ;; + ;; Example: + ;; var shl = 1 + ;; val x = shl shl shl + ;; shl < 100 && foo() // this is not a continuation of the previous line. + ;; + ;; var shl = 1 + ;; val x = shl shl + ;; shl < 100 && foo() // this is a continuation of the previous line. + ;; + ;; var shl = 1 + ;; val x = shl shl shl ++ + ;; shl < 100 && foo() // this is not a continuation of the previous line. + ;; + ;; var shl = 1 + ;; val x = shl shl ++ + ;; shl < 100 && foo() // this is a continuation of the previous line. + ;; + ;; val x = foo()!! + ;; foo() // this is not a continuation of the previous line. + ;; + ;; val x = !! + ;; foo() // this is a continuation of the previous line. + ((or + (memq + (kotlin-mode--token-type previous-token) + '(implicit-\; string-chunk-before-template-expression)) + (member + (kotlin-mode--token-text previous-token) + '("(" "{" "[" "*" "%" "/" "+" "-" "&&" "||" ":" + "=" "+=" "-=" "*=" "/=" "%=" + "->" "." ".." "::" "?:" "?." "<=" ">=" "!=" "!==" "==" "===" + "as" "as?" "is" "!is" "in" "!in" "," ";" "{" "[" "(" + ;; "class" will be handled later. + "package" "import" "interface" "fun" "object" + "val" "var" "typealias" "constructor" "by" "companion" "init" + "where" "if" "else" "when" "try" "catch" "finally" "for" "do" + "while" "throw" "out" "reified")) + ;; Inequality operator cannot end a statement/declaration. + (and (eq (kotlin-mode--token-type previous-token) 'operator) + (member (kotlin-mode--token-text previous-token) '("<" ">")))) + nil) -(defun kotlin-mode--line-ends-excluding-comment-p (pattern) - "Return whether the current line ends with the given PATTERN. + ;; "class" cannot end a statement unless preceded by "::". + ;; + ;; Example: + ;; + ;; class // No implicit semicolon here + ;; Foo { + ;; } + ;; + ;; val x = Foo :: + ;; class // Implicit semicolon here + ;; foo() + ((and (equal (kotlin-mode--token-text previous-token) "class") + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (not (equal (kotlin-mode--token-text + (kotlin-mode--backward-token-simple)) + "::")))) + nil) -Ignore comments at the end of the line." - (let ((end-position - ;; last point where is neither spaces nor comment - (max - (line-beginning-position) - (save-excursion - (end-of-line) - (when (nth 4 (syntax-ppss)) - ;; If the point is inside a comment, goto the beginning - ;; of the comment. - (goto-char (nth 8 (syntax-ppss)))) - (forward-comment (- (point))) - (point))))) - (save-excursion - (save-restriction - (beginning-of-line) - (narrow-to-region (point) end-position) - (looking-at (format ".*%s$" pattern)))))) - -(defun kotlin-mode--line-contains-p (pattern) - "Return whether the current line contains the given PATTERN." - (save-excursion - (beginning-of-line) - (looking-at (format ".*%s.*" pattern)))) + ;; Annotations, labels, and modifiers, that start a statement/declaration, + ;; except for getter and setter. + ;; + ;; "suspend" is excluded because it can be used as type modifier. + ((or + (memq (kotlin-mode--token-type next-token) '(annotation label)) + (member (kotlin-mode--token-text next-token) + (remove "suspend" kotlin-mode--modifier-keywords))) + (not (save-excursion + (kotlin-mode--try-forward-modifiers) + (member (kotlin-mode--token-text + (kotlin-mode--forward-token-simple)) + '("get" "set"))))) + + ;; Tokens that start a statement/declaration + ((member + (kotlin-mode--token-text next-token) + ;; "class" will be handled later. + '("package" "import" "interface" "val" "var" "typealias" + "constructor" "companion" "init" "for" "do" "is" + ;; While we should insert a semicolon before "in" in a "when" + ;; expression or beginning of a expression, we should + ;; suppress a semicolon before "in" in "for", type + ;; parameters, or type arguments: + ;; + ;; when (x) { + ;; 1 -> 1 // implicit semicolon here + ;; in xs -> 2 + ;; } + ;; + ;; // line breaks are prohibited before infix "in" operator. + ;; val x = 1 // implicit semicolon here + ;; in xs // Though this is an invalid expression anyway. + ;; + ;; for ( + ;; x // no implicit semicolons here + ;; in + ;; xs + ;; ) {} + ;; + ;; Foo< // no implicit semicolons here + ;; in X + ;; > + ;; + ;; Because detecting the context is hard at lexer level, we + ;; omit semicolon for now, and handle it later in the + ;; indentation code. + )) + t) -;; Line continuation + ;; While or do-while + ((equal (kotlin-mode--token-text next-token) "while") + ;; insert semicolon unless it is a part of do-while. + (save-excursion (not (kotlin-mode--find-do-for-while)))) + + ;; "class" starts a new declaration unless preceded by "::". + ;; + ;; Example: + ;; + ;; class Foo { // This start a class declaration. + ;; } + ;; + ;; Foo :: + ;; class // This does not start a class declaration. + ((and (equal (kotlin-mode--token-text next-token) "class") + (not (equal (kotlin-mode--token-text previous-token) "::"))) + t) -(defun kotlin-mode--line-continuation () - "Return whether this line continues a statement in the previous line." - (let ((case-fold-search nil)) - (cond - ;; Tokens that end a statement - ((save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment-p - (rx (group - (or - ".*" - (seq word-start - (or "return" "continue" "break") - word-end)))))) + ;; Tokens that cannot start a statement/declaration. + ((or (memq + (kotlin-mode--token-type next-token) + '(implicit-\; string-chunk-after-template-expression)) + (member + (kotlin-mode--token-text next-token) + '("*" "%" "/" "&&" "||" ":" "=" "+=" "-=" "*=" "/=" "%=" + "->" "." ".." "::" "?:" "?." "?" "<" ">" "<=" ">=" + "!=" "!==" "==" "===" + "," ";" ")" "]" "}" + "as" "as?" "get" "set" "by" "where" "else" "catch" "finally" + ;; Because detecting the context is hard at lexer level, + ;; we omit semicolon before "in" for now, and handle it + ;; later in the indentation code. + "in" + ;; Strictly speaking, open curly bracket may start a statement + ;; as a part of lambda expression, it is rare case, so we + ;; suppress semicolon before it. + "{"))) nil) - ;; Modifiers, that cannot end a statement. - ((save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment-p - (rx (group (seq word-start - (or - "public" "private" "protected" - "internal" "enum" "sealed" "annotation" - "data" "inner" "tailrec" "operator" "inline" - "infix" "external" "suspend" "override" - "abstract" "final" "open" "const" "lateinit" - "vararg" "noinline" "crossinline" "reified" - "expect" "actual") - word-end))))) + ;; Open square bracket start a new statement unless preceded by a + ;; colon (already handled above). + ((eq (kotlin-mode--token-type next-token) '\[) t) - ;; Tokens that start a statement that have lower priority than modifiers. - ((kotlin-mode--line-begins-excluding-comment-p - (rx (group (seq word-start - (or - "public" "private" "protected" "internal" - "enum" "sealed" "annotation" "data" "inner" - "tailrec" "operator" "inline" "infix" - "external" "override" "abstract" "final" - "open" "const" "lateinit" "vararg" "noinline" - "crossinline" "reified" "expect" "actual" - "package" "import" "interface" "val" "var" - "typealias" "constructor" "companion" "init" - "is" "in" "out" "for" "while" "do") - word-end)))) + ;; Open parenthesis start a new statement unless preceded by: + ;; - a colon (already handled above), + ;; - a comma (already handled above), + ;; - a dot (already handled above), + ;; - an equal sign (already handled above), + ;; - "->" (already handled above), + ;; - "is" (already handled above), + ;; - "!is" (already handled above), + ;; - "as" (already handled above), + ;; - "as?" (already handled above), + ;; - "if" (already handled above), + ;; - "while" (already handled above), + ;; - "for" (already handled above), + ;; - "when" (already handled above), + ;; - "catch" (already handled above), + ;; or is a part of: + ;; - secondary constructor declaration, + ;; - class declaration, + ;; - function declaration, + ;; - variable declaration, + ;; - getter declaration, + ;; - setter declaration, + ;; - constructor declaration call (example: constructor(): this(1)), + ;; + ;; Examples: + ;; + ;; public constructor() {} + ;; + ;; public class Foo() {} + ;; public class Foo () {} + ;; public class Foo public constructor () + ;; public class Foo @AAA constructor () + ;; + ;; fun foo() {} + ;; fun foo() {} + ;; fun (A).foo() {} + ;; fun A.B.C.foo() {} + ;; fun @AAA (A).foo() {} + ;; fun (A).foo() {} + ;; fun @AAA (A).foo() {} + ;; fun @AAA A.B.C.foo() {} + ;; + ;; fun () {} + ;; fun (A).() {} + ;; fun A.B.C.() {} + ;; + ;; val (x, y) = foo() + ;; var (x, y) = foo() + ;; val (x, y) = foo() + ;; val (A).(x, y) = foo() + ;; val (A).(x, y) = foo() + ;; val @AAA A.(x, y) = foo() + ;; val @AAA A.B.C.(x, y) = foo() + ;; + ;; get() {} + ;; set(value) {} + ;; + ;; constructor (): this(1) + ;; constructor (): super(1) + ;; + ;; Note that function argument cannot preceded by newline: + ;; + ;; val x = foo + ;; (bar()) // This is not a continuation of the previous line. + ((eq (kotlin-mode--token-type next-token) '\() + (and + (not (kotlin-mode--constructor-parameter-clause-p)) + (not (kotlin-mode--function-parameter-clause-p)) + (not (kotlin-mode--multi-variable-declaration-p)) + (not (kotlin-mode--constructor-delegation-call-p)))) + + ;; Suppress implicit semicolon after the beginning of an interpolated + ;; expression. + ((eq (kotlin-mode--token-type previous-token) + 'string-chunk-before-interpolated-expression) nil) - ((and (kotlin-mode--line-begins-excluding-comment-p - (rx (group (seq word-start "class" word-end)))) - (not - (save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment-p (rx (group "::")))))) - nil) + ;; Otherwise, inserts implicit semicolon. + (t t)))) - ;; Tokens that cannot end a statement +(defun kotlin-mode--close-parenthesis-before-control-structure-body-p (token) + "Return t if TOKEN is close parenthesis before control structure body. + +Return t if TOKEN is the close-parenthesis of \"if (...)\", +\"while(...)\", or \"for (...)\", but not followed by \"{\". + +Return nil otherwise." + (and + (eq (kotlin-mode--token-type token) '\)) + (save-excursion + (goto-char (kotlin-mode--token-end token)) + (not (eq (kotlin-mode--token-type (kotlin-mode--forward-token-simple)) + '\{))) + (save-excursion + (goto-char (kotlin-mode--token-end token)) + (condition-case nil + (progn + (backward-list) + (let ((previous-token (kotlin-mode--backward-token-simple))) + (or + (member (kotlin-mode--token-text previous-token) '("if" "for")) + (and + (equal (kotlin-mode--token-text previous-token) "while") + (not (save-excursion (kotlin-mode--find-do-for-while))))))) + (scan-error + nil))))) + +(defun kotlin-mode--find-do-for-while () + "Return \"do\" token for \"while\" token. + +If \"while\" is not part of do-while, return nil. + +Assuming the point is before \"while\" token." + ;; Example: + ;; + ;; do + ;; while (false) + ;; print(1) + ;; while (false) + ;; print(2) + ;; + ;; is parsed as three statements: + ;; + ;; do while (false); + ;; print(1); + ;; while (false) print(2); + ;; + ;; So we don't need to worry about bare while-statement in body of do-while. + (let ((nesting-level 1) + previous-token) + (while (< 0 nesting-level) + (setq previous-token (kotlin-mode--backward-token-or-list t)) + (cond + ((equal (kotlin-mode--token-text previous-token) "while") + (setq nesting-level (1+ nesting-level))) + + ((equal (kotlin-mode--token-text previous-token) "do") + (setq nesting-level (1- nesting-level))) + + ((memq (kotlin-mode--token-type previous-token) '(outside-of-buffer {)) + ;; Unmatched + (setq nesting-level 0)))) + (and (equal (kotlin-mode--token-text previous-token) "do") + previous-token))) + +(defun kotlin-mode--constructor-parameter-clause-p () + "Return non-nil if the point is before parameters of constructor declaration." + ;; Examples: + ;; + ;; public constructor() {} + ;; + ;; public class Foo() {} + ;; public class Foo () {} + ;; public class Foo public constructor () + ;; public class Foo @AAA constructor () + (let ((previous-token (save-excursion (kotlin-mode--backward-token-simple)))) + (or + (equal (kotlin-mode--token-text previous-token) "constructor") + (save-excursion + (kotlin-mode--try-backward-type-parameters) + (and + (eq (kotlin-mode--token-type (kotlin-mode--backward-token-simple)) + 'atom) + (equal (kotlin-mode--token-text (kotlin-mode--backward-token-simple)) + "class")))))) + +(defun kotlin-mode--function-parameter-clause-p () + "Return non-nil if the point is before parameters of function declaration." + ;; Examples: + ;; + ;; fun foo() {} + ;; fun foo() {} + ;; fun (A).foo() {} + ;; fun A.B.C.foo() {} + ;; fun @AAA (A).foo() {} + ;; fun (A).foo() {} + ;; fun @AAA (A).foo() {} + ;; fun @AAA A.B.C.foo() {} + ;; + ;; fun () {} + ;; fun (A).() {} + ;; fun A.B.C.() {} + (let ((previous-token (save-excursion (kotlin-mode--backward-token-simple)))) + (cond + ;; fun () {} + ;; fun (A).() {} // for first parenthesis + ;; fun (A).foo() {} // for first parenthesis + ((equal (kotlin-mode--token-text previous-token) "fun") + t) + + ;; fun (A).() {} + ;; fun A.B.C.() {} + ((equal (kotlin-mode--token-text previous-token) ".") + (save-excursion + (and (kotlin-mode--try-backward-receiver-type) + (equal (kotlin-mode--token-text + (kotlin-mode--backward-token-simple)) + "fun")))) + + ;; fun @AAA (A).foo() {} // for first parenthesis + ;; fun (A).foo() {} // for first parenthesis + ;; fun @AAA (A).foo() {} // for first parenthesis ((save-excursion - (kotlin-mode--prev-line) - (kotlin-mode--line-ends-excluding-comment-p - (rx (group - (or - (any "-%*./:=+&|<@") - "->" - (seq word-start - "as?") - (seq "!is" - "!in" - word-end) - (seq word-start - (or - "package" "import" "class" "interface" "fun" - "object" "val" "var" "typealias" "constructor" - "by" "companion" "init" "where" "if" "else" - "when" "try" "catch" "finally" "for" "do" "while" - "throw" "as" "is" "in" "out") - word-end)))))) + (kotlin-mode--try-backward-type-modifiers) + (kotlin-mode--try-backward-type-parameters) + (equal (kotlin-mode--token-text + (kotlin-mode--backward-token-simple)) + "fun")) t) - ;; Tokens that cannot start a statement - ((kotlin-mode--line-begins-excluding-comment-p - (rx (group - (or - (any ".:=" - (seq word-start "as?") - (seq word-start - (or "get" "set" "as" "by" "where") - word-end))))) + ;; fun foo() {} + ;; fun foo() {} + ;; fun (A).foo() {} // for second parenthesis + ;; fun A.B.C.foo() {} + ;; fun @AAA (A).foo() {} // for second parenthesis + ;; fun (A).foo() {} // for second parenthesis + ;; fun @AAA (A).foo() {} // for second parenthesis + ;; fun @AAA A.B.C.foo() {} + ((eq (kotlin-mode--token-type previous-token) 'atom) + (save-excursion + (goto-char (kotlin-mode--token-start previous-token)) + (kotlin-mode--try-backward-receiver-type) + (kotlin-mode--try-backward-type-parameters) + (equal (kotlin-mode--token-text + (kotlin-mode--backward-token-simple)) + "fun")))))) + +(defun kotlin-mode--try-backward-receiver-type () + "Try backward dot, type, and type modifiers. + +Return non-nil if succeeds. Keep position and return nil otherwise." + (let ((pos (point)) + (previous-token (kotlin-mode--backward-token-simple))) + (if (and (equal (kotlin-mode--token-text previous-token) ".") + (kotlin-mode--try-backward-type)) + (progn + (kotlin-mode--try-backward-type-modifiers) + t) + (goto-char pos) + nil))) + +(defun kotlin-mode--try-backward-type () + "Try backward type but not type modifiers. + +Return non-nil if succeeds. Keep position and return nil otherwise." + ;; type + ;; : typeModifiers? + ;; ( parenthesizedType + ;; | nullableType + ;; | typeReference + ;; | functionType) + ;; ; + ;; + ;; typeReference + ;; : userType + ;; | DYNAMIC + ;; ; + ;; + ;; userType + ;; : simpleUserType (NL* DOT NL* simpleUserType)* + ;; ; + ;; + ;; simpleUserType + ;; : simpleIdentifier (NL* typeArguments)? + ;; ; + ;; + ;; functionType + ;; : (receiverType NL* DOT NL*)? functionTypeParameters NL* ARROW NL* type + ;; ; + (let ((pos (point)) + (previous-token (kotlin-mode--backward-token-simple)) + result) + (setq + result + (cond + ;; parenthesizedType + ((equal (kotlin-mode--token-text previous-token) ")") + (goto-char (kotlin-mode--token-end previous-token)) + (condition-case nil + (progn + (backward-list) + t) + (scan-error + (goto-char pos) + nil))) + + ;; nullableType + ((equal (kotlin-mode--token-text previous-token) "?") + (kotlin-mode--try-backward-type)) + + ;; typeReference + (t + (goto-char (kotlin-mode--token-end previous-token)) + (kotlin-mode--try-backward-type-reference)))) + ;; functionType + (while (and result + (equal (kotlin-mode--token-text + (save-excursion (kotlin-mode--backward-token-simple))) + "->")) + ;; Skip "->" + (kotlin-mode--backward-token-simple) + ;; Skip parameters and a receiver type if any. + (setq result (condition-case nil + (progn + (backward-list) + (kotlin-mode--try-backward-receiver-type) + t) + (scan-error + nil)))) + (unless result + (goto-char pos)) + result)) + +(defun kotlin-mode--try-forward-type-reference () + "Try forward type reference. + +Return non-nil if succeeds. Keep position and return nil otherwise." + ;; typeReference + ;; : userType + ;; | DYNAMIC + ;; ; + ;; userType + ;; : simpleUserType (NL* DOT NL* simpleUserType)* + ;; ; + ;; + ;; simpleUserType + ;; : simpleIdentifier (NL* typeArguments)? + ;; ; + (let ((pos (point)) + result) + (setq result (kotlin-mode--forward-simple-user-type)) + (while (and result + (equal (kotlin-mode--token-text + (save-excursion (kotlin-mode--forward-token-simple))) + ".")) + ;; Skip "." + (kotlin-mode--forward-token-simple) + (setq result (kotlin-mode--forward-simple-user-type))) + (unless result + (goto-char pos)) + result)) + +(defun kotlin-mode--forward-simple-user-type () + "Forward simple user type. + +Return non-nil if succeeds. Otherwise, return nil and the point is undefined." + ;; simpleUserType + ;; : simpleIdentifier (NL* typeArguments)? + ;; ; + (let ((next-token (save-excursion (kotlin-mode--forward-token-simple)))) + (if (eq (kotlin-mode--token-type next-token) 'atom) + ;; Found simpleIdentifier + (progn + (goto-char (kotlin-mode--token-end next-token)) + (when (equal (kotlin-mode--token-text + (save-excursion (kotlin-mode--forward-token-simple))) + "<") + ;; Maybe type paramters + (kotlin-mode--try-forward-type-parameters)) + t) + ;; Not a simpleUserType + nil))) + +(defun kotlin-mode--try-backward-type-reference () + "Try backward type reference. + +Return non-nil if succeeds. Keep position and return nil otherwise." + ;; typeReference + ;; : userType + ;; | DYNAMIC + ;; ; + ;; userType + ;; : simpleUserType (NL* DOT NL* simpleUserType)* + ;; ; + ;; + ;; simpleUserType + ;; : simpleIdentifier (NL* typeArguments)? + ;; ; + (let ((pos (point)) + result) + (setq result (kotlin-mode--backward-simple-user-type)) + (while (and result + (equal (kotlin-mode--token-text + (save-excursion (kotlin-mode--backward-token-simple))) + ".")) + ;; Skip "." + (kotlin-mode--backward-token-simple) + (setq result (kotlin-mode--backward-simple-user-type))) + (unless result + (goto-char pos)) + result)) + +(defun kotlin-mode--backward-simple-user-type () + "Backward simple user type. + +Return non-nil if succeeds. Otherwise, return nil and the point is undefined." + ;; simpleUserType + ;; : simpleIdentifier (NL* typeArguments)? + ;; ; + (let ((pos (point)) + (previous-token (save-excursion (kotlin-mode--backward-token-simple)))) + (cond + ;; simpleUserType with type arguments + ((equal (kotlin-mode--token-text previous-token) ">") + (if (and (kotlin-mode--try-backward-type-parameters) + (eq (kotlin-mode--token-type + (kotlin-mode--backward-token-simple)) + 'atom)) + t + (goto-char pos) + nil)) + + ;; Start of an annotation + ((eq (kotlin-mode--token-type previous-token) 'annotation) + (goto-char (kotlin-mode--token-start previous-token)) + (forward-char) + t) + + ;; Other simpleUserType + ((eq (kotlin-mode--token-type previous-token) 'atom) + (goto-char (kotlin-mode--token-start previous-token)) t) - (t nil)))) + ;; Not a simpleUserType + (t + nil)))) + +(defun kotlin-mode--try-backward-type-modifiers () + "Try backward type modifiers. + +Keep position if failed." + (let ((previous-token (save-excursion (kotlin-mode--backward-token-simple)))) + (while (or + (eq (kotlin-mode--token-type previous-token) 'annotation) + (equal (kotlin-mode--token-text previous-token) "suspend")) + (goto-char (kotlin-mode--token-start previous-token)) + (setq previous-token + (save-excursion (kotlin-mode--backward-token-simple)))))) + +(defun kotlin-mode--multi-variable-declaration-p () + "Return non-nil if the point is before multi variable declaration." + ;; Example: + ;; + ;; val (x, y) = foo() + ;; var (x, y) = foo() + ;; val (x, y) = foo() + ;; val (A).(x, y) = foo() + ;; val (A).(x, y) = foo() + ;; val @AAA A.(x, y) = foo() + ;; val @AAA A.B.C.(x, y) = foo() + (save-excursion + (kotlin-mode--try-backward-receiver-type) + (kotlin-mode--try-backward-type-parameters) + (member (kotlin-mode--token-text + (kotlin-mode--backward-token-simple)) + '("val" "var")))) + +(defun kotlin-mode--constructor-delegation-call-p () + "Return non-nil if the point is before constructor delegation call arguments." + ;; Example: + ;; + ;; constructor (): this(1) + ;; constructor (): super(1) + (save-excursion + (and (member (kotlin-mode--token-text (kotlin-mode--backward-token-simple)) + '("this" "super")) + (eq (kotlin-mode--token-type (kotlin-mode--backward-token-simple)) + ':)))) + +(defun kotlin-mode--try-forward-modifiers () + "Try forward modifiers. + +Keep position if failed." + (let ((next-token (save-excursion (kotlin-mode--forward-token-simple)))) + (while (or + (eq (kotlin-mode--token-type next-token) 'annotation) + (member (kotlin-mode--token-text next-token) + kotlin-mode--modifier-keywords)) + (goto-char (kotlin-mode--token-end next-token)) + (setq next-token (save-excursion (kotlin-mode--forward-token-simple)))))) + +(defun kotlin-mode--try-backward-modifiers () + "Try forward modifiers. + +Keep position if failed." + (let ((previous-token (save-excursion (kotlin-mode--backward-token-simple)))) + (while (or + (eq (kotlin-mode--token-type previous-token) 'annotation) + (member (kotlin-mode--token-text previous-token) + kotlin-mode--modifier-keywords)) + (goto-char (kotlin-mode--token-start previous-token)) + (setq previous-token + (save-excursion (kotlin-mode--backward-token-simple)))))) + +(defun kotlin-mode--backward-token-or-list (&optional use-backward-token-simple) + "Move point to the start position of the previous token or list. + +Return the token skipped. + +If USE-BACKWARD-TOKEN-SIMPLE is non-nil, use +`kotlin-mode--backward-token-simple' instead of +`kotlin-mode--backward-token'." + (let* ((previous-token (if use-backward-token-simple + (kotlin-mode--backward-token-simple) + (kotlin-mode--backward-token))) + (previous-type (kotlin-mode--token-type previous-token)) + (previous-text (kotlin-mode--token-text previous-token)) + (previous-start (kotlin-mode--token-start previous-token)) + (previous-end (kotlin-mode--token-end previous-token))) + (cond + ;; Maybe list + ((memq previous-type '(} \) \] \)-before-control-structure-body)) + (goto-char previous-end) + (condition-case nil + (progn + (backward-list) + (make-instance + 'kotlin-mode--token + :type (assoc-default + previous-type + '((} . {}) + (\) . \(\)) + (\)-before-control-structure-body . + \(\)-before-control-structure-body) + (\] . \[\]))) + :text (buffer-substring-no-properties (point) previous-end) + :start (point) + :end previous-end)) + (scan-error + (goto-char previous-start) + previous-token))) + + ;; Maybe type parameter list + ((equal previous-text ">") + (goto-char previous-end) + (if (kotlin-mode--try-backward-type-parameters) + (make-instance + 'kotlin-mode--token + :type '<> + :text (buffer-substring-no-properties (point) previous-end) + :start (point) + :end previous-end) + (goto-char previous-start) + previous-token)) + + ;; Other token + (t previous-token)))) + +(defun kotlin-mode--forward-token-or-list (&optional use-forward-token-simple) + "Move point to the end position of the next token or list. + +Return the token skipped. + +If USE-FORWARD-TOKEN-SIMPLE is non-nil, use +`kotlin-mode--forward-token-simple' instead of +`kotlin-mode--forward-token'." + (let* ((next-token (if use-forward-token-simple + (kotlin-mode--forward-token-simple) + (kotlin-mode--forward-token))) + (next-type (kotlin-mode--token-type next-token)) + (next-text (kotlin-mode--token-text next-token)) + (next-start (kotlin-mode--token-start next-token)) + (next-end (kotlin-mode--token-end next-token)) + result-type) + (cond + ;; Maybe list + ((memq next-type '({ \( \[)) + (goto-char next-start) + (condition-case nil + (progn + (forward-list) + (setq + result-type + (if (kotlin-mode--close-parenthesis-before-control-structure-body-p + (save-excursion (kotlin-mode--backward-token-simple))) + '\(\)-before-control-structure-body + (assoc-default next-type '(({ . {}) + (\( . \(\)) + (\[ . \[\]))))) + (make-instance + 'kotlin-mode--token + :type result-type + :text (buffer-substring-no-properties next-start (point)) + :start next-start + :end (point))) + (scan-error + (goto-char next-end) + next-token))) + + ;; Maybe type parameter list + ((equal next-text "<") + (goto-char next-start) + (if (kotlin-mode--try-forward-type-parameters) + (make-instance + 'kotlin-mode--token + :type '<> + :text (buffer-substring-no-properties next-start (point)) + :start next-start + :end (point)) + (goto-char next-end) + next-token)) + + ;; Other token + (t next-token)))) + +(defun kotlin-mode--try-backward-type-parameters () + "Move point to the start of the type parameter list. + +Return non-nil if succeeds. Keep position and return nil otherwise. + +It is a type parameter list if: +- it has matching angle brackets, and +- it does not have tokens that cannot appears in a type parameter list." + (let ((pos (point))) + (if (and + (equal (kotlin-mode--token-text (kotlin-mode--backward-token-simple)) + ">") + (kotlin-mode--try-skip-type-parameters + (lambda () (kotlin-mode--backward-token-or-list t)) + "<" ">")) + t + (goto-char pos) + nil))) + +(defun kotlin-mode--try-forward-type-parameters () + "Move point to the end of the type parameter list. + +Return non-nil if succeeds. Keep position and return nil otherwise. + +It is a type parameter list if: +- it has matching angle brackets, and +- it does not have tokens that cannot appears in a type parameter list." + (let ((pos (point))) + (if (and + (equal (kotlin-mode--token-text (kotlin-mode--forward-token-simple)) + "<") + (kotlin-mode--try-skip-type-parameters + (lambda () (kotlin-mode--forward-token-or-list t)) + ">" "<")) + t + (goto-char pos) + nil))) + +(defconst kotlin-mode--tokens-not-in-type-parameter-list + ;; Whitelisting tend to be fragile. So we list tokens that are + ;; unlikely to appear in type parameter lists in the current + ;; version and future ones. + ;; + ;; Example of type parameters: + ;; < + ;; A: B, + ;; @AAA(aaa) reified in C: @AAA suspend D.(x: Int) -> (X)? + ;; > + ;; + ;; Example of type arguments + ;; < + ;; A, + ;; @AAA(aaa) in *, + ;; suspend D.(x: Int) -> (X)? + ;; > + ;; + ;; We don't need to consider the contents of inner brackets because + ;; they are skipped by `kotlin-mode--backward-token-or-list'. + ;; + ;; String literals and numbers are also excluded by + ;; `kotlin-mode--try-skip-type-parameters'. + `(outside-of-buffer + \; + { } \( \) \[ \] + "%" "/" "+" "-" + "++" "--" + "&&" "||" + "!==" "!=" + ;; exclude "in" + "is" "!is" "!in" "as" "as?" + "!" + "=" "+=" "-=" "*=" "/=" "%=" + ".." "::" "?:" "?." "<=" ">=" "==" "===" + "package" "import" "class" "interface" "fun" "object" "val" "var" + "typealias" "constructor" "by" "companion" "init" "if" "else" "when" + "try" "catch" "finally" "for" "do" "while" "throw" + return continue break "true" "false" "null" + ,@(remove "suspend" kotlin-mode--modifier-keywords))) + +(defun kotlin-mode--try-skip-type-parameters + (skip-token-or-list-function matching-bracket-text unmatching-bracket-text) + "Skip type parameters if the point is just before/after one. + +Return non-nil if succeeds. Keep position and return nil otherwise. + +Assuming open/close bracket is already skipped. + +SKIP-TOKEN-OR-LIST-FUNCTION is a function skipping forward/backward a +token or a list. +MATCHING-BRACKET-TEXT is a text of the matching bracket. +UNMATCHING-BRACKET-TEXT is a text of the current bracket." + (let ((pos (point)) + result + (prohibited-tokens (cons + unmatching-bracket-text + kotlin-mode--tokens-not-in-type-parameter-list)) + (next-token (funcall skip-token-or-list-function))) + (while + (cond + ;; Prohibited tokens + ((or + (memq (kotlin-mode--token-type next-token) prohibited-tokens) + (member (kotlin-mode--token-text next-token) prohibited-tokens) + (string-match-p "^[\"'0-9]" (kotlin-mode--token-text next-token))) + ;; Not a type parameter list. + ;; Return to the initial position and stop the loop. + (goto-char pos) + (setq result nil) + nil) + + ;; Matching bracket + ((equal (kotlin-mode--token-text next-token) matching-bracket-text) + ;; Found the matching open angle bracket. Stop the loop. + (setq result t) + nil) + + ;; Otherwise, keep scanning + (t t)) + (setq next-token (funcall skip-token-or-list-function))) + result)) + +(defun kotlin-mode--forward-token () + "Move point forward to the next position of the end of a token. + +Return the token object. If no more tokens available, return a token with +type `outside-of-buffer'." + (let ((pos (point))) + ;; Skip comments and whitespaces. + (let ((chunk (kotlin-mode--chunk-after))) + ;; If point is inside a comment, go back to the beginning of the + ;; comment before skipping it. + (when (kotlin-mode--chunk-comment-p chunk) + (goto-char (kotlin-mode--chunk-start chunk)))) + (forward-comment (point-max)) + (cond + ;; Outside of buffer + ((eobp) + (make-instance 'kotlin-mode--token + :type 'outside-of-buffer + :text "" + :start (point) + :end (point))) + + ;; Implicit semicolon + ((and + ;; Check (forward-comment (point-max)) skipped a newline. + (< pos + (save-excursion + (kotlin-mode--goto-non-comment-bol) + (point))) + (save-excursion (goto-char pos) (kotlin-mode--implicit-semi-p))) + (make-instance 'kotlin-mode--token + :type 'implicit-\; + :text (buffer-substring-no-properties pos (point)) + :start pos + :end (point))) + + (t + (let ((token (kotlin-mode--extend-annotation-token-forward + (kotlin-mode--forward-token-simple)))) + (cond + ;; Close parenthesis of "if ()", "while ()", or "for ()" + ((kotlin-mode--close-parenthesis-before-control-structure-body-p token) + (make-instance 'kotlin-mode--token + :type '\)-before-control-structure-body + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + ;; "else" not followed by "if" on the same line or "{" + ((kotlin-mode--bare-else-p token) + (make-instance 'kotlin-mode--token + :type 'bare-else + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + ;; super@label + ((save-excursion + (and (equal (kotlin-mode--token-text token) "super") + (eq (char-after) ?<) + (kotlin-mode--try-forward-type-parameters) + (eq (char-after) ?@))) + (kotlin-mode--try-forward-type-parameters) + (forward-char) + (skip-syntax-forward "w") + (make-instance 'kotlin-mode--token + :type 'super + :text (buffer-substring-no-properties + (kotlin-mode--token-start token) + (point)) + :start (kotlin-mode--token-start token) + :end (point))) + + ;; "->" of lambda parameters + ((kotlin-mode--anonymous-function-parameter-arrow-p token) + (make-instance 'kotlin-mode--token + :type 'anonymous-function-parameter-arrow + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + ;; "->" of when expression + ((kotlin-mode--when-expression-arrow-p token) + (make-instance 'kotlin-mode--token + :type 'when-expression-arrow + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + (t token))))))) + +(defun kotlin-mode--bare-else-p (token) + "Return non-nil if TOKEN is bare \"else\" token. + +A bare \"else\" token is a \"else\" token not followed by \"if\" on +the same line, \"->\", or \"{\"." + (let ((next-token (save-excursion + (goto-char (kotlin-mode--token-end token)) + (kotlin-mode--forward-token-simple)))) + (and (equal (kotlin-mode--token-text token) "else") + (not (eq (kotlin-mode--token-type next-token) '{)) + (not (eq (kotlin-mode--token-text next-token) "->")) + (not (and + (equal (kotlin-mode--token-text next-token) "if") + (save-excursion + (= (progn + (goto-char (kotlin-mode--token-start next-token)) + (kotlin-mode--goto-non-comment-bol) + (point)) + (progn + (goto-char (kotlin-mode--token-start token)) + (kotlin-mode--goto-non-comment-bol) + (point))))))))) + +(defun kotlin-mode--anonymous-function-parameter-arrow-p (token) + "Return non-nil if TOKEN is an arrow of lambda parameters." + ;; Examples: + ;; + ;; { -> 1} + ;; { x -> 1 } + ;; { f: (Int) -> Int, x: Int -> f(x) } + ;; { f: (Int) -> Int -> f(1) } + ;; { f: (Int) -> (Int) -> Int -> f(1) } + ;; { (x: Int, y: Int) -> x + y } + ;; + ;; If an arrow is not preceded by a close parenthesis, it is an end + ;; of parameters. + ;; + ;; If an arrow is preceded by a close parenthesis and the + ;; parentheses are preceded by an open curly bracket, the arrow is + ;; an end of parameters. + ;; + ;; Otherwise, it is an arrow in function types. + ;; + ;; Furthermore, the curly open bracket should not be preceded by + ;; "when ()". + (and (equal (kotlin-mode--token-text token) "->") + (save-excursion + (goto-char (kotlin-mode--token-start token)) + (forward-comment (- (point))) + (or + ;; The arrow is not preceded by a close parenthesis. + ;; + ;; Examples: + ;; { -> 1} + ;; { x -> 1 } + ;; { f: (Int) -> Int, x: Int -> f(x) } // last arrow + ;; { f: (Int) -> Int -> f(1) } // last arrow + ;; { f: (Int) -> (Int) -> Int -> f(1) } // last arrow + (not (eq (char-before) ?\))) + ;; The parentheses are preceded by an open curly bracket. + ;; + ;; Example: + ;; { (x: Int, y: Int) -> x + y } + (condition-case nil + (progn (backward-list) + (forward-comment (- (point))) + (eq (char-before) ?{)) + (scan-error nil)))) + (save-excursion + ;; The token is inside a curly brackets but not inside a + ;; when-expression. + (goto-char (kotlin-mode--token-start token)) + (let ((containing-bracket + (kotlin-mode--find-containing-brackets (point)))) + (and (eq (car containing-bracket) '{) + (not (kotlin-mode--inside-when-expression-p + containing-bracket))))))) + +(defun kotlin-mode--when-expression-arrow-p (token) + "Return non-nil if TOKEN is an arrow of when expression." + ;; This function does not distinguish arrows of when expression and + ;; ones of function types. + (and (equal (kotlin-mode--token-text token) "->") + (save-excursion + (goto-char (kotlin-mode--token-start token)) + (kotlin-mode--inside-when-expression-p)))) + +(defun kotlin-mode--inside-when-expression-p (&optional containing-bracket) + "Return non-nil if point is inside a when expression. + +CONTAINING-BRACKET is a return value of +`kotlin-mode--find-containing-brackets'. If ommitted, +\(kotlin-mode--find-containing-brackets (point)) is used." + (unless containing-bracket + (setq containing-bracket (kotlin-mode--find-containing-brackets (point)))) + (goto-char (cdr containing-bracket)) + (and (eq (car containing-bracket) '{) + (eq (kotlin-mode--token-type (kotlin-mode--backward-token-or-list t)) + '\(\)) + (equal (kotlin-mode--token-text (kotlin-mode--backward-token-simple)) + "when"))) + +(defun kotlin-mode--forward-token-simple () + "Like `kotlin-mode--forward-token' without recursion. + +This function does not return `implicit-;'." + (forward-comment (point-max)) + (cond + ;; Outside of buffer + ((eobp) + (make-instance 'kotlin-mode--token + :type 'outside-of-buffer + :text "" + :start (point) + :end (point))) + + ;; End of template expression + ((and (eq (char-after) ?\}) + (equal (get-text-property (point) 'syntax-table) + (string-to-syntax "|"))) + (let ((pos-after-comment (point))) + (kotlin-mode--forward-string-chunk) + (make-instance + 'kotlin-mode--token + :type 'string-chunk-after-template-expression + :text (buffer-substring-no-properties pos-after-comment (point)) + :start pos-after-comment + :end (point)))) + + ;; :: + ((looking-at "::") + (forward-char 2) + (make-instance 'kotlin-mode--token + :type 'operator + :text "::" + :start (- (point) 2) + :end (point))) + + ;; Separators and parentheses + ((memq (char-after) '(?, ?\; ?\{ ?\} ?\[ ?\] ?\( ?\) ?:)) + (forward-char) + (make-instance 'kotlin-mode--token + :type (intern (string (char-before))) + :text (string (char-before)) + :start (1- (point)) + :end (point))) + + ;; Open angle bracket for type parameters or type arguments + ;; + ;; We use a heuristic: spaces are inserted around inequality sign, but not + ;; for angle bracket, and a type parameter starts with upper case + ;; characters, parentheses, asterisk, keyword 'reified', keyword 'in', + ;; keyword 'out', keyword 'suspend', or annotations. + ((and (eq (char-after) ?<) + (looking-at + (rx (seq "<" (or (any "_(@[*" upper) + (seq (or "reified" "in" "out" "suspend") + word-end)))))) + (forward-char) + (make-instance 'kotlin-mode--token + :type '< + :text "<" + :start (1- (point)) + :end (point))) + + ;; Close angle bracket for type parameters or type arguments + ;; + ;; Close angle bracket follows identifiers, parentheses, question symbols, + ;; or other angle brackets (e.g. Foo>) + ((and (eq (char-after) ?>) + (save-excursion + ;; You know that regular languages can be reversed. Thus you may + ;; think that `looking-back' reverses the given regexp and scans + ;; chars backwards. Nevertheless, `looking-back' function does not + ;; do that. It just repeats `looking-at' with decrementing start + ;; position until it succeeds. The document says that it is not + ;; recommended to use. So we use combination of + ;; `skip-chars-backward', `skip-syntax-backward', and + ;; `looking-at' here. + (skip-chars-backward "?)>") + (skip-syntax-backward "w") + (looking-at "[[:upper:]_]"))) + (forward-char) + (make-instance 'kotlin-mode--token + :type '> + :text ">" + :start (1- (point)) + :end (point))) + + ;; Operator (other than as, in, or is) + ((looking-at + (rx + (or + "++" "--" + "&&" "||" + "!==" "!=" + (seq "!is" word-end) (seq "!in" word-end) "as?" + "!" + "=" "+=" "-=" "*=" "/=" "%=" + "->" + ".." "." "?:" "?." "?" "<" ">" "<=" ">=" "==" "===" + "*" "%" "/" "+" "-"))) + (let ((text (match-string-no-properties 0)) + (start (match-beginning 0)) + (end (match-end 0))) + (goto-char end) + (make-instance 'kotlin-mode--token + :type 'operator + :text text + :start start + :end end))) + + ;; Backquoted identifier or character literal + ((memq (char-after) '(?` ?')) + (let ((pos-after-comment (point))) + (kotlin-mode--forward-string-chunk) + (make-instance + 'kotlin-mode--token + :type 'atom + :text (buffer-substring-no-properties pos-after-comment (point)) + :start pos-after-comment + :end (point)))) + + ;; String + ((looking-at "\"") + (let ((pos-after-comment (point))) + (forward-char) + (kotlin-mode--end-of-string) + (make-instance + 'kotlin-mode--token + :type 'atom + :text (buffer-substring-no-properties pos-after-comment (point)) + :start pos-after-comment + :end (point)))) + + ;; Part of annotations. Only @ and first identifier. + ;; + ;; This will be augmented by + ;; `kotlin-mode--extend-annotation-token-forward' later. + ((eq (char-after) ?@) + (let ((pos-after-comment (point))) + (forward-char) + (if (eq (char-after) ?`) + (kotlin-mode--forward-string-chunk) + (skip-syntax-forward "w")) + ;; Strictly speaking, single at sign is not an annotation, but + ;; we treat it as an annotation. + (make-instance + 'kotlin-mode--token + :type 'annotation + :text (buffer-substring-no-properties pos-after-comment (point)) + :start pos-after-comment + :end (point)))) + + ;; Other tokens including identifiers, keywords, labels, and numbers. + ;; + ;; Note that we parse 123.456e+2 as "123" "." "456e" "+" "2". + (t + (let* ((pos-after-comment (point)) + (text + (cond + ;; return@label, continue@label, break@label, this@label, + ;; or super@label. + ((looking-at (rx (or "return@" + "continue@" + "break@" + "this@" + "super@"))) + (skip-syntax-forward "w") + (forward-char) + (skip-syntax-forward "w") + (buffer-substring-no-properties pos-after-comment (point))) + + ;; Identifiers, keywords, labels, or numbers + ((eq (syntax-class (syntax-after (point))) + (syntax-class (string-to-syntax "w"))) + (skip-syntax-forward "w") + ;; Skip an at sign if exists for label. + (when (eq (char-after) ?@) + (forward-char)) + (buffer-substring-no-properties pos-after-comment (point))) + ;; Unknown character type. Treats as a single-letter token. + (t + (forward-char) + (string (char-before))))) + (type (cond + ((member text '("is" "in" "as")) + ;; Note that "as?" is already handled. + 'operator) + + ((eq (aref text (1- (length text))) ?@) + 'label) + + ((string-match "^return\\>" text) + 'return) + + ((string-match "^continue\\>" text) + 'return) + + ((string-match "^break\\>" text) + 'break) + + ((string-match "^this\\>" text) + 'this) + + ((string-match "^super\\>" text) + 'super) + + (t + 'atom)))) + (make-instance 'kotlin-mode--token + :type type + :text text + :start (- (point) (length text)) + :end (point)))))) + +(defun kotlin-mode--extend-annotation-token-forward (token) + "Return annotation token if TOKEN is the start of an annotation. + +TOKEN is assumed to be a return value of `kotlin-mode--forward-token-simple'." + (if (eq (kotlin-mode--token-type token) 'annotation) + (let ((pos (point))) + (cond + ;; Example: @file:Abc(def) + ;; Example: @file:[Abc Def Ghi] + ((save-excursion + (forward-comment (point-max)) + (eq (char-after) ?:)) + (forward-comment (point-max)) + (forward-char) + (unless (or (kotlin-mode--try-forward-multi-annotation-bracket) + (kotlin-mode--try-forward-unescaped-annotation)) + (goto-char pos))) + + ;; Example: @[Abc Def Ghi] + ((equal (kotlin-mode--token-text token) "@") + (kotlin-mode--try-forward-multi-annotation-bracket)) + + ;; Example: @Abc(def) + (t + (goto-char (1+ (kotlin-mode--token-start token))) + (kotlin-mode--try-forward-unescaped-annotation))) + (make-instance 'kotlin-mode--token + :type 'annotation + :text (buffer-substring-no-properties + (kotlin-mode--token-start token) + (point)) + :start (kotlin-mode--token-start token) + :end (point))) + token)) + +(defun kotlin-mode--try-forward-multi-annotation-bracket () + "Move point forward to the end of square brackets. + +Return non-nil if succeeds. Keep position and return nil otherwise." + (let ((pos (point))) + (when (save-excursion + (forward-comment (point-max)) + (eq (char-after) ?\[)) + (condition-case nil + (progn + (forward-list) + t) + (scan-error + (goto-char pos) + nil))))) + +(defun kotlin-mode--try-forward-unescaped-annotation () + "Move point forward to the end of unescapedAnnotation. + +Skip userType and constructor arguments if exists. + +Return non-nil if succeeds. Keep position and return nil otherwise." + (if (kotlin-mode--try-forward-type-reference) + (let ((pos (point))) + (when (save-excursion + ;; Line breaks are prohibited before bracket. + (kotlin-mode--forward-comments-but-not-line-breaks) + (eq (char-after) ?\()) + (condition-case nil + (forward-list) + (scan-error + (goto-char pos)))) + t) + nil)) + +(defun kotlin-mode--backward-token () + "Move point backward to the previous position of the end of a token. + +Return the token object. If no more tokens available, return a token with +type `outside-of-buffer'." + (let ((pos (point))) + ;; Skip comments and whitespaces. + (let ((chunk (kotlin-mode--chunk-after))) + (when (kotlin-mode--chunk-comment-p chunk) + (goto-char (kotlin-mode--chunk-start chunk)))) + (forward-comment (- (point))) + (cond + ;; Outside of buffer + ((bobp) + (make-instance 'kotlin-mode--token + :type 'outside-of-buffer + :text "" + :start (point) + :end (point))) + + ;; Implicit semicolon + ((and + ;; Check (forward-comment (- (point))) skipped a newline. + (< (save-excursion + (kotlin-mode--goto-non-comment-eol) + (point)) + pos) + (save-excursion (goto-char pos) (kotlin-mode--implicit-semi-p))) + (make-instance 'kotlin-mode--token + :type 'implicit-\; + :text (buffer-substring-no-properties (point) pos) + :start (point) + :end pos)) + + (t + (let ((token (kotlin-mode--extend-annotation-token-backward + (kotlin-mode--backward-token-simple)))) + (cond + ;; Close parenthesis of "if ()", "while ()", or "for ()" + ((kotlin-mode--close-parenthesis-before-control-structure-body-p token) + (make-instance 'kotlin-mode--token + :type '\)-before-control-structure-body + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + ;; "else" not followed by "if" on the same line or "{" + ((kotlin-mode--bare-else-p token) + (make-instance 'kotlin-mode--token + :type 'bare-else + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + ;; super@label + ((save-excursion + (and (eq (kotlin-mode--token-type token) 'annotation) + (eq (char-before) ?>) + (kotlin-mode--try-backward-type-parameters) + (equal (kotlin-mode--token-text + (kotlin-mode--backward-token-simple)) + "super"))) + (kotlin-mode--try-backward-type-parameters) + (kotlin-mode--backward-token-simple) + (make-instance 'kotlin-mode--token + :type 'super + :text (buffer-substring-no-properties + (point) + (kotlin-mode--token-end token)) + :start (point) + :end (kotlin-mode--token-end token))) + + ;; "->" of lambda parameters + ((kotlin-mode--anonymous-function-parameter-arrow-p token) + (make-instance 'kotlin-mode--token + :type 'anonymous-function-parameter-arrow + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + ;; "->" of when expression + ((kotlin-mode--when-expression-arrow-p token) + (make-instance 'kotlin-mode--token + :type 'when-expression-arrow + :text (kotlin-mode--token-text token) + :start (kotlin-mode--token-start token) + :end (kotlin-mode--token-end token))) + + (t token))))))) + +(defun kotlin-mode--backward-token-simple () + "Like `kotlin-mode--backward-token' without recursion. + +This function does not return `implicit-;'" + (forward-comment (- (point))) + (cond + ;; Outside of buffer + ((bobp) + (make-instance 'kotlin-mode--token + :type 'outside-of-buffer + :text "" + :start (point) + :end (point))) + + ;; Beginning of template expression + ((and (eq (char-before) ?\{) + (equal (get-text-property (1- (point)) 'syntax-table) + (string-to-syntax "|"))) + (let ((pos-before-comment (point))) + (kotlin-mode--backward-string-chunk) + (make-instance + 'kotlin-mode--token + :type 'string-chunk-before-template-expression + :text (buffer-substring-no-properties (point) pos-before-comment) + :start (point) + :end pos-before-comment))) + + ;; ::, ?: + ((member (buffer-substring-no-properties + (max (point-min) (- (point) 2)) + (point)) + '("::" "?:")) + (backward-char 2) + (make-instance 'kotlin-mode--token + :type 'operator + :text (buffer-substring-no-properties (point) (+ 2 (point))) + :start (point) + :end (+ 2 (point)))) + + ;; Separators and parentheses + ((memq (char-before) '(?, ?\; ?\{ ?\} ?\[ ?\] ?\( ?\) ?:)) + (backward-char) + (make-instance 'kotlin-mode--token + :type (intern (string (char-after))) + :text (string (char-after)) + :start (point) + :end (1+ (point)))) + + ;; Open angle bracket for type parameters + ;; + ;; We use a heuristic: spaces are inserted around inequality sign, + ;; but not for angle bracket, and a type parameter starts with an + ;; upper case characters, parenthesis, or keyword 'reified', + ;; keyword 'in', keyword 'out', keyword 'suspend', or annotations. + ((and (eq (char-before) ?<) + (looking-at + (rx (or (any "_(@[*" upper) + (seq (or "reified" "in" "out") + word-end))))) + (backward-char) + (make-instance 'kotlin-mode--token + :type '< + :text "<" + :start (point) + :end (1+ (point)))) + + ;; Close angle bracket for type parameters + ;; + ;; Close angle bracket follows identifier, parentheses, question symbols, + ;; or other angle brackets (e.g. Foo>) + ((and (eq (char-before) ?>) + (save-excursion + (skip-chars-backward "?)>") + (skip-syntax-backward "w") + (looking-at "[[:upper:]_]"))) + (backward-char) + (make-instance 'kotlin-mode--token + :type '> + :text ">" + :start (point) + :end (1+ (point)))) + + ;; Operator (3 letters) + ((member (buffer-substring-no-properties + (max (point-min) (- (point) 3)) + (point)) + '("===" "!==" "!is" "!in")) + (backward-char 3) + (make-instance 'kotlin-mode--token + :type 'operator + :text (buffer-substring-no-properties (point) (+ 3 (point))) + :start (point) + :end (+ 3 (point)))) + + ;; Operator (2 letters, other than as, in, or is) + ((member (buffer-substring-no-properties + (max (point-min) (- (point) 2)) + (point)) + '("++" "--" + "&&" "||" + "+=" "-=" "*=" "/=" "%=" + ".." "?." + "<=" ">=" "!=" "==" + "->")) + (backward-char 2) + (make-instance 'kotlin-mode--token + :type 'operator + :text (buffer-substring-no-properties (point) (+ 2 (point))) + :start (point) + :end (+ 2 (point)))) + + ;; ? or as? + ((eq (char-before) ??) + (let ((pos-before-comment (point))) + (backward-char) + (skip-syntax-backward "w") + (unless (looking-at (rx "as?")) + (goto-char pos-before-comment) + (backward-char)) + (make-instance + 'kotlin-mode--token + :type 'operator + :text (buffer-substring-no-properties (point) pos-before-comment) + :start (point) + :end pos-before-comment))) + + ;; Operator (1 letter) + ((member (buffer-substring-no-properties + (max (point-min) (- (point) 1)) + (point)) + '("*" "%" "/" "+" "-" "!" "=" "." "?" "<" ">")) + (backward-char) + (make-instance 'kotlin-mode--token + :type 'operator + :text (buffer-substring-no-properties (point) (1+ (point))) + :start (point) + :end (1+ (point)))) + + ;; Backquoted identifier + ((eq (char-before) ?`) + (let ((pos-before-comment (point))) + (kotlin-mode--backward-string-chunk) + (when (eq (char-before) ?@) + (backward-char)) + (make-instance + 'kotlin-mode--token + :type (if (eq (char-after) ?@) 'annotation 'atom) + :text (buffer-substring-no-properties (point) pos-before-comment) + :start (point) + :end pos-before-comment))) + + ;; character literal + ((eq (char-before) ?') + (let ((pos-before-comment (point))) + (kotlin-mode--backward-string-chunk) + (make-instance + 'kotlin-mode--token + :type 'atom + :text (buffer-substring-no-properties (point) pos-before-comment) + :start (point) + :end pos-before-comment))) + + ;; String + ((eq (char-before) ?\") + (let ((pos-before-comment (point))) + (backward-char) + (kotlin-mode--beginning-of-string) + (make-instance + 'kotlin-mode--token + :type 'atom + :text (buffer-substring-no-properties (point) pos-before-comment) + :start (point) + :end pos-before-comment))) + + ;; Other tokens including identifiers, keywords, labels, numbers, + ;; and annotations without bracket. + ;; + ;; Note that we parse 123.456e+2 as "123" "." "456e" "+" "2". + (t + (let* ((pos-before-comment (point)) + (text + (cond + ;; Labels (and single at sign) + ((eq (char-before) ?@) + (backward-char) + (skip-syntax-backward "w") + (buffer-substring-no-properties (point) pos-before-comment)) + + ;; Identifiers, keywords, numbers, (part of) annotations + ((eq (syntax-class (syntax-after (1- (point)))) + (syntax-class (string-to-syntax "w"))) + (skip-syntax-backward "w") + (when (eq (char-before) ?@) + ;; Annotation, return@ or something like it. + (backward-char) + (let ((pos (point))) + (skip-syntax-backward "w") + (unless (looking-at + (rx (or "return" + "continue" + "break" + "this" + "super"))) + (goto-char pos)))) + (buffer-substring-no-properties (point) pos-before-comment)) + + ;; Unknown character type. Treats as a single-letter token. + (t (backward-char) (string (char-after))))) + (type (cond + ((member text '("as" "in" "is")) + 'operator) + + ((string-prefix-p "@" text) + ;; Strictly speaking, single at sign is not an + ;; annotation, but we treat it as an annotation. + 'annotation) + + ((string-match "^return\\>" text) + 'return) + + ((string-match "^continue\\>" text) + 'return) + + ((string-match "^break\\>" text) + 'break) + + ((string-match "^this\\>" text) + 'this) + + ((string-match "^super\\>" text) + 'super) + + ((eq (aref text (1- (length text))) ?@) + 'label) + + (t + 'atom)))) + (make-instance 'kotlin-mode--token + :type type + :text text + :start (point) + :end (+ (point) (length text))))))) + +(defun kotlin-mode--extend-annotation-token-backward (token) + "Return annotation token if TOKEN is the end of an annotation. + +TOKEN is assumed to be a return value of `kotlin-mode--backward-token-simple'." + (let ((pos (point))) + (goto-char (kotlin-mode--token-end token)) + (cond + ;; Example: @[Abc Def Ghi] + ;; Example: @file:[Abc Def Ghi] + ((eq (char-before) ?\]) + (condition-case nil + (progn + (backward-list) + (forward-comment (- (point))) + (if (or (and (eq (char-before) ?@) (prog1 t (backward-char))) + (kotlin-mode--try-backward-annotation-use-site-target)) + (make-instance 'kotlin-mode--token + :type 'annotation + :text (buffer-substring-no-properties + (point) + (kotlin-mode--token-end token)) + :start (point) + :end (kotlin-mode--token-end token)) + ;; Not an annotation. + (goto-char pos) + token)) + (scan-error + ;; Not an annotation. + (goto-char pos) + token))) + + ;; Example: @Abc(def) + ;; Example: @file:Abc(def) + ;; Example: @Abc + ;; Example: @file:Abc + ;; Example: @Abc + ((or (memq (char-before) '(?\) ?` ?>)) + (eq (syntax-class (syntax-after (1- (point)))) + (syntax-class (string-to-syntax "w")))) + (when (eq (char-before) ?\)) + (condition-case nil + (progn + (backward-list) + ;; Line breaks are prohibited before bracket. + (kotlin-mode--backward-comments-but-not-line-breaks) + (unless (or (memq (char-before) '(?` ?>)) + (eq (syntax-class (syntax-after (1- (point)))) + (syntax-class (string-to-syntax "w")))) + (goto-char pos))) + (scan-error + (goto-char pos)))) + (if (and + (kotlin-mode--try-backward-type-reference) + (or (and (eq (char-before) ?@) (prog1 t (backward-char))) + (kotlin-mode--try-backward-annotation-use-site-target))) + (make-instance 'kotlin-mode--token + :type 'annotation + :text (buffer-substring-no-properties + (point) + (kotlin-mode--token-end token)) + :start (point) + :end (kotlin-mode--token-end token)) + ;; Not an annotation. + (goto-char pos) + token)) + + ;; Not an annotation. + (t + (goto-char pos) + token)))) + +(defun kotlin-mode--try-backward-annotation-use-site-target () + "Try backward annotationUseSiteTarget. + +Try to skip colon, word, and at sign. + +Return non-nil if succeeds. Keep position and return nil otherwise." + (let ((pos (point))) + (forward-comment (- (point))) + (if (eq (char-before) ?:) + (progn + (backward-char) + (forward-comment (- (point))) + (if (and (< (skip-syntax-backward "w") 0) + (eq (char-before) ?@)) + (progn + (backward-char) + t) + (goto-char pos) + nil)) + (goto-char pos) + nil))) + +(defun kotlin-mode--forward-string-chunk () + "Skip forward a string chunk. + +A string chunk is a part of single-line/multiline string delimited with +quotation marks or template expressions." + (condition-case nil + (goto-char (scan-sexps (point) 1)) + (scan-error (goto-char (point-max))))) + +(defun kotlin-mode--backward-string-chunk () + "Skip backward a string chunk. + +A string chunk is a part of single-line/multiline string delimited with +quotation marks or template expressions." + (condition-case nil + (goto-char (scan-sexps (point) -1)) + (scan-error (goto-char (point-min))))) + +(defun kotlin-mode--beginning-of-string () + "Move point to the beginning of single-line/multiline string. + +Return the point of the beginning. + +Assuming the point is on a string." + (goto-char (or (nth 8 (syntax-ppss)) (point))) + (let (matching-bracket) + (while (setq matching-bracket + (get-text-property + (point) + 'kotlin-property--matching-bracket)) + (goto-char matching-bracket) + (goto-char (nth 8 (syntax-ppss)))) + (point))) + +(defun kotlin-mode--end-of-string () + "Move point to the end of single-line/multiline string. + +Assuming the point is on a string." + (goto-char (or (nth 8 (syntax-ppss)) (point))) + (let (matching-bracket) + (kotlin-mode--forward-string-chunk) + (while (setq matching-bracket + (get-text-property + (1- (point)) + 'kotlin-property--matching-bracket)) + (goto-char matching-bracket) + (kotlin-mode--forward-string-chunk))) + (point)) + +(defun kotlin-mode--goto-non-comment-bol () + "Back to the beginning of line that is not inside a comment." + (beginning-of-line) + (let (chunk) + (while (progn + (setq chunk (kotlin-mode--chunk-after)) + (kotlin-mode--chunk-comment-p chunk)) + ;; The point is in a comment. Backs to the beginning of the comment. + (goto-char (kotlin-mode--chunk-start chunk)) + (beginning-of-line)))) + +(defun kotlin-mode--goto-non-comment-eol () + "Proceed to the end of line that is not inside a comment. + +If this line ends with a single-line comment, goto just before the comment." + (end-of-line) + (let (chunk) + (while (progn + (setq chunk (kotlin-mode--chunk-after)) + (kotlin-mode--chunk-comment-p chunk)) + ;; The point is in a comment. + (if (kotlin-mode--chunk-single-line-comment-p chunk) + ;; This is a single-line comment + ;; Back to the beginning of the comment. + (goto-char (kotlin-mode--chunk-start chunk)) + ;; This is a multiline comment + ;; Proceed to the end of the comment. + (goto-char (kotlin-mode--chunk-start chunk)) + (forward-comment 1) + (end-of-line) + ;; If the comment is incomplete, back to the beginning of the comment. + (when (and (eobp) (kotlin-mode--chunk-after)) + (goto-char (kotlin-mode--chunk-start (kotlin-mode--chunk-after)))))))) + +(defun kotlin-mode--bol-other-than-comments-p () + "Return t if there is nothing other than comments in the front of this line. + +Return nil otherwise. +Newlines inside comments are ignored." + ;; Foo // ← bol + ;; /* */ Foo // ← bol + ;; X /* */ Foo // ← not bol + ;; + ;; /* + ;; */ /* */ /* + ;; */ Foo // ← bol + ;; + ;; X /* + ;; */ /* */ /* + ;; */ Foo // ← not bol + ;; + ;; X + ;; /* */ /* + ;; */ Foo // ← bol + (save-excursion + (let ((pos (point))) + (kotlin-mode--goto-non-comment-bol) + (forward-comment (point-max)) + (<= pos (point))))) + +(defun kotlin-mode--eol-other-than-comments-p () + "Return t if there is nothing other than comments until the end of this line. + +Return nil otherwise. +Newlines inside comments are ignored." + (save-excursion + (let ((pos (point))) + (kotlin-mode--goto-non-comment-eol) + (forward-comment (- (point))) + (<= (point) pos)))) + +(defun kotlin-mode--skip-whitespaces () + "Skip forward whitespaces and newlines." + (skip-syntax-forward " >")) + +(defun kotlin-mode--forward-comments-but-not-line-breaks () + "Skip forward comments but not line breaks." + (goto-char (min + (save-excursion + (kotlin-mode--goto-non-comment-eol) + (point)) + (save-excursion + (forward-comment (point-max)) + (point))))) + +(defun kotlin-mode--backward-comments-but-not-line-breaks () + "Skip backward comments but not line breaks." + (goto-char (max + (save-excursion + (kotlin-mode--goto-non-comment-bol) + (point)) + (save-excursion + (forward-comment (- (point))) + (point))))) (provide 'kotlin-mode-lexer) diff --git a/kotlin-mode.el b/kotlin-mode.el index 5ca0ddb..eedbb35 100644 --- a/kotlin-mode.el +++ b/kotlin-mode.el @@ -34,6 +34,7 @@ (require 'eieio) (require 'kotlin-mode-lexer) +(require 'kotlin-mode-indent) (defgroup kotlin nil "A Kotlin major mode." @@ -144,16 +145,18 @@ START and END define region within current buffer." (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 [remap indent-new-comment-line] + #'kotlin-mode--indent-new-comment-line) map) "Keymap for `kotlin-mode'.") ;;; Font Lock -(defconst kotlin-mode--misc-keywords +(defconst kotlin-mode--package-keywords '("package" "import")) (defconst kotlin-mode--type-decl-keywords - '("sealed" "inner" "data" "class" "interface" "trait" "typealias" "enum" "object")) + '("class" "interface" "typealias" "object")) (defconst kotlin-mode--fun-decl-keywords '("fun")) @@ -177,8 +180,8 @@ START and END define region within current buffer." (defconst kotlin-mode--generic-type-parameter-keywords '("where")) -(defvar kotlin-mode--keywords - (append kotlin-mode--misc-keywords +(defvar kotlin-mode--misc-keywords + (append kotlin-mode--package-keywords kotlin-mode--type-decl-keywords kotlin-mode--fun-decl-keywords kotlin-mode--val-decl-keywords @@ -190,15 +193,14 @@ START and END define region within current buffer." (defconst kotlin-mode--constants-keywords '("null" "true" "false")) -(defconst kotlin-mode--modifier-keywords - '("open" "private" "protected" "public" "lateinit" - "override" "abstract" "final" "companion" - "annotation" "internal" "const" "in" "out" - "actual" "expect" "crossinline" "inline" "noinline" "external" - "infix" "operator" "reified" "suspend" "tailrec" "vararg")) +(defconst kotlin-mode--variance-modifier-keywords + '("in" "out")) + +(defconst kotlin-mode--reification-modifier-keywords + '("reified")) (defconst kotlin-mode--property-keywords - '("by" "get" "set")) ;; "by" "get" "set" + '("by" "get" "set")) (defconst kotlin-mode--initializer-keywords '("init" "constructor")) @@ -213,13 +215,13 @@ START and END define region within current buffer." (defvar kotlin-mode--font-lock-keywords `(;; Keywords (,(rx-to-string - `(and bow (group (or ,@kotlin-mode--keywords)) eow) + `(and bow (group (or ,@kotlin-mode--misc-keywords)) eow) t) 1 font-lock-keyword-face) ;; Package names (,(rx-to-string - `(and (or ,@kotlin-mode--misc-keywords) (+ space) + `(and (or ,@kotlin-mode--package-keywords) (+ space) (group (+ (any word ?.)))) t) 1 font-lock-string-face) @@ -263,11 +265,14 @@ START and END define region within current buffer." t) 1 font-lock-function-name-face) - ;; Access modifier - ;; Access modifier is valid identifier being used as variable + ;; Modifiers + ;; Modifier is valid identifier being used as variable ;; TODO: Highlight only modifiers in the front of class/fun (,(rx-to-string - `(and bow (group (or ,@kotlin-mode--modifier-keywords)) + `(and bow (group (or ,@kotlin-mode--modifier-keywords + ,@kotlin-mode--companion-modifier-keywords + ,@kotlin-mode--variance-modifier-keywords + ,@kotlin-mode--reification-modifier-keywords)) eow) t) 1 font-lock-keyword-face) @@ -323,361 +328,6 @@ and return non-nil. Return nil otherwise." t) (kotlin-mode--match-interpolation limit)))))) -;; Indentation - -(defun kotlin-mode--base-indentation () - "Return the indentation level of the current line based on brackets only. - -Ignore 'continuation' indentation." - (cond ((kotlin-mode--line-continuation) - (- (current-indentation) kotlin-tab-width)) - ((kotlin-mode--in-comment-block-p) - (- (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 backwards. - -Scan from the cursor position toward the beginning of the line. -Increment the count +1 for open-brackets, -1 for close-brackets. - -When the overall count exceeds zero, mark the COUNTER finished, set -indentation, and return immediately. 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 the 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()) -}" - ;; Ignore comments and spaces at the end of the line. - ;; Example: - ;; fun foo() { // ignore comments here - ;; bar() - ;; } - (when (nth 4 (syntax-ppss)) - ;; If the point is inside a comment, goto the beginning of the - ;; comment. - (goto-char (nth 8 (syntax-ppss)))) - (forward-comment (- (point))) - - (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) - "Increment count of COUNTER by VAL." - (cl-incf (oref counter count) val)) - -(defun kotlin-mode--subtract-count (counter val) - "Decrement count of COUNTER by VAL." - (cl-decf (oref counter count) val)) - -(defun kotlin-mode--add-indent (counter val) - "Increment indentation of COUNTER by VAL." - (cl-incf (oref counter indent) val)) - -(defun kotlin-mode--finished-p (counter) - "Return t if COUNTER is finished." - (oref counter finished)) - -(defun kotlin-mode--in-comment-block-p () - "Return whether the cursor is within a standard comment block structure. - -Return non-nil if the comment has the following format: -/** - * Description here - */" - (save-excursion - (let ((in-comment-block nil) - (keep-going (and - (not (kotlin-mode--line-begins-p "\\*\\*+/")) - (not (kotlin-mode--line-begins-p "/\\*")) - (nth 4 (syntax-ppss))))) - (while keep-going - (kotlin-mode--prev-line) - (cond - ((kotlin-mode--line-begins-p "/\\*") - (setq keep-going nil) - (setq in-comment-block t)) - ((bobp) - (setq keep-going nil)) - ((kotlin-mode--line-contains-p "\\*/") - (setq keep-going nil)))) - in-comment-block))) - -(defun kotlin-mode--first-line-p () - "Determine if point is on the first line." - (save-excursion - (beginning-of-line) - (bobp) - ) - ) - -(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. - -Caller must pass in PARSER-STATE-INDEX, which refers to the index -of the list returned by `syntax-ppss'. - -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--indent-line () - "Indent the current line of Kotlin code." - (interactive) - (let ((follow-indentation-p - (and (<= (line-beginning-position) (point)) - (>= (+ (line-beginning-position) - (current-indentation)) - (point))))) - (save-excursion - (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 cur-indent - (indent-line-to cur-indent) - (indent-line-to 0))))) - - (when follow-indentation-p - (back-to-indentation)))) - - -(defun kotlin-mode--beginning-of-buffer-indent () - "Indent current line assuming the point is at the beginning of the buffer." - (indent-line-to 0)) ;; the Kotlin mode @@ -686,17 +336,48 @@ If it does not exist, will return nil." "Major mode for editing Kotlin." (setq font-lock-defaults '((kotlin-mode--font-lock-keywords) nil nil)) + (setq-local parse-sexp-lookup-properties t) (add-hook 'syntax-propertize-extend-region-functions #'kotlin-mode--syntax-propertize-extend-region nil t) (setq-local syntax-propertize-function #'kotlin-mode--syntax-propertize) - (set (make-local-variable 'comment-start) "//") - (set (make-local-variable 'comment-padding) 1) - (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *") - (set (make-local-variable 'comment-end) "") - (set (make-local-variable 'indent-line-function) 'kotlin-mode--indent-line) - (setq-local adaptive-fill-regexp comment-start-skip) + + (setq-local comment-start "//") + (setq-local comment-end "") + (setq-local comment-padding 1) + (setq-local comment-start-skip + (rx (seq (zero-or-more (syntax whitespace)) + (or + ;; Single-line comment + (seq "/" (one-or-more "/")) + ;; Multi-line comment + (seq "/" (one-or-more "*")) + ;; Middle of multi-line-comment + (seq (one-or-more "*") " ")) + (zero-or-more (syntax whitespace))))) + (setq-local adaptive-fill-regexp + (rx (seq (zero-or-more (syntax whitespace)) + (or + ;; Single-line comment + (seq "/" (one-or-more "/")) + ;; Middle of multi-line-comment + (seq (one-or-more "*") " ")) + (zero-or-more (syntax whitespace))))) + (setq-local fill-indent-according-to-mode t) + (setq-local comment-multi-line t) + + (setq-local indent-line-function 'kotlin-mode--indent-line) + + (setq-local electric-indent-chars + (append "{}()[]:;,." electric-indent-chars)) + + (add-hook 'post-self-insert-hook #'kotlin-mode--post-self-insert nil t) + + (setq-local kotlin-mode--anchor-overlay + (make-overlay (point-min) (point-min) nil t)) + + (delete-overlay kotlin-mode--anchor-overlay) :group 'kotlin :syntax-table kotlin-mode-syntax-table) diff --git a/test/kotlin-mode-test.el b/test/kotlin-mode-test.el index 897a8f8..b6f1ebf 100644 --- a/test/kotlin-mode-test.el +++ b/test/kotlin-mode-test.el @@ -76,7 +76,7 @@ println(arg) (should (equal (buffer-string) "fun test(args: Array) { args.forEach(arg -> - println(arg) + println(arg) ) ")) ))) @@ -180,6 +180,9 @@ println(arg) (while (and (looking-at "^[ \t]*$") (not (eobp))) (forward-line))) +(defvar kotlin-mode--test-keep-going nil + "If non-nil, do not stop at error in `kotlin-mode--sample-test'.") + (ert-deftest kotlin-mode--sample-test () (dolist (filename '("test/sample.kt" "test/pathological.kt")) (with-temp-buffer @@ -191,46 +194,80 @@ println(arg) (setq-local kotlin-tab-width 4) (while (not (eobp)) - (back-to-indentation) - (let (;; (thing-at-point 'line) returns string with property. - (expected-line (buffer-substring-no-properties - (line-beginning-position) - (line-end-position))) - actual-line - (original-indent (current-column)) - (known-bug (looking-at ".*//.*KNOWN_BUG"))) - ;; Remove existing indentation, or indent to column 1 if - ;; expected indentation is column 0. - (if (= original-indent 0) - (indent-line-to 1) - (delete-horizontal-space)) - - ;; Indent the line - (kotlin-mode--indent-line) - - (setq actual-line (buffer-substring-no-properties - (line-beginning-position) - (line-end-position))) - - ;; Check that the correct indentation is re-applied - (if known-bug - (if (equal expected-line actual-line) - (message "%s:%s:info: KNOWN_BUG is fixed somehow" - filename - (line-number-at-pos)) - (back-to-indentation) - (message "%s:%s:warn: (known bug) expected indentation to column %d but %d" - filename - (line-number-at-pos) - original-indent - (current-column))) - (should - (equal - (format "%s:%s: %s" filename (line-number-at-pos) expected-line) - (format "%s:%s: %s" filename (line-number-at-pos) actual-line)))) - - ;; Restore to original indentation for KNOWN_BUG line. - (indent-line-to original-indent) - - ;; Go to the next non-empty line - (next-non-empty-line)))))) + (kotlin-mode--test-current-line filename nil) + + ;; Indent without following lines + (narrow-to-region (point-min) (line-end-position)) + (kotlin-mode--test-current-line filename t) + (widen) + + ;; Go to the next non-empty line + (next-non-empty-line))))) + +(defun kotlin-mode--test-current-line (filename truncated) + (back-to-indentation) + (let (;; (thing-at-point 'line) returns string with property, so + ;; we use `buffer-substring-no-properties'. + (expected-line (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + actual-line + (original-indent (current-column)) + ;; Known bug when the following lines are not present. + (known-bug-when-truncated + (looking-at ".*//.*KNOWN_BUG_WHEN_TRUNCATED")) + (known-bug (looking-at ".*//.*KNOWN_BUG\\($\\| \\)"))) + ;; Remove existing indentation, or indent to column 1 if expected + ;; indentation is column 0. + ;; Keep indent inside multiline string. + (unless (kotlin-mode--chunk-multiline-string-p + (kotlin-mode--chunk-after)) + (if (= original-indent 0) + (indent-line-to 1) + (delete-horizontal-space))) + + ;; Indent the line + (kotlin-mode--indent-line) + + (setq actual-line (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + + ;; Check that the correct indentation is re-applied + (unless (and truncated known-bug) + (if (or known-bug (and truncated known-bug-when-truncated)) + (if (equal expected-line actual-line) + (message "%s:%s:info: %s is fixed somehow" + filename + (line-number-at-pos) + (if truncated "KNOWN_BUG_WHEN_TRUNCATED" "KNOWN_BUG")) + (back-to-indentation) + (message "%s:%s:warn: (known bug) %sexpected indentation to column %d but %d" + filename + (line-number-at-pos) + (if truncated "(when code is truncated) " "") + original-indent + (current-column))) + (if (and kotlin-mode--test-keep-going + (not (equal expected-line actual-line))) + (message "%s:%s:err: %sexpected indentation to column %d but %d" + filename + (line-number-at-pos) + (if truncated "(when code is truncated) " "") + original-indent + (current-column)) + (should + (equal + (format "%s:%s: %s%s" + filename + (line-number-at-pos) + (if truncated "(when code is truncated) " "") + expected-line) + (format "%s:%s: %s%s" + filename + (line-number-at-pos) + (if truncated "(when code is truncated) " "") + actual-line)))))) + + ;; Restore to original indentation for KNOWN_BUG line. + (indent-line-to original-indent))) diff --git a/test/pathological.kt b/test/pathological.kt index ec3421f..09e7c41 100644 --- a/test/pathological.kt +++ b/test/pathological.kt @@ -17,10 +17,16 @@ : Foo . - Bar + Bar< + A // KNOWN_BUG + > // KNOWN_BUG . Baz +@file + : + Foo(aaa) + // packageHeader package foo // Line breaks are prohibited after "package". .bar // Line breaks are prohibited after dot. @@ -44,10 +50,10 @@ public typealias A < - B, // KNOWN_BUG - C // KNOWN_BUG - > // KNOWN_BUG - = // KNOWN_BUG + B, // KNOWN_BUG_WHEN_TRUNCATED + C // KNOWN_BUG_WHEN_TRUNCATED + > + = D // classDeclaration @@ -58,12 +64,12 @@ public class A < - A, // KNOWN_BUG - B // KNOWN_BUG - > // KNOWN_BUG + A, // KNOWN_BUG_WHEN_TRUNCATED + B // KNOWN_BUG_WHEN_TRUNCATED + > // primaryConstructor public - constructor // KNOWN_BUG + constructor ( // classParameters val @@ -82,47 +88,48 @@ public : // delegationSepcifiers Base - by - x + - x, - B, // KNOWN_BUG + by + x + + x, + B, C, D . (Int) -> Int - where // KNOWN_BUG - @A // KNOWN_BUG - A // KNOWN_BUG + where + @A + A : A, B : B { - public // KNOWN_BUG + public interface A { - fun a(): Int // KNOWN_BUG - } // KNOWN_BUG + fun a(): Int + fun b(): Int + } // typeParameters data class Bar< - @A // KNOWN_BUG - out // KNOWN_BUG - A + @A // KNOWN_BUG_WHEN_TRUNCATED + out + A // KNOWN_BUG_WHEN_TRUNCATED : A, - @A - in + @A // KNOWN_BUG_WHEN_TRUNCATED + in // KNOWN_BUG_WHEN_TRUNCATED A : A - > // KNOWN_BUG - { // brace on its own line // KNOWN_BUG + > + { // brace on its own line fun a() { } } @@ -131,15 +138,117 @@ public enum class A(val x: Int) { - // enumClassBody // KNOWN_BUG - A(1), B(1), // KNOWN_BUG + // enumClassBody + A(1), B(1), C(1) { override fun a() = 2 - }, D(1), + }, D(1) { + override fun a() = 3 + }, E(1); fun a(): Int = x - } // KNOWN_BUG + } + + // typeConstraints + public + class + Foo< + A // KNOWN_BUG_WHEN_TRUNCATED + > + : + X, + Y + where + A + : + B, + A + : + C { + } + + public class Foo: X, Y + where A + : + B, + A + : + C { + } + + public class Foo: X, Y where + A + : + B, + A + : + C { + } + + public class Foo: X, Y where A + : + B, + A + : + C { + } + + fun < + A + > Foo.foo() + : A + where + A + : + B, + A + : + C { + } + + fun < + A + > Foo.foo() + : A + where + A + : + B, + A + : + C + = + A() + + val + < + A // KNOWN_BUG_WHEN_TRUNCATED + > + A + . + x + where + A + : + B, + A + : + C + = + 1 + + val f = fun A.(): A + where + A: + B, + A: + C { + } + + class Foo where T: A + , T: B + , T: C // anonymousInitializer init { @@ -153,29 +262,29 @@ public A : B { - fun foo() { // KNOWN_BUG + fun foo() { } - } // KNOWN_BUG + } // functionDeclaration public fun < - A // KNOWN_BUG + A // KNOWN_BUG_WHEN_TRUNCATED > A - . // KNOWN_BUG + . foo // functionValueParameters // KNOWN_BUG - ( // KNOWN_BUG + ( a: Int = 1 ) - : // KNOWN_BUG + : A where - A : A { - a() // KNOWN_BUG - } // KNOWN_BUG + A : A { + a() + } fun foo(x) @@ -186,22 +295,22 @@ public public val < - A // KNOWN_BUG - > // KNOWN_BUG + A // KNOWN_BUG_WHEN_TRUNCATED + > A - . // KNOWN_BUG + . @A - a // KNOWN_BUG - : // KNOWN_BUG - A - where - A + a : A + where + A + : + A = a - get() = 1 - set(value) { + public get() = 1 + public set(value) { foo(value) } @@ -221,18 +330,19 @@ public // getter/setter with default implementation var x = 1 - @A get // KNOWN_BUG + @A get @A set - // objectDeclaration // KNOWN_BUG - public // KNOWN_BUG + // objectDeclaration + public object Foo : Bar { - fun foo() { // KNOWN_BUG + fun foo() { + bar() } - } // KNOWN_BUG + } // secondaryConstructor public @@ -241,24 +351,24 @@ public ) : this - ( // KNOWN_BUG + ( 1 ) { - a() // KNOWN_BUG - } // KNOWN_BUG + a() + } - public // KNOWN_BUG + public constructor ( ) : super - ( // KNOWN_BUG + ( 1 ) - // dynamic type // KNOWN_BUG - var x: dynamic = 1 // KNOWN_BUG + // dynamic type + var x: dynamic = 1 // nullableType var @@ -266,21 +376,21 @@ public : A < - X, // KNOWN_BUG - *, // KNOWN_BUG + X, // KNOWN_BUG_WHEN_TRUNCATED + *, // KNOWN_BUG_WHEN_TRUNCATED out Y, in Z - > // KNOWN_BUG - . // KNOWN_BUG + > + . B . C < - X // KNOWN_BUG + X // KNOWN_BUG_WHEN_TRUNCATED > - ? // KNOWN_BUG + ? ? ? = @@ -315,29 +425,35 @@ public ) -> C -} // KNOWN_BUG +} -// statements // KNOWN_BUG -fun foo() { // KNOWN_BUG +// statements +fun foo() { //explicit semicolons - a(); - b(); + /* aaa */ a(); b(); c(); // annotation @A + @B + .D(aaa) @C + @[ + A + B + C + ] // label aaa@ - a() // KNOWN_BUG + a() - // forStatement // KNOWN_BUG - for ( // KNOWN_BUG + // forStatement + for ( @A a : A in - aaa + aaa ) { a() } @@ -347,7 +463,7 @@ fun foo() { // KNOWN_BUG @A (x, y) in - aaa + aaa ) { a() @@ -356,10 +472,10 @@ fun foo() { // KNOWN_BUG for ( a in aaa ) - a() // KNOWN_BUG + a() - // whileStatement // KNOWN_BUG - while ( // KNOWN_BUG + // whileStatement + while ( a() ) { a() @@ -376,36 +492,47 @@ fun foo() { // KNOWN_BUG while ( a() ) - a() // KNOWN_BUG + a() - while ( // KNOWN_BUG + while ( a() ) - ; // KNOWN_BUG + ; - // doWhileStatement // KNOWN_BUG - do { // KNOWN_BUG + // doWhileStatement + do { a() } while ( a() ) do - { // KNOWN_BUG + { a() } - while // KNOWN_BUG + while ( a() ) do - a() + a() + + b() while (a()) do while (a()) + while (a()) + do + do + while(a()) + while(a()) + + while(a()) + do + while(a()) + // assignment (x, y) = // Line breaks are prohibited before assignment operators. a() @@ -473,9 +600,9 @@ fun foo() { // KNOWN_BUG val x = a > - b // KNOWN_BUG + b - val x = // KNOWN_BUG + val x = a <= b @@ -484,19 +611,19 @@ fun foo() { // KNOWN_BUG b val x = - a in // Line breaks are prohibited before in/is operators + foo(a) in // Line breaks are prohibited before in/is operators b val x = - a - !in // KNOWN_BUG - b // KNOWN_BUG + a !in + b when (a()) { 1 -> a() // Line breaks are prohibited before in/is operators, So the following // line should not be indented. in aaa -> a() + !in aaa -> a() } val x = @@ -512,6 +639,7 @@ fun foo() { // KNOWN_BUG // Line breaks are prohibited before in/is operators, So the following // line should not be indented. is X -> a() + !is X -> a() } val x = @@ -524,8 +652,16 @@ fun foo() { // KNOWN_BUG a shl // Line breaks are allowed after infix function. b // KNOWN_BUG + var shl = 1 // KNOWN_BUG + val x = shl shl shl + shl < 100 && foo() // this is not a continuation of the previous line. + + var shl = 1 + val x = shl shl + shl < 100 && foo() // this is a continuation of the previous line. // KNOWN_BUG + // This is not a infix function call; line breaks are // KNOWN_BUG - // prohibited before infix function. // KNOWN_BUG + // prohibited before infix function. val x = // KNOWN_BUG a f (b) // So this line should not indented. @@ -559,17 +695,17 @@ fun foo() { // KNOWN_BUG // prefixUnaryExpression val x = @a - a@ // label // KNOWN_BUG - + // KNOWN_BUG + a@ // label + + - ++ - a + a // KNOWN_BUG - val x = + val x = // KNOWN_BUG -- - a + a // KNOWN_BUG - val x = + val x = // KNOWN_BUG ! a // KNOWN_BUG @@ -588,45 +724,83 @@ fun foo() { // KNOWN_BUG a -- a // This line too. - val x = + var shl = 1 + val x = shl shl shl ++ + shl < 100 && foo() // this is not a continuation of the previous line. + + var shl = 1 + val x = shl shl ++ + shl < 100 && foo() // this is a continuation of the previous line. // KNOWN_BUG + + val x = // KNOWN_BUG a!! - val x = + val x = foo()!! + foo() // this is not a continuation of the previous line. + + val x = !! + foo() // this is a continuation of the previous line. // KNOWN_BUG + + val x = // KNOWN_BUG f< // Line breaks are prohibited before type arguments. - A // KNOWN_BUG + A // KNOWN_BUG_WHEN_TRUNCATED >( // Line breaks are prohibited before function arguments. x )[ // Line breaks are prohibited before subscript. x ] - . // KNOWN_BUG + . a . b // lambda arguments + val x = f() + { + a() + } + val x = f() @A a@ - { // KNOWN_BUG + { a() } - val x = f() @A a@ { // KNOWN_BUG + val x = f() @A a@ { + a() + } + + val x = f + { a() } val x = f @A a@ - { // KNOWN_BUG + { a() } - val x = f @A a@ { // KNOWN_BUG + val x = f @A a@ { a() } + val x = x + .foo { + a + } + .bar { + a + } + + val x = x.foo { + a + }.bar { + a + } + val x = f // Line breaks are prohibited before function arguments. (1 + 1).also { print(it) } // So this line should not be indented. @@ -643,9 +817,9 @@ fun foo() { // KNOWN_BUG // collectionLiteral @A(x = [ 1, 2, 3, - 4, 5, 6, + /* aaa */ 4, 5, 6, 7, 8, 9 - ]) + ]) a() // CharacterLiteral @@ -660,27 +834,27 @@ fun foo() { // KNOWN_BUG // stringLiteral val x = "abc\"def${ - foo("abc") // KNOWN_BUG - }ghi${ // KNOWN_BUG - "aaa\${ // dollar sign cannot be escaped // KNOWN_BUG - 1 // KNOWN_BUG - }bbb" // KNOWN_BUG - }jkl" // KNOWN_BUG + foo("abc") + }ghi${ + "aaa\${ // dollar sign cannot be escaped + 1 + }bbb" + }jkl" val x = """a - "b" // KNOWN_BUG + "b" c${ - foo("""a // KNOWN_BUG - b // KNOWN_BUG - c // KNOWN_BUG - """) // KNOWN_BUG - }d // KNOWN_BUG - e // KNOWN_BUG + foo("""a + b + c + """) + }d + e f${ - """aaa\${ // KNOWN_BUG - 1 // KNOWN_BUG - }bbb""" // KNOWN_BUG - }ghi""" // KNOWN_BUG + """aaa\${ + 1 + }bbb""" + }ghi""" val x = a("'") @@ -693,15 +867,15 @@ fun foo() { // KNOWN_BUG } val f: () -> Unit = { -> - a() // KNOWN_BUG - a() // KNOWN_BUG + a() + a() a() } val f: () -> Unit = { - -> // KNOWN_BUG + -> + a() a() - a() // KNOWN_BUG a() } @@ -716,9 +890,9 @@ fun foo() { // KNOWN_BUG ) : AAA - -> - a() // KNOWN_BUG - a() // KNOWN_BUG + -> + a() + a() a() } @@ -726,12 +900,24 @@ fun foo() { // KNOWN_BUG x, y, z - -> // KNOWN_BUG + -> + a() a() - a() // KNOWN_BUG a() } + val f: () -> Unit = { + (x, + y, + z) + -> + a() + a() + a() + } + + // functionLiteral + val f = fun A @@ -742,42 +928,46 @@ fun foo() { // KNOWN_BUG : AAA where - A - : - A, - B // KNOWN_BUG - : // KNOWN_BUG - B { - a() // KNOWN_BUG - } // KNOWN_BUG + A + : + A, + B + : + B { + a() // KNOWN_BUG + } // KNOWN_BUG - // functionLiteral val f = fun - { // KNOWN_BUG + { a() } - // objectLiteral // KNOWN_BUG - val x = // KNOWN_BUG + // objectLiteral + val object: A by a, B by b { + fun foo() { + } + } + + val x = object : A, - B // KNOWN_BUG + B by b, C { - fun foo() { // KNOWN_BUG + fun foo() { + } } - } // KNOWN_BUG - val x = object // KNOWN_BUG - { // KNOWN_BUG + val x = object + { fun foo() { } } - // thisExpression // KNOWN_BUG - val x = // KNOWN_BUG + // thisExpression + val x = this val x = @@ -792,214 +982,364 @@ fun foo() { // KNOWN_BUG val x = super< - Int // KNOWN_BUG + Int // KNOWN_BUG_WHEN_TRUNCATED >@Foo - // ifExpression // KNOWN_BUG - val x = if ( // KNOWN_BUG - a - ) { - a() - } - - val x = if - ( - a - ) - { - a() - } - - val x = if ( - a - ) - a() // KNOWN_BUG - - val x = if ( // KNOWN_BUG - a - ) - ; // KNOWN_BUG - - val x = if ( // KNOWN_BUG - a - ) { - a() - } else { - a() - } - - val x = if - ( - a - - ) - { - a() - } - else - { // KNOWN_BUG - a() - } - - val x = if ( // KNOWN_BUG - a - ) - a() // KNOWN_BUG - else // KNOWN_BUG - a() - - val x = if ( - a - ) - ; // KNOWN_BUG - else // KNOWN_BUG - ; - - val x = if ( - a - ) - else - ; - - // whenExpression - - val x = when ( - @A - val - a - = - a() - ) { - a(), b(), - c(), d() - -> - { // KNOWN_BUG - a() - } - - in // KNOWN_BUG - a() - -> - { // KNOWN_BUG - } - - is // KNOWN_BUG - A - -> - { // KNOWN_BUG - } - - a() // KNOWN_BUG - -> - a() - - else - -> - { // KNOWN_BUG - } - } - - val x = when - ( - a() - ) - { - a -> 1 - } - - // tryExpression - val x = try { - a() - } catch(@A e: E) { - a() - } catch(@A e: E) { - a() - } finally { - a() - } - - val x = try - { // KNOWN_BUG - a() - } - catch // KNOWN_BUG - ( - @A e: E - ) - { - a() - } - finally - { // KNOWN_BUG - a() - } - - val x = try { // KNOWN_BUG - a() - } finally { - a() - } - - val x = try - { // KNOWN_BUG - a() - } - finally // KNOWN_BUG - { // KNOWN_BUG - a() - } - - // jumpExpression // KNOWN_BUG - val x = // KNOWN_BUG - throw - a() - - val x = - return a() // Line breaks are prohibited after return. - - val x = - return // Line breaks are prohibited after return. - a() // So this line should not be indented. - - val x = - return@A a() // Line breaks are prohibited after return. - - val x = - return@A // Line breaks are prohibited after return. - a() // So this line should not be indented. - - val x = - continue - - val x = - continue@A - - val x = - break - - val x = - break@A - - // callableReference - val x = - Foo - :: - foo - - val x = - Foo - :: - class - - // typeModifier // KNOWN_BUG - val f: - suspend - () -> Unit - = - suspend - { // KNOWN_BUG - a() - } + // ifExpression + val x = if ( + a + + 1 + ) { + a() + } + + val x = if + ( + a + + 1 + ) + { + a() + } + + val x = if ( + a + ) + a() + + val x = if ( + a + ) + ; + + val x = if ( + a + ) { + a() + } else { + a() + } + + val x = if + ( + a + ) + { + a() + } + else + { + a() + } + + val x = if ( + a + ) + a() + else + a() + + val x = if ( + a + ) + ; + else + ; + + val x = if ( + a + ) + else + ; + + val x = + if ( + a + ) { + a() + } else { + a() + } + + val x = if (foo) 1 + else 2 // should be indented? + + val x = + if (foo) 1 + else 2 + + val x = if (foo) 1 + + 1 + else 2 + + 1 + + val x = if (foo) + if (bar) + aaa() + + 1 + else + if (baz) + if (aaa) aaa else bbb + + 1 + else + if (aaa) aaa else + bbb + + 1 + else + if (bar) + if (aaa) + if (bbb) + ccc + + 1 + + 2 + else + ccc + + 1 + + 2 + else + ccc + + 1 + + 2 + else + aaa() + + 1 + + val x = if (foo) + while (false) { + aaa() + + 1 + } + else + while (false) { + aaa() + + 1 + } + + val x = if (a) + foo() + + 1 + else if (e) + foo() + + 1 + else + foo() + + 1 + + val x = if (a) { + foo() + + 1 + } else if (e) { + foo() + + 1 + } else { + foo() + + 1 + } + + val x = if (a) { + foo() + + 1 + } else + if (e) { + foo() + + 1 + } else { + foo() + + 1 + } + + val x = if (a) + { + foo() + + 1 + } + else if (e) + { + foo() + + 1 + } + else + { + foo() + + 1 + } + + val x = if (a) if (a) 1 + + 1 else 1 else + if (a) if (a) 1 + + 1 else 1 else + 1 + + // whenExpression + + val x = when ( + @A + val + a + = + a() + ) { + a(), b(), + c(), d() + -> + { + a() + } + + a() -> { + a() + } + + in + a() + -> + { + a () + } + + is + A + -> + { + } + + a() + -> + if (x) { + 1 + } else { + 2 + } + + if (x) + 1 + else + 2 + -> + if (x) + 1 + else + 2 + + else + -> + { + a() + } + } + + val x = when + ( + a() + ) + { + a -> 1 + } + + // tryExpression + val x = try { + a() + } catch(@A e: E) { + a() + } catch(@A e: E) { + a() + } finally { + a() + } + + val x = + try { + a() + } catch(@A e: E) { + a() + } catch(@A e: E) { + a() + } finally { + a() + } + + val x = try + { + a() + } + catch + ( + @A e: E + ) + { + a() + } + finally + { + a() + } + + val x = try { + a() + } finally { + a() + } + + val x = try + { + a() + } + finally + { + a() + } + + // jumpExpression + val x = + throw + a() + + val x = + return a() // Line breaks are prohibited after return. + + val x = + return // Line breaks are prohibited after return. + a() // So this line should not be indented. + + val x = + return@A a() // Line breaks are prohibited after return. + + val x = + return@A // Line breaks are prohibited after return. + a() // So this line should not be indented. + + val x = + continue + + val x = + continue@A + + val x = + break + + val x = + break@A + + // callableReference + val x = + Foo + :: + foo + + val x = + Foo + :: + class + + // typeModifier + val f: + suspend + () -> Unit + = + suspend + { + a() + } } class Foo: Base { @@ -1007,7 +1347,7 @@ class Foo: Base { public override fun f() { - } // KNOWN_BUG + } public lateinit @@ -1018,22 +1358,22 @@ class Foo: Base { override public fun f() { - } // KNOWN_BUG + } override private fun f() { - } // KNOWN_BUG + } override internal fun f() { - } // KNOWN_BUG + } override protected fun f() { - } // KNOWN_BUG + } // functionModifier public @@ -1041,27 +1381,27 @@ class Foo: Base { infix inline fun A.f(): A { - return a() // KNOWN_BUG - } // KNOWN_BUG + return a() + } public operator fun A.unaryPlus(): A { - return a() // KNOWN_BUG - } // KNOWN_BUG + return a() + } public suspend fun foo() { - a() // KNOWN_BUG - } // KNOWN_BUG + a() + } } public external fun foo() { - a() // KNOWN_BUG -} // KNOWN_BUG + a() +} class Foo { // propertyModifier @@ -1075,23 +1415,25 @@ class Foo { public abstract class Foo { - fun foo() { - } // KNOWN_BUG -} // KNOWN_BUG + fun foo() { + bar() + } +} public final class Foo { - fun foo() { // KNOWN_BUG + fun foo() { + bar() } -} // KNOWN_BUG +} public open class Foo { - fun foo() { // KNOWN_BUG + fun foo() { } -} // KNOWN_BUG +} class Foo { // parameterModifier @@ -1119,9 +1461,9 @@ class Foo { // reificationModifier inline fun < @A - reified // KNOWN_BUG - T - > foo() { // KNOWN_BUG + reified + T // KNOWN_BUG_WHEN_TRUNCATED + > foo() { a() } } @@ -1130,19 +1472,142 @@ class Foo { public expect class Foo { - fun foo() // KNOWN_BUG -} // KNOWN_BUG + fun foo() +} public actual class Foo { - fun foo() // KNOWN_BUG -} // KNOWN_BUG + fun foo() +} + + +// Ambiguous commas, colons, curly brackets, and objects. + +class C: A by object: B1, + B2 { + fun foo() {} + }, + B by object: B3, + B4 { + fun foo() {} + } { + fun foo(x: T): Int { + return when (x) { + object: B1 by object: B1 { + fun foo() {} + }, + B2 { + fun foo() {} + }, + object: B3, + B4 { + fun foo() {} + } -> + 1 + + else -> + 2 + } + } +} + +// Curly braces may appar at: +// classBody +// class (optional) +// interface (optional) +// companionObject (optional) +// objectDeclaration (optional) +// enumEntry (optional) +// objectLiteral +// block +// init +// functionBody +// fun (optional) +// getter +// setter +// anonymousFunction +// constructor (optional) +// controlStructureBody (optional) +// for +// while +// do +// if +// else +// whenEntry +// try +// catch +// finally +// lambda +// when + +class C: + A by foo bar { // infix function call + aaa() // this is not a class body // KNOWN_BUG + } { // KNOWN_BUG + // this is the class body + + fun foo() { + aaa() + } +} + +// Ambiguous arrows + +val f = { g: + (Int) -> + (Int) -> + Int -> + g(1, 2) +} + +when (x) { + 1 -> + f as + (Int) -> + Int // KNOWN_BUG + f as (Int) -> + Int -> + f as + (Int) -> + Int // KNOWN_BUG + f(1) -> + f as + (Int) -> + Int // KNOWN_BUG + (f) -> + f as + (Int) -> + Int // KNOWN_BUG + is (Int) -> + Int -> + f as + (Int) -> + Int // KNOWN_BUG + is Foo<(Int) -> + Int, // KNOWN_BUG + (Int) -> // KNOWN_BUG + Int> -> // KNOWN_BUG + f as + (Int) -> + Int // KNOWN_BUG + 1 < (2), (f) -> + f as + (Int) -> + Int // KNOWN_BUG + else -> + f as + (Int) -> + Int // KNOWN_BUG +} // various identifiers +val `abc` = + 1 + val `abc def 'ghi "aaa ${ aaa \` = // backquotes cannot be escaped - a() // KNOWN_BUG + a() // UNICODE_CLASS_ND val _0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९০১২৩৪৫৬৭৮৯੦੧੨੩੪੫੬੭੮੯૦૧૨૩૪૫૬૭૮૯୦୧୨୩୪୫୬୭୮୯௦௧௨௩௪௫௬௭௮௯౦౧౨౩౪౫౬౭౮౯೦೧೨೩೪೫೬೭೮೯൦൧൨൩൪൫൬൭൮൯෦෧෨෩෪෫෬෭෮෯๐๑๒๓๔๕๖๗๘๙໐໑໒໓໔໕໖໗໘໙༠༡༢༣༤༥༦༧༨༩၀၁၂၃၄၅၆၇၈၉႐႑႒႓႔႕႖႗႘႙០១២៣៤៥៦៧៨៩᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹0123456789𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩 = diff --git a/test/sample.kt b/test/sample.kt index ddd5f28..cb8c56a 100644 --- a/test/sample.kt +++ b/test/sample.kt @@ -13,7 +13,7 @@ import bar.Bar as bBar /**************************************************************** Multiline comment without leading "*" -****************************************************************/ +****************************************************************/ // KNOWN_BUG fun sum(a: Int, b: Int): Int { return a + b @@ -56,8 +56,8 @@ fun main(args: Array) { fun max(a: Int, b: Int): Int { if (a > b) - return a // KNOWN_BUG - else // KNOWN_BUG + return a + else return b } @@ -83,16 +83,16 @@ fun getStringLength(obj: Any): Int? { fun main(args: Array) { for (arg in args) - print(arg) // KNOWN_BUG + print(arg) } for (i in args.indices) - print(args[i]) // KNOWN_BUG + print(args[i]) -fun main(args: Array) { // KNOWN_BUG +fun main(args: Array) { var i = 0 while (i < args.size) - print(args[i++]) // KNOWN_BUG + print(args[i++]) } fun cases(obj: Any) { @@ -106,21 +106,21 @@ fun cases(obj: Any) { } if (x in 1..y-1) - print("OK") // KNOWN_BUG + print("OK") -if (x !in 0..array.lastIndex) // KNOWN_BUG - print("Out") // KNOWN_BUG +if (x !in 0..array.lastIndex) + print("Out") -for (x in 1..5) // KNOWN_BUG - print(x) // KNOWN_BUG +for (x in 1..5) + print(x) -for (name in names) // KNOWN_BUG - println(name) // KNOWN_BUG +for (name in names) + println(name) -if (text in names) // names.contains(text) is called // KNOWN_BUG - print("Yes") // KNOWN_BUG +if (text in names) // names.contains(text) is called + print("Yes") -names.filter { it.startsWith("A") } // KNOWN_BUG +names.filter { it.startsWith("A") } .sortedBy { it } .map { it.toUpperCase() } .forEach { print(it) } @@ -227,7 +227,7 @@ inline fun Gson.fromJson(json): T = this.fromJson(json, T::clas loop@ for (i in 1..100) { for (j in 1..100) { if (x) - break@loop // KNOWN_BUG + break@loop } } @@ -364,15 +364,15 @@ var stringRepresentation: String } var setterVisibility: String = "abc" - private set // the setter is private and has the default implementation // KNOWN_BUG + private set // the setter is private and has the default implementation -var setterWithAnnotation: Any? = null // KNOWN_BUG - @Inject set // annotate the setter with Inject // KNOWN_BUG +var setterWithAnnotation: Any? = null + @Inject set // annotate the setter with Inject -var counter = 0 // the initializer value is written directly to the backing field // KNOWN_BUG +var counter = 0 // the initializer value is written directly to the backing field set(value) { if (value >= 0) - field = value // KNOWN_BUG + field = value } val isEmpty: Boolean @@ -446,9 +446,9 @@ 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 // KNOWN_BUG + private set // setter is visible only in example.kt -internal val baz = 6 // visible inside the same module // KNOWN_BUG +internal val baz = 6 // visible inside the same module open class Outer { private val a = 1 @@ -556,11 +556,11 @@ fun > sort(list: List) { fun cloneWhenGreater(list: List, threshold: T): List where T : Comparable, - T : Cloneable { // KNOWN_BUG - return list.filter { it > threshold }.map { it.clone() } // KNOWN_BUG -} // KNOWN_BUG + T : Cloneable { + return list.filter { it > threshold }.map { it.clone() } +} -enum class Direction { // KNOWN_BUG +enum class Direction { NORTH, SOUTH, WEST, EAST } @@ -591,8 +591,7 @@ window.addMouseListener( override fun mouseEntered(e: MouseEvent) { // ... } - } -) + }) val adHoc = object { var x: Int = 0 @@ -608,12 +607,12 @@ fun countClicks(window: JComponent) { object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { clickCount++ - } // KNOWN_BUG + } override fun mouseEntered(e: MouseEvent) { enterCount++ - } // KNOWN_BUG - }) + } + }) // ... } @@ -651,8 +650,8 @@ infix fun Int.shl(x: Int): Int { fun asList(vararg ts: T): List { val result = ArrayList() for (t in ts) // ts is an Array - result.add(t) // KNOWN_BUG - return result // KNOWN_BUG + result.add(t) + return result } tailrec fun findFixPoint(x: Double = 1.0): Double @@ -673,8 +672,8 @@ val result = lock(lock, ::toBeSynchronized) fun List.map(transform: (T) -> R): List { val result = arrayListOf() for (item in this) - result.add(transform(item)) // KNOWN_BUG - return result // KNOWN_BUG + result.add(transform(item)) + return result } val doubled = ints.map { it -> it * 2 } @@ -682,14 +681,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 -}) + }) fun max(collection: Collection, less: (T, T) -> Boolean): T? { var max: T? = null for (it in collection) - if (max == null || less(max, it)) // KNOWN_BUG - max = it // KNOWN_BUG - return max // KNOWN_BUG + if (max == null || less(max, it)) + max = it + return max } val sum: (Int, Int) -> Int = { x, y -> x + y } @@ -732,8 +731,10 @@ inline fun TreeNode.findParentOfType(): T? { class Test { fun tryAdd(a: Int?, b: Int?) : Int? { var result: Int? = null - a?.let { lhs -> - b?.let { rhs -> + a?.let { + lhs -> + b?.let { + rhs -> result = lhs + rhs } } From 23a693d264355f3a05ffc20db908b4d08334bf1c Mon Sep 17 00:00:00 2001 From: taku0 Date: Sat, 21 Dec 2019 19:10:15 +0900 Subject: [PATCH 5/9] Add documentation of indentation logic --- doc/indentation_logic/.gitignore | 1 + doc/indentation_logic/Makefile | 11 + doc/indentation_logic/pages.pdf | Bin 0 -> 272143 bytes doc/indentation_logic/src/pages.svg | 3322 +++++++++++++++++++++ doc/indentation_logic/src/split_pages.kts | 56 + 5 files changed, 3390 insertions(+) create mode 100644 doc/indentation_logic/.gitignore create mode 100644 doc/indentation_logic/Makefile create mode 100644 doc/indentation_logic/pages.pdf create mode 100644 doc/indentation_logic/src/pages.svg create mode 100644 doc/indentation_logic/src/split_pages.kts diff --git a/doc/indentation_logic/.gitignore b/doc/indentation_logic/.gitignore new file mode 100644 index 0000000..63fe867 --- /dev/null +++ b/doc/indentation_logic/.gitignore @@ -0,0 +1 @@ +/pages diff --git a/doc/indentation_logic/Makefile b/doc/indentation_logic/Makefile new file mode 100644 index 0000000..5a53c6f --- /dev/null +++ b/doc/indentation_logic/Makefile @@ -0,0 +1,11 @@ +pages.pdf: pages/page_000.pdf + pdfunite pages/page_*.pdf pages.pdf + +pages/page_000.pdf: pages/page_000.svg + for i in pages/page_*.svg ; do inkscape --export-pdf=pages/"$$( basename "$${i}" .svg )".pdf "$${i}" ; done + +pages/page_000.svg: src/pages.svg src/split_pages.kts + kotlinc-jvm -script src/split_pages.kts src/pages.svg + +clean: + rm -rf pages.pdf pages diff --git a/doc/indentation_logic/pages.pdf b/doc/indentation_logic/pages.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b0d2fde34a35e4033ca1b4415fd55fb35e228b7c GIT binary patch literal 272143 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%0Ae0$jP1+qsbp*Ee#N}1Nm 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%0D%O8Pt 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + Indentation logic of kotlin-mode + 2019-12-01 + + + Basic idea (1/2) + Programs consist of... + List of statements/expressions ,sorrounded by (curly/round) brackets ,preceded by some texts + + for (x in xs) { print(x); print(x); print(x);} + + + 1 + + + + 2 + + + + 3 + + + + + + 2 + + + + + return foo( a + b, b + c, c + d) + + + 1 + + + + + 2 + + + + 2 + + + + 3 + + + + + + + return foo( a + b, b + c, ccc + ddd) + Basic idea (2/2) + We have four cases to indent + + Case 1. after an element delimiter, such as semicolon or commaCase 2. after an open bracketCase 3. before close bracketCase 4. other case, that is to say, inside a list element + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + + + + + Case 1: after an element delimiter + + + return foo( a, b, c, d, e, f) + + + Align with the preceding element at the start of a line.To seek the preceding element at the start of a line,we seek an element delimiter (comma or semicolon) at the end of a line or open bracket before the element.Then the next token is the token we align to.Elements may start with tokens of various types,but it ends with tokens of handful types, so seeking it is easier. + + ←Seek this + then align with the next token→ + We call this token “parent”. + + + + + + bar();return foo( a, b, c, d, e, f) + + Case 2. after an open bracket + + + + + Align with the start of the “preceding text” with offset.The procedure of seeking the start of the preceding text is same as the case 1. Seek a parent token, then the next token is the token align to. + + ←Seek this + then align with the next token→ + with offset + + + + bar();return foo( a, b, c, d, e, f) + Case 3. before close bracket + Align with the start of the preceding text of the open bracketwithout offset.To find the open bracket, we can use the ‘backward-list’. + + + ←then seek this + then align with the next token→ + Seek this + + + + + Case 4. other case, inside a list element + + + foo();val x = 1 + 2 + 3 + + + + + + + + foo();val x = 1 + 2 + 3 + + + If the line is the second line,align with the start ofthe element with offset. + If the line is the third orfollowing lines,align with the the previous line. + + + if-else statement + + aaa();if (foo) bar()else baz(); + + + + + + aaa();if (foo) bar()else baz(); + + + aaa();if (foo) bar()else baz(); + + + + + If the point is after if (...), then align with the if token with offset.If the point is before else token, then align with the matching if token without offset.If the point is after else token, then align with the else token with offset.Note that if-else can be nested, so when seeking the matching if token, we have to count number of else and if tokens. + We have similar rules for for, while, and do-while. + + + Advanced topics + + + Implicit semicolons + In Kotlin and other languages, a statement may end with a newline.We use a heuristic function ‘kotlin-mode--implicit-semi-pto detect it.It examines tokens before and after the newline.Example: + for (x in xs) { aaa() if (bbb) if (ccc) ddd() else eee() fff()}ccc() + ← No implicit semicolon here... + ← Implicit semicolon here + ← ... to aligh this line to the first if token rather than the else token. + ← ... only here ... + ← Implicit semicolon here + ← Implicit semicolon here + + + Ambiguous commas, colons, curly brackets, and objects + Commas are not always contained by brackets.Texts before brackets may contain another brackets.We handle them carefully. + class C: A by object: A1, A2 { fun aaa() {} }, B by object: B1, B2 { fun bbb() {} } { fun ccc(x: X): Int { return when (x) { object: X1 by object: XX1 { fun xxx1() {} }, X2 { fun xxx2() {} }, object: Y1, Y2 { fun yyy() {} } -> 1 else -> 2 } }} + When seeking the previous element of this line, + if we got a pair of curly brackets, + then jump to the object tokenand resume seeking, + + + + to skip this comma. + + + + Ambiguous arrows + val f = { g: (Int) -> (Int) -> Int -> g(1, 2)}when (x) { 1 -> f1 as (Int) -> Int f2 as (Int) -> Int -> f3 is (Int) -> Int -> f4} + ← arrow for function type + ← arrow for function type + ← arrow for lambda parameters + ← arrow for when-entry + ← arrow for function type + ← arrow for function type + ← arrow for when-entry + ← arrow for function type + ← arrow for when-entry + Arrows have many meanings and indentation rules.We use heuristics for this, but it is not precise. + Cannot handle those cases for now.We assume all arrows inside a when-expression are parts of when-entries. + + + + + + Angle brackets <> + Token ‘<’ and ‘>’ may be used as inequality operators or angle brackets for type parameters.We use heuristics to distinguish them:- Angle bracket must be balanced.- Angle bracket cannot contain some kind of tokens. + + + Ambiguous operators + We cannot handle those cases for now. + var shl = 1val x = shl shl shl // The last “shl” is a variable named “shl”.shl < 100 && foo() // This is not a continuation of the previous line.var shl = 1val x = shl shl // The last “shl” is a shift operator. shl < 100 && foo() // This is a continuation of the previous line.var shl = 1val x = shl shl shl ++ // postfix increment operatorshl < 100 && foo() // This is not a continuation of the previous line.var shl = 1val x = shl shl ++ // prefix increment operator shl < 100 && foo() // This is a continuation of the previous line.val x = foo()!! // postfix operatorfoo() // This is not a continuation of the previous line.val x = !! // two prefix operators foo() // This is a continuation of the previous line. + + + Implementation + kotlin-mode--indent-line ← entry point for indenting line kotlin-mode--calculate-indent ← calculate the amount of the indentation kotlin-mode--calculate-indent-of-multiline-comment ← when the point is inside a multiline comment kotlin-mode--calculate-indent-of-multiline-string ← when the point is inside a multiline string kotlin-mode--calculate-indent-of-single-line-comment ← when the point is before a single-line comment kotlin-mode--calculate-indent-of-code ← other case, including before a single-line string kotlin-mode--forward-token ← lexer kotlin-mode--forward-token-simple ← lexer without unbounded recursion kotlin-mode--implicit-semi-p ← determinate implicit semicolon ... kotlin-mode--backward-token ← lexer kotlin-mode--backward-token-simple ← lexer without unbounded recursion kotlin-mode--implicit-semi-p ... kotlin-mode--calculate-indent-after-open-curly-brace ← when the point is after ‘{’ kotlin-mode--curly-brace-type ← determinate the type of the block kotlin-mode--find-parent-and-align-with-next kotlin-mode--backward-sexps-until kotlin-mode--backward-token-or-list kotlin-mode--backward-token kotlin-mode--forward-token-or-list kotlin-mode--forward-token kotlin-mode--calculate-indent-after-comma ... kotlin-mode--calculate-indent-after-semicolon ... kotlin-mode--calculate-indent-of-expression ... kotlin-mode--find-parent-and-align-with-next ... ... + Overview of functions for indentation. Details are omitted. + + + Data types + kotlin-mode--token Lexical tokens. Consists of the type, the text, and the location (start and end) of the token.kotlin-mode--indentation Location of anchor point paired with offset. + Other notable functions + kotlin-mode--indent-new-comment-line Replacement for indent-new-comment-line. Break a line, indent it, and tweak comment delimiters.kotlin-mode--post-self-insert Do electric indentation. + + diff --git a/doc/indentation_logic/src/split_pages.kts b/doc/indentation_logic/src/split_pages.kts new file mode 100644 index 0000000..a2f1334 --- /dev/null +++ b/doc/indentation_logic/src/split_pages.kts @@ -0,0 +1,56 @@ +/** + * Write each layer of Inkscape SVG file into separate SVG files. + */ +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult +import org.w3c.dom.Element + +val INKSCAPE_NS = "http://www.inkscape.org/namespaces/inkscape" + +if (args.size < 1) { + System.err.println("Usage: kotlinc-jvm -script split_pages.kts pages.svg") + System.exit(-1) +} + +val transformer = TransformerFactory.newInstance().newTransformer() + +val inputFile = File(args[0]) +val document = DocumentBuilderFactory + .newDefaultInstance() + .also { it.setNamespaceAware(true) } + .newDocumentBuilder() + .parse(inputFile) + +val svgElement = document.documentElement +val childNodes = svgElement.childNodes +val layers = 0.until(childNodes.length) + .map { childNodes.item(it) } + .filter { child -> + child is Element && + child.localName == "g" && + child.getAttributeNS(INKSCAPE_NS, "groupmode") == "layer" + } + +for (layer in layers) { + svgElement.removeChild(layer) +} + +val outputDirectory = File("pages") + +outputDirectory.mkdirs() + +for ((index, layer) in layers.withIndex()) { + svgElement.appendChild(layer) + + // Assuming `style="display:none"` + layer.attributes.removeNamedItem("style") + + val outputFile = File(outputDirectory, "page_%03d.svg".format(index)) + + transformer.transform(DOMSource(document), StreamResult(outputFile)) + + svgElement.removeChild(layer) +} From 1854330e27a2e52d8f83fb4e81d6c5fdc9522580 Mon Sep 17 00:00:00 2001 From: taku0 Date: Wed, 22 Jan 2020 23:21:44 +0900 Subject: [PATCH 6/9] Fix for 24.x when template string is incomplete --- kotlin-mode-lexer.el | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/kotlin-mode-lexer.el b/kotlin-mode-lexer.el index 6e45212..cf37034 100644 --- a/kotlin-mode-lexer.el +++ b/kotlin-mode-lexer.el @@ -2464,10 +2464,12 @@ Return the point of the beginning. Assuming the point is on a string." (goto-char (or (nth 8 (syntax-ppss)) (point))) (let (matching-bracket) - (while (setq matching-bracket - (get-text-property - (point) - 'kotlin-property--matching-bracket)) + (while (and + (setq matching-bracket + (get-text-property + (point) + 'kotlin-property--matching-bracket)) + (< (point-min) matching-bracket)) (goto-char matching-bracket) (goto-char (nth 8 (syntax-ppss)))) (point))) @@ -2479,10 +2481,11 @@ Assuming the point is on a string." (goto-char (or (nth 8 (syntax-ppss)) (point))) (let (matching-bracket) (kotlin-mode--forward-string-chunk) - (while (setq matching-bracket - (get-text-property - (1- (point)) - 'kotlin-property--matching-bracket)) + (while (and (setq matching-bracket + (get-text-property + (1- (point)) + 'kotlin-property--matching-bracket)) + (< matching-bracket (point-max))) (goto-char matching-bracket) (kotlin-mode--forward-string-chunk))) (point)) From eb2af1723e23813a09d07a59f51ded293994f8a0 Mon Sep 17 00:00:00 2001 From: taku0 Date: Wed, 23 Feb 2022 16:09:31 +0900 Subject: [PATCH 7/9] Support value modifier --- kotlin-mode-lexer.el | 7 ++++--- test/pathological.kt | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/kotlin-mode-lexer.el b/kotlin-mode-lexer.el index cf37034..0402325 100644 --- a/kotlin-mode-lexer.el +++ b/kotlin-mode-lexer.el @@ -578,7 +578,7 @@ expression as a token with one of the following types: '("vararg" "crossinline" "noinline")) (defconst kotlin-mode--function-modifier-keywords - '("tailrec" "operator" "infix" "inline" "external" "suspend")) + '("tailrec" "operator" "infix" "inline" "value" "external" "suspend")) (defconst kotlin-mode--modifier-keywords (append kotlin-mode--inheritance-modifier-keywords @@ -644,8 +644,9 @@ expression as a token with one of the following types: ;; Labels (eq (kotlin-mode--token-type previous-token) 'label) ;; Modifiers - (member (kotlin-mode--token-text previous-token) - kotlin-mode--modifier-keywords)) + (and (member (kotlin-mode--token-text previous-token) + kotlin-mode--modifier-keywords) + (not (equal (kotlin-mode--token-text previous-token) "value")))) nil) ;; Tokens that cannot end a statement diff --git a/test/pathological.kt b/test/pathological.kt index 09e7c41..c12342f 100644 --- a/test/pathological.kt +++ b/test/pathological.kt @@ -425,8 +425,15 @@ public ) -> C + + // value class + + value + class Foo { // KNOWN_BUG + } // KNOWN_BUG } + // statements fun foo() { //explicit semicolons From 2f64645460b78d76b053efb01b20e9d04192aef8 Mon Sep 17 00:00:00 2001 From: taku0 Date: Sun, 15 Jan 2023 13:37:44 +0900 Subject: [PATCH 8/9] Support definitely non-nullable type --- kotlin-mode-lexer.el | 8 ++++---- test/pathological.kt | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/kotlin-mode-lexer.el b/kotlin-mode-lexer.el index 0402325..8e99415 100644 --- a/kotlin-mode-lexer.el +++ b/kotlin-mode-lexer.el @@ -683,7 +683,7 @@ expression as a token with one of the following types: '(implicit-\; string-chunk-before-template-expression)) (member (kotlin-mode--token-text previous-token) - '("(" "{" "[" "*" "%" "/" "+" "-" "&&" "||" ":" + '("(" "{" "[" "*" "%" "/" "+" "-" "&&" "||" ":" "&" "=" "+=" "-=" "*=" "/=" "%=" "->" "." ".." "::" "?:" "?." "<=" ">=" "!=" "!==" "==" "===" "as" "as?" "is" "!is" "in" "!in" "," ";" "{" "[" "(" @@ -790,7 +790,7 @@ expression as a token with one of the following types: '(implicit-\; string-chunk-after-template-expression)) (member (kotlin-mode--token-text next-token) - '("*" "%" "/" "&&" "||" ":" "=" "+=" "-=" "*=" "/=" "%=" + '("*" "%" "/" "&" "&&" "||" ":" "=" "+=" "-=" "*=" "/=" "%=" "->" "." ".." "::" "?:" "?." "?" "<" ">" "<=" ">=" "!=" "!==" "==" "===" "," ";" ")" "]" "}" @@ -1828,7 +1828,7 @@ This function does not return `implicit-;'." "=" "+=" "-=" "*=" "/=" "%=" "->" ".." "." "?:" "?." "?" "<" ">" "<=" ">=" "==" "===" - "*" "%" "/" "+" "-"))) + "*" "%" "/" "+" "-" "&"))) (let ((text (match-string-no-properties 0)) (start (match-beginning 0)) (end (match-end 0))) @@ -2233,7 +2233,7 @@ This function does not return `implicit-;'" ((member (buffer-substring-no-properties (max (point-min) (- (point) 1)) (point)) - '("*" "%" "/" "+" "-" "!" "=" "." "?" "<" ">")) + '("*" "%" "/" "+" "-" "!" "=" "." "?" "<" ">" "&")) (backward-char) (make-instance 'kotlin-mode--token :type 'operator diff --git a/test/pathological.kt b/test/pathological.kt index c12342f..b17d82a 100644 --- a/test/pathological.kt +++ b/test/pathological.kt @@ -426,6 +426,15 @@ public -> C + // definitelyNonNullableType + fun foo( + x: + A + & + Any + ) { + } + // value class value From 70de2560a6e201b02aa4fc65c520c805db14c950 Mon Sep 17 00:00:00 2001 From: taku0 Date: Sun, 15 Jan 2023 13:50:15 +0900 Subject: [PATCH 9/9] Support closed range operator --- kotlin-mode-lexer.el | 68 ++++++++++++++++++++++---------------------- test/pathological.kt | 22 ++++++++------ 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/kotlin-mode-lexer.el b/kotlin-mode-lexer.el index 8e99415..c19e999 100644 --- a/kotlin-mode-lexer.el +++ b/kotlin-mode-lexer.el @@ -685,7 +685,7 @@ expression as a token with one of the following types: (kotlin-mode--token-text previous-token) '("(" "{" "[" "*" "%" "/" "+" "-" "&&" "||" ":" "&" "=" "+=" "-=" "*=" "/=" "%=" - "->" "." ".." "::" "?:" "?." "<=" ">=" "!=" "!==" "==" "===" + "->" "." ".." "..<" "::" "?:" "?." "<=" ">=" "!=" "!==" "==" "===" "as" "as?" "is" "!is" "in" "!in" "," ";" "{" "[" "(" ;; "class" will be handled later. "package" "import" "interface" "fun" "object" @@ -791,7 +791,7 @@ expression as a token with one of the following types: (member (kotlin-mode--token-text next-token) '("*" "%" "/" "&" "&&" "||" ":" "=" "+=" "-=" "*=" "/=" "%=" - "->" "." ".." "::" "?:" "?." "?" "<" ">" "<=" ">=" + "->" "." ".." "..<" "::" "?:" "?." "?" "<" ">" "<=" ">=" "!=" "!==" "==" "===" "," ";" ")" "]" "}" "as" "as?" "get" "set" "by" "where" "else" "catch" "finally" @@ -1487,7 +1487,7 @@ It is a type parameter list if: "is" "!is" "!in" "as" "as?" "!" "=" "+=" "-=" "*=" "/=" "%=" - ".." "::" "?:" "?." "<=" ">=" "==" "===" + ".." "..<" "::" "?:" "?." "<=" ">=" "==" "===" "package" "import" "class" "interface" "fun" "object" "val" "var" "typealias" "constructor" "by" "companion" "init" "if" "else" "when" "try" "catch" "finally" "for" "do" "while" "throw" @@ -1825,9 +1825,9 @@ This function does not return `implicit-;'." "!==" "!=" (seq "!is" word-end) (seq "!in" word-end) "as?" "!" - "=" "+=" "-=" "*=" "/=" "%=" "->" - ".." "." "?:" "?." "?" "<" ">" "<=" ">=" "==" "===" + "..<" ".." "." "?:" "?." "?" "<" ">" "<=" ">=" "===" "==" + "=" "+=" "-=" "*=" "/=" "%=" "*" "%" "/" "+" "-" "&"))) (let ((text (match-string-no-properties 0)) (start (match-beginning 0)) @@ -2151,6 +2151,35 @@ This function does not return `implicit-;'" :start (point) :end (1+ (point)))) + ;; Operator (3 letters) + ((member (buffer-substring-no-properties + (max (point-min) (- (point) 3)) + (point)) + '("===" "!==" "!is" "!in" "..<")) + (backward-char 3) + (make-instance 'kotlin-mode--token + :type 'operator + :text (buffer-substring-no-properties (point) (+ 3 (point))) + :start (point) + :end (+ 3 (point)))) + + ;; Operator (2 letters, other than as, in, or is) + ((member (buffer-substring-no-properties + (max (point-min) (- (point) 2)) + (point)) + '("++" "--" + "&&" "||" + "+=" "-=" "*=" "/=" "%=" + ".." "?." + "<=" ">=" "!=" "==" + "->")) + (backward-char 2) + (make-instance 'kotlin-mode--token + :type 'operator + :text (buffer-substring-no-properties (point) (+ 2 (point))) + :start (point) + :end (+ 2 (point)))) + ;; Open angle bracket for type parameters ;; ;; We use a heuristic: spaces are inserted around inequality sign, @@ -2185,35 +2214,6 @@ This function does not return `implicit-;'" :start (point) :end (1+ (point)))) - ;; Operator (3 letters) - ((member (buffer-substring-no-properties - (max (point-min) (- (point) 3)) - (point)) - '("===" "!==" "!is" "!in")) - (backward-char 3) - (make-instance 'kotlin-mode--token - :type 'operator - :text (buffer-substring-no-properties (point) (+ 3 (point))) - :start (point) - :end (+ 3 (point)))) - - ;; Operator (2 letters, other than as, in, or is) - ((member (buffer-substring-no-properties - (max (point-min) (- (point) 2)) - (point)) - '("++" "--" - "&&" "||" - "+=" "-=" "*=" "/=" "%=" - ".." "?." - "<=" ">=" "!=" "==" - "->")) - (backward-char 2) - (make-instance 'kotlin-mode--token - :type 'operator - :text (buffer-substring-no-properties (point) (+ 2 (point))) - :start (point) - :end (+ 2 (point)))) - ;; ? or as? ((eq (char-before) ??) (let ((pos-before-comment (point))) diff --git a/test/pathological.kt b/test/pathological.kt index b17d82a..2c2624a 100644 --- a/test/pathological.kt +++ b/test/pathological.kt @@ -667,18 +667,20 @@ fun foo() { val x = a shl // Line breaks are allowed after infix function. b // KNOWN_BUG + val y = 1 // KNOWN_BUG - var shl = 1 // KNOWN_BUG - val x = shl shl shl - shl < 100 && foo() // this is not a continuation of the previous line. + var shl = 1 + val x = shl shl shl + shl < 100 && foo() // this is not a continuation of the previous line. - var shl = 1 - val x = shl shl - shl < 100 && foo() // this is a continuation of the previous line. // KNOWN_BUG + var shl = 1 + val x = shl shl + shl < 100 && foo() // this is a continuation of the previous line. // KNOWN_BUG + val y = 1 // KNOWN_BUG - // This is not a infix function call; line breaks are // KNOWN_BUG + // This is not a infix function call; line breaks are // prohibited before infix function. - val x = // KNOWN_BUG + val x = a f (b) // So this line should not indented. @@ -686,6 +688,10 @@ fun foo() { a .. // Line breaks are prohibited before range operator. b + val x = + a ..< // Line breaks are prohibited before closed range operator. + b + val x = a + // Line breaks are prohibited before additive operators. b -