Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Scala 3 #170

Draft
wants to merge 45 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ce97702
Read the code and fiddle toward Scala 3 a bit
Mar 16, 2021
eef2bff
Begin to match `scala-mode-syntax` against the 3.0 BNF
Apr 2, 2021
7d190ef
Octal numerals are no longer supported in Scala
Apr 13, 2021
d22aed2
Continue to match `scala-mode-syntax` against the 3.0 BNF
Apr 13, 2021
18a27ce
Continue to match `scala-mode-syntax` against the 3.0 BNF (2)
May 5, 2021
265e69a
Beginning to see just how vastly daunting this change is
Jun 15, 2021
5e73968
Realized that whitespace syntax centers around block, not body
Jun 23, 2021
cfeef68
Still trying to figure out my way around
Jun 23, 2021
225081e
Some crude but meaningful progress on indentation
Jun 24, 2021
7dad271
Small and imprecise progress on indentation
Jun 25, 2021
cd11904
finish going through the lexical syntax, and some
Jul 22, 2021
1fa19e6
I am not going to make progress just by tweaking
Jul 22, 2021
1859e83
Really go nuclear on the existing indenting algorithms
Jul 22, 2021
c11cbbd
baby steps on a new approach
Oct 4, 2021
738b88e
primitive support for object blocks with whitespace
Oct 4, 2021
b00f933
support enum and more flexible object blocks
Oct 4, 2021
80d75fc
initial support for indentation-based match-case expressions
Oct 4, 2021
06494c3
fix misbehavior on whitespace around `case`
Oct 4, 2021
ac22bd6
support many more syntactic scenarios
Oct 4, 2021
8218464
initial support for for-comprehensions, which are problematic
Oct 4, 2021
8eb91cd
initial support for dot-chaining, which is problematic
Oct 4, 2021
0e77db6
get for-comps working tolerably, and a bit of work on dot chaining
Oct 4, 2021
0f3e7ea
get if-then-else working
Oct 5, 2021
c5b0f46
role back dedented; if-then-else edgecases; &c
Oct 5, 2021
11d5c11
hey, this is starting to work pretty well
Oct 5, 2021
f2a0fbd
Get dot-chaining much more stable; but other things unstable
Dec 23, 2021
b7ea24a
Run both indentation algorithms; choose result by simple heuristic
Dec 23, 2021
442203e
Still trying to get dot-chaining perfected
Dec 23, 2021
d26a1d2
Detect infinite loop and break
Dec 30, 2021
a6983a5
Seems to fix the infinite loop
Dec 30, 2021
c8a7bba
Extract function: `scala-indent:continue-lookback?`
Dec 30, 2021
1ef3ce5
Augh, try to fix a bunch of cases without breaking others
Jan 4, 2022
c18f2bb
WIP: Adding cycling indent
Jan 5, 2022
74eaa24
Cycles between current indent and 0 when cycle-indent is true
jackcviers May 16, 2022
35f265f
Merge pull request #1 from jackcviers/scala3-indent-cycle
Kazark May 18, 2022
9d77eec
Fixes incorrect argument order in call-interactively
jackcviers May 19, 2022
e22ef62
Merge pull request #2 from jackcviers/scala3-indent-cycle
Kazark May 19, 2022
ad122e4
fix typo in function name
maemre Apr 1, 2023
56d1f77
indent after declarations
maemre Apr 1, 2023
5323a09
do not push nil into the analysis stack
maemre Apr 1, 2023
9df2ba4
disable aggressive paren rule temporarily
maemre Apr 3, 2023
be4ef5c
preliminary indentation support for `extension`
maemre Jun 5, 2023
417dd61
add support for `do` as an align keyword like `then` and `else`.
maemre Jun 5, 2023
5eef13b
Preserve line numbers for blank lines.
maemre Jun 5, 2023
238d4ea
Merge pull request #7 from maemre/scala3
Kazark Jul 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 78 additions & 35 deletions scala-mode-syntax.el
Original file line number Diff line number Diff line change
@@ -1,44 +1,91 @@
;;;; scala-mode-syntax.el - Major mode for editing scala, syntax
;;; Copyright (c) 2012 Heikki Vesalainen
;;;; scala-mode-syntax.el - Major mode for editing Scala, syntax
;;; Copyright (c) 2021 Heikki Vesalainen
;;; For information on the License, see the LICENSE file

;;; Based on Scala Language Specification (SLS) Version 2.9
;;; Based on Scala Language Specification (SLS) Version 3.0
;;; https://dotty.epfl.ch/docs/internals/syntax.html

;;;;
;;;; Scala syntax regular expressions
;;;;

;;; Based on the Scala language specification 2.9. Note: order is not
;;; Based on the Scala language specification 3.0. Note: order is not
;;; the same as in the document, as here things are declared before
;;; used.

;;; A note on naming. Things that end with '-re' are regular
;;; expressions. Things that end with '-group' are regular expression
;;; A note on naming. Things that end with `-re' are regular
;;; expressions. Things that end with `-group' are regular expression
;;; character groups without the enclosing [], i.e. they are not
;;; regular expressions, but can be used in declaring one.

;; single letter matching groups (Chapter 1)
(defconst scala-syntax:hexDigit-group "0-9A-Fa-f")
(defconst scala-syntax:UnicodeEscape-re (concat "\\\\u[" scala-syntax:hexDigit-group "]\\{4\\}"))

(defconst scala-syntax:UnicodeEscape-re
;; using `format' allows editing these regexes with one step closer to a sane
;; number of backslash escapes, via `string-edit', at the expense of making %
;; a special character
(format "\\\\u[%s]\\{4\\}" scala-syntax:hexDigit-group))

;; TODO BNF for `upper' adds the coments "and Unicode category Lu"; do Emacs
;; regexes handle this naturally?
(defconst scala-syntax:upper-group "[:upper:]\\$") ;; missing _ to make ids work
(defconst scala-syntax:upperAndUnderscore-group (concat "_" scala-syntax:upper-group ))
;; NOTE `upperAndUnderscore' corresponds to the `upper' group in the BNF
(defconst scala-syntax:upperAndUnderscore-group
(concat "_" scala-syntax:upper-group ))
;; TODO BNF for `lower' adds the coments "and Unicode category Ll"; do Emacs
;; regexes handle this naturally?
(defconst scala-syntax:lower-group "[:lower:]")
(defconst scala-syntax:letter-group (concat scala-syntax:lower-group scala-syntax:upper-group)) ;; TODO: add Lt, Lo, Nl
;; TODO BNF for `lower' adds the coments "and Unicode categories Lo, Lt, Nl"
(defconst scala-syntax:letter-group (concat scala-syntax:lower-group
scala-syntax:upper-group))
(defconst scala-syntax:digit-group "0-9")
(defconst scala-syntax:letterOrDigit-group (concat
scala-syntax:upperAndUnderscore-group
scala-syntax:lower-group
scala-syntax:digit-group))
(defconst scala-syntax:opchar-safe-group "!%&*+/?\\\\^|~-") ;; TODO: Sm, So
;; NOTE `letterOrDigit' does not have a separate entry in the 3.0 BNF.
(defconst scala-syntax:letterOrDigit-group
(concat
scala-syntax:upperAndUnderscore-group
scala-syntax:lower-group
scala-syntax:digit-group))
;; TODO ensure Unicode Sm, So disallowed in `opchar'
;; TODO do the math: check these positively stated symbols against the
;; negatively stated BNF.
(defconst scala-syntax:opchar-safe-group "!%&*+/?\\\\^|~-")
(defconst scala-syntax:opchar-unsafe-group "#:<=>@")
(defconst scala-syntax:opchar-group (concat scala-syntax:opchar-unsafe-group
scala-syntax:opchar-safe-group))

;; Scala delimiters (Chapter 1), but no quotes
;; NOTE `delim' in the BNF
;; TODO should backtick be here? I'm not sure it is handled correctly ATM.
Kazark marked this conversation as resolved.
Show resolved Hide resolved
;; Scala delimiters, but no quotes
(defconst scala-syntax:delimiter-group ".,;")

;; Integer Literal (Chapter 1.3.1)
;; NOTE BNF also has a definition here for `printabeChar'
;; NOTE BNF also has a definition here for `charEscapeSeq'

(defconst scala-syntax:op-re
(concat "[" scala-syntax:opchar-group "]+" ))

(defconst scala-syntax:idrest-re
;; Eagerness of regexp causes problems with _. The following is a workaround,
;; but the resulting regexp matches only what SLS demands.
(format "\\([_]??[%s%s]+\\)*\\(_+%s\\|_\\)?"
scala-syntax:letter-group
scala-syntax:digit-group
scala-syntax:op-re))

(defconst scala-syntax:varid-re
(concat "[" scala-syntax:lower-group "]" scala-syntax:idrest-re))

;; `alphaid' introduced by SIP-11 - String Interpolation
;; https://docs.scala-lang.org/sips/string-interpolation.html
(defconst scala-syntax:alphaid-re
(format "\\([%s%s]%s\\)"
scala-syntax:lower-group
scala-syntax:upperAndUnderscore-group
scala-syntax:idrest-re))

;; TODO resume here (`plainid' in the BNF)

;; Integer Literal
(defconst scala-syntax:nonZeroDigit-group "1-9")
(defconst scala-syntax:octalDigit-group "0-7")
(defconst scala-syntax:decimalNumeral-re
Expand Down Expand Up @@ -116,19 +163,10 @@
"\\(?:" scala-syntax:multiLineStringLiteral-end-re "\\)?"
"\\|\\(\"\\)" "\\(\\\\.\\|[^\"\n\\]\\)*" "\\(\"\\)"))

;; Identifiers (Chapter 1.1)
(defconst scala-syntax:op-re
(concat "[" scala-syntax:opchar-group "]+" ))
(defconst scala-syntax:idrest-re
;; Eagerness of regexp causes problems with _. The following is a workaround,
;; but the resulting regexp matches only what SLS demands.
(concat "\\(" "[_]??" "[" scala-syntax:letter-group scala-syntax:digit-group "]+" "\\)*"
"\\(" "_+" scala-syntax:op-re "\\|" "_" "\\)?"))
(defconst scala-syntax:varid-re (concat "[" scala-syntax:lower-group "]" scala-syntax:idrest-re))
(defconst scala-syntax:capitalid-re (concat "[" scala-syntax:upperAndUnderscore-group "]" scala-syntax:idrest-re))
;; alphaid introduce by SIP11
(defconst scala-syntax:alphaid-re (concat "\\(" "[" scala-syntax:lower-group scala-syntax:upperAndUnderscore-group "]" scala-syntax:idrest-re "\\)"))
(defconst scala-syntax:plainid-re (concat "\\(" scala-syntax:alphaid-re "\\|" scala-syntax:op-re "\\)"))
(defconst scala-syntax:capitalid-re
(concat "[" scala-syntax:upperAndUnderscore-group "]" scala-syntax:idrest-re))
(defconst scala-syntax:plainid-re
(concat "\\(" scala-syntax:alphaid-re "\\|" scala-syntax:op-re "\\)"))
;; stringlit is referred to, but not defined Scala Language Specification 2.9
;; we define it as consisting of anything but '`' and newline
(defconst scala-syntax:stringlit-re "[^`\n\r]")
Expand Down Expand Up @@ -364,12 +402,14 @@
(defconst scala-syntax:final-re
(concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:final-unsafe-re "\\)"))

;; TODO open
(defconst scala-syntax:sealed-unsafe-re
(regexp-opt '("sealed") 'words))

(defconst scala-syntax:sealed-re
(concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:sealed-unsafe-re "\\)"))

;; TODO using/given
(defconst scala-syntax:implicit-unsafe-re
(regexp-opt '("implicit") 'words))

Expand All @@ -395,7 +435,7 @@
(concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:protected-unsafe-re "\\)"))

(defconst scala-syntax:modifiers-unsafe-re
(regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy"
(regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy" "open"
"private" "protected") 'words))

(defconst scala-syntax:modifiers-re
Expand Down Expand Up @@ -705,6 +745,7 @@ stableId"
(and (looking-at scala-syntax:case-re)
(goto-char (match-end 0))
(scala-syntax:skip-forward-ignorable)
;; TODO "not in an enum environment"
(not (looking-at-p scala-syntax:class-or-object-re)))))

(defun scala-syntax:looking-back-empty-line-p ()
Expand Down Expand Up @@ -784,6 +825,7 @@ one."
(when (scala-syntax:looking-at "[[]")
(forward-list)))))

;; TODO if-then-else syntax
(defun scala-syntax:looking-back-else-if-p ()
;; TODO: rewrite using (scala-syntax:if-skipped (scala:syntax:skip-backward-else-if))
(save-excursion
Expand Down Expand Up @@ -961,18 +1003,18 @@ not. A list must be either enclosed in parentheses or start with
;; Functions to help with finding the beginning and end of scala definitions.

(defconst scala-syntax:modifiers-re
(regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy"
(regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy" "using" "extension"
"private" "protected" "case") 'words))

(defconst scala-syntax:whitespace-delimeted-modifiers-re
(defconst scala-syntax:whitespace-delimited-modifiers-re
(concat "\\(?:" scala-syntax:modifiers-re "\\(?: *\\)" "\\)*"))

(defconst scala-syntax:definition-words-re
(mapconcat 'regexp-quote '("class" "object" "trait" "val" "var" "def" "type") "\\|"))
(mapconcat 'regexp-quote '("class" "object" "trait" "val" "var" "def" "type" "enum" "given") "\\|"))

(defun scala-syntax:build-definition-re (words-re)
(concat " *"
scala-syntax:whitespace-delimeted-modifiers-re
scala-syntax:whitespace-delimited-modifiers-re
words-re
"\\(?: *\\)"
"\\(?2:"
Expand Down Expand Up @@ -1018,6 +1060,7 @@ val a, b = (1, 2)
(scala-syntax:find-brace-equals-or-next)
(scala-syntax:handle-brace-equals-or-next))

;; TODO or : or... (indentation syntax)
(defun scala-syntax:find-brace-equals-or-next ()
(scala-syntax:go-to-pos
(save-excursion
Expand Down
8 changes: 5 additions & 3 deletions scala-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,17 @@ If there is no plausible default, return nil."

;;;###autoload
(defun scala-mode:goto-start-of-code ()
"Go to the start of the real code in the file: object, class or trait."
"Go to the start of the real code in the file.

Object, class, trait, enum, def, val, or given."
(interactive)
(let* ((case-fold-search nil))
(search-forward-regexp "\\([[:space:]]+\\|^\\)\\(class\\|object\\|trait\\)" nil t)
(search-forward-regexp "\\([[:space:]]+\\|^\\)\\(class\\|enum\\|object\\|trait\\|def\\|val\\|given\\)" nil t)
(move-beginning-of-line nil)))

;;;###autoload
(define-derived-mode scala-mode prog-mode "Scala"
"Major mode for editing scala code.
"Major mode for editing Scala code.

When started, runs `scala-mode-hook'.

Expand Down