Skip to content

Commit

Permalink
fix: scala-mode calc-indent-function (lem-project#1770)
Browse files Browse the repository at this point in the history
I attached a screen record of the bug in scala-mode calc-indent-function.
lem-js-node::js-calc-indent it breaks the indentes in some cases. I tried
to fix it in this PR.
  • Loading branch information
Prikaz98 authored and carson ellsworth committed Feb 28, 2025
1 parent 5ec1126 commit 49d0d42
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
78 changes: 77 additions & 1 deletion extensions/scala-mode/scala-mode.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,80 @@
(set-syntax-parser table (make-tmlanguage-scala))
table))

(defun exceptional-indents (point tab-width)
(line-start point)
(skip-whitespace-forward point t)

;; Indent for method chain
(when (looking-at point "^(\\?|\\.)")
(return-from exceptional-indents tab-width))

(with-point ((prev-point point))
(if (lem-js-mode::move-to-previous-line prev-point)
(with-point ((start prev-point)
(end prev-point))
(line-start start)
(line-end end)
(skip-whitespace-forward start t)
(cond
((looking-at start "^case.+=>") 2)
;;Usually class indents +2 more for fields
((looking-at start "^(case +)?class [A-Z][a-zA-Z]+\\(") 2)
((and (search-backward-regexp end ",$" start)
(looking-at start "((override val )|(val ))?[a-zA-Z]+:")) 2)
(t 0)))
0)))

(defun scala-calc-indent (point)
(line-start point)
(when (in-string-or-comment-p point)
(with-point ((point point))
(back-to-indentation point)
(return-from scala-calc-indent
(if (in-string-or-comment-p point)
(point-column point)
(scala-calc-indent point)))))

(with-point ((point point)
(prev-point point))
(or (lem-js-mode::move-to-previous-line prev-point)
(return-from scala-calc-indent 0))
(let ((tab-width (variable-value 'tab-width :default point))
(column (length (lem-js-mode::get-line-indent prev-point)))
(prev-state (with-point ((start prev-point))
(line-start start)
(parse-partial-sexp (copy-point start :temporary)
(line-end start))))
(indents 0))

(incf indents (* (lem-js-mode::value-between
(+ (pps-state-paren-depth prev-state)
(if (pps-state-paren-stack prev-state) 1 0))
0 1)
tab-width))

(with-point ((prev-start prev-point)
(prev-end prev-point))
(skip-whitespace-forward point t)
(line-start prev-start)
(skip-whitespace-forward prev-start t)
(line-end prev-end)

;; Block end
(when (looking-at point "^(}|\\)|\\])")
(with-point ((p point))
(character-offset p 1)
(scan-lists p -1 0)
(line-start p)
(back-to-indentation p)
(return-from scala-calc-indent (point-column p))))

(when (= indents 0)
(decf indents (exceptional-indents prev-point tab-width)))
(incf indents (exceptional-indents point tab-width)))

(+ column indents))))

(define-major-mode scala-mode language-mode
(:name "Scala"
:keymap *scala-mode-keymap*
Expand All @@ -50,11 +124,13 @@
(setf (variable-value 'enable-syntax-highlight) t
(variable-value 'indent-tabs-mode) nil
(variable-value 'tab-width) 2
(variable-value 'calc-indent-function) 'lem-js-mode::js-calc-indent
(variable-value 'calc-indent-function) 'scala-calc-indent
(variable-value 'line-comment) "//"
(variable-value 'beginning-of-defun-function) 'beginning-of-defun
(variable-value 'end-of-defun-function) 'end-of-defun))

(define-key *scala-mode-keymap* "Return" 'newline-and-indent)

(defun beginning-of-defun (point n)
(loop :repeat n :do (search-backward-regexp point "^\\w")))

Expand Down
1 change: 1 addition & 0 deletions lem-tests.asd
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
(:file "self-insert-command")
(:file "interp")
(:file "file")
(:file "scala-mode")
(:file "completion"))
:perform (test-op (o c)
(symbol-call :rove :run c)))
51 changes: 51 additions & 0 deletions tests/sample-code/LemScalaIndent.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
object LemScalaIndent {
def main(args: Array[String]): Unit = {
for (i <- 0 until 100) {
Option(i)
.map(_ + 1)
.map(_ % 2)

val oneTwo: String = {
val one = "one"
val two = "two"
s"$one$two"
}

trait Human[T] {
val age: Int
}

trait Greeter {
def sayHello: Unit
}

case class Person(
val name: String,
surname: String,
override val age: Int
) extends Human[Person]
with Greeter {
override def sayHello: Unit = println(s"Hello, I'm $name! Nice to meet you")
}

val person = Person(
name = "vasya",
surname = "pupkin",
age = 24
)

person match {
case Person(name, surname, age) =>
println(s"Person name: $name surname: $surname")
case _ =>
throw new RuntimeException("Unrecognizable person")
}

if (person.name == "vasya") {
println("Vasya is hear")
} else {
println("Vasya is lost")
}
}
}
}
19 changes: 19 additions & 0 deletions tests/scala-mode.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(defpackage :lem-tests/scala-mode
(:use :cl
:rove
:lem-tests/utilities))
(in-package :lem-tests/scala-mode)

(deftest scala-indent-region
(with-testing-buffer (buffer (lem:find-file-buffer (sample-file "LemScalaIndent.scala")))
(testing "Test indent region"
(let ((before (lem:buffer-text buffer))
(after)
(is-correct))
(lem:indent-current-buffer)
(setq after (lem:buffer-text buffer))
(setq is-correct (string= before after))
(ok is-correct
(if is-correct
"Indent is correct"
(diff-text before after)))))))

0 comments on commit 49d0d42

Please sign in to comment.