branch: elpa/scala-mode commit a8b0936d466b499f0e20897e93c2a9236c3608d0 Author: Heikki Vesalainen <heikkivesalai...@yahoo.com> Commit: Heikki Vesalainen <heikkivesalai...@yahoo.com>
Working on syntax --- Example.scala | 58 +++++++- Test.scala | 31 ++++ scala-mode-constants.el | 8 ++ scala-mode-fontlock.el | 28 ++++ scala-mode-indent.el | 13 ++ scala-mode-map.el | 26 ++++ scala-mode-syntax.el | 376 ++++++++++++++++++++++++++++++++++++++++++++++++ scala-mode.el | 97 +++++++++++++ 8 files changed, 631 insertions(+), 6 deletions(-) diff --git a/Example.scala b/Example.scala index 7eb013c..3b312b6 100644 --- a/Example.scala +++ b/Example.scala @@ -12,14 +12,14 @@ Indenting happens relative to an indent anchor. Usually the indent anchor is the the declaration or expression is inside a parameter list, then the anchor is inside the list. */ -def f(s: String <%, - i: Int) = +def f(s: String, + i: Int) = s.take(i) // indented relative to 'def' /* */ val x = foo( zot, // indented relative to '/* */' someThing - map (x=a===> x.length) // indented relative to 'someThing' + map (x => x.length) // indented relative to 'someThing' ) val x = @@ -41,7 +41,7 @@ Any line not beginning with a declaration or expression start reserved word (i.e This rule does not apply in the following cases: - if the previous lines was empty - previous line was an annotation -- previous statement ended with ';' +- previous statement ended with ';,' - the current line starts a body (of declaration or anonymous function) - block or simple expression starts with an anonymous function (lambda) declaration */ @@ -89,6 +89,11 @@ List("foo", "bar") s.length ) +List("foo") map ( + s => // start lambda + s.length // run-on rule does not apply +) + /* 1.3 Parameter lists @@ -102,6 +107,7 @@ of a line, it will be indented to the same column as the first paremeter groups parentheses. - A closing parantheses that is on its own line, will be indented to the same column with the indent anchor. +- Rule does not apply, if the first parameter was a lambda expression */ class Foo( @@ -181,12 +187,17 @@ val z = for { i <- 1 to 10, } /* -1.7 If statements +1.7 If and try statements If statements will be indented acording to the following rules: -- The the body of the 'if' or 'else if' statement is a simple expression (i.e. not a block), then the next 'else if' or 'else' is aligned with the previous 'if' or 'else if', +- If the the body of the 'if' or 'else if' statement is a simple expression (i.e. not a block), then the next 'else if' or 'else' is aligned with the previous 'if' or 'else if', - otherwise 'else if' and 'else' is aligned with previous block close. + +Try statements will be indented acording to the following rules: +- If the body of the 'try' is a simple expression (i.e. not a block), then the next 'catch' is aligned with the previous try +- otherwise 'catch' is aligned with previous block close. +- Finally is aligned similarly */ val x = if (kissa) @@ -204,6 +215,25 @@ val y = if (kissa) { zot } +val a = try + foo() + catch { + case e => bar() + } + +val b = try { + foo() +} catch { + case e => bar() +} finally { + zot() +} + +val c = try + zot() + finally + log("zotted") + /* 1.8 Block opening curly brackets on new line @@ -232,3 +262,19 @@ class Foo "hello" } } + +/* +2. font-lock support +*/ + +/* +2.1 Types +*/ + +val strings = Seq("""multi +line"quote +strings""", "normal strings", 'c') + +val `quoted id` = 1 + +val symbol = 'Symbol diff --git a/Test.scala b/Test.scala new file mode 100644 index 0000000..5fb26be --- /dev/null +++ b/Test.scala @@ -0,0 +1,31 @@ +/* fooo bar */ + +"""foo""".r """bar""".r + +""" +qasduote_asdasd a+_ +'f' +""", val zot "fo\ro" + +__ + +asdasd_dfasdf_>> _<< + +val x:[Type <% Foo] => '\u123c' +val y = "foo a\"sdasd" +" ", " ", "as\"dasd" +`asdasd`> asdasdasd +"asdasd + +"""asdasdasd +asdasda +asdasdasd +asdasdasd"asda" +asdasdasdasdasd asd"" +asdasd +asdasdasdasdasd"""asdasdasd + +"""asdasd"""asasdasd + +"""asdasdasdasdasdlkjhasd +asdasdkj""" diff --git a/scala-mode-constants.el b/scala-mode-constants.el new file mode 100644 index 0000000..2c7507d --- /dev/null +++ b/scala-mode-constants.el @@ -0,0 +1,8 @@ +;;; scala-mode-constants.el - Major mode for editing scala, constants +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +;;; Based on Scala Language Specification (SLS) Version 2.9 + +(provide 'scala-mode-constants) + diff --git a/scala-mode-fontlock.el b/scala-mode-fontlock.el new file mode 100644 index 0000000..31f64ee --- /dev/null +++ b/scala-mode-fontlock.el @@ -0,0 +1,28 @@ +;;; scala-mode-fontlock.el - Major mode for editing scala, font-lock +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +(provide 'scala-mode-fontlock) + +(require 'scala-mode-syntax) +(require 'scala-mode-constants) + +(defun scala-font-lock:mark-symbol (limit) + (if (re-search-forward scala-syntax:reserved-symbols-re limit t) + (goto-char (match-end 2)) ;; step back to the match (re matches futher) + nil)) + +(defun scala-font-lock:mark-underscore (limit) + (if (re-search-forward scala-syntax:reserved-symbol-underscore-re limit t) + (goto-char (match-end 2)) ;; step back to the match (re matches futher) + nil)) + +(defvar scala-font-lock:keywords + `(;; keywords + (,scala-syntax:keywords-re 0 font-lock-keyword-face) + + ;; symbols + (scala-font-lock:mark-symbol 2 font-lock-keyword-face) + (scala-font-lock:mark-underscore 2 font-lock-keyword-face) + +)) diff --git a/scala-mode-indent.el b/scala-mode-indent.el new file mode 100644 index 0000000..b10356e --- /dev/null +++ b/scala-mode-indent.el @@ -0,0 +1,13 @@ +;;; scala-mode.el - Major mode for editing scala, indenting +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +(provide 'scala-mode-indent) + +(defun scala-goto-indent-anhor () + "moves back to the point whose column will be used as +the anchor relative to which indenting is calculated." + (interactive) + +) + diff --git a/scala-mode-map.el b/scala-mode-map.el new file mode 100644 index 0000000..9ad1758 --- /dev/null +++ b/scala-mode-map.el @@ -0,0 +1,26 @@ +;;; scala-mode-map.el - Major mode for editing scala, keyboard map +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +(provide 'scala-mode-map) + +(defmacro scala-mode-map:define-keys (key-map key-funcs) + (cons 'progn (mapcar + #'(lambda (key-func) + `(define-key ,key-map ,(car key-func) ,(cadr key-func))) + key-funcs))) + +(defvar scala-mode-map nil + "Local key map used for scala mode") + +(when (not scala-mode-map) + (let ((keymap (make-sparse-keymap))) + (scala-mode-map:define-keys + keymap + (([backspace] 'backward-delete-char-untabify) + ;; ("\r" 'scala-newline) + ([(control c)(control c)] 'comment-region) + ;; ("}" 'scala-electric-brace) + )) + (setq scala-mode-map keymap))) + diff --git a/scala-mode-syntax.el b/scala-mode-syntax.el new file mode 100644 index 0000000..84c3e10 --- /dev/null +++ b/scala-mode-syntax.el @@ -0,0 +1,376 @@ +;;;; scala-mode-syntax.el - Major mode for editing scala, syntax +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +;;; Based on Scala Language Specification (SLS) Version 2.9 + +(provide 'scala-mode-syntax) + +(require 'scala-mode-constants) + +;;;; Scala syntax regular expressions +;;; Based on the Scala language specification 2.9. 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 +;;; 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:upper-group "_[:upper:]\\$") ;; missing _ to make ids work +(defconst scala-syntax:upperAndUnderscore-group (concat "_" scala-syntax:upper-group )) +(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 +(defconst scala-syntax:digit-group "0-9") +(defconst scala-syntax:opchar-group "!#%&*+/:<=>?@\\\\\\^|~\\-") ;; TODO: Sm, So + +;; Integer Literal (Chapter 1.3.1) +(defconst scala-syntax:nonZeroDigit-group "1-9") +(defconst scala-syntax:octalDigit-group "0-7") +(defconst scala-syntax:decimalNumeral-re + (concat "0" + "\\|[" scala-syntax:nonZeroDigit-group "][" scala-syntax:digit-group "]*")) +(defconst scala-syntax:hexNumeral-re (concat "0x[" scala-syntax:hexDigit-group "]+")) +(defconst scala-syntax:octalNumeral-re (concat "0[" scala-syntax:octalDigit-group "]+")) +(defconst scala-syntax:integerLiteral-re (concat "-?" ;; added from definition of literal + "\\(" scala-syntax:decimalNumeral-re + "\\|" scala-syntax:hexNumeral-re + "\\|" scala-syntax:octalNumeral-re + "\\)[Ll]?")) + +;; Floating Point Literal (Chapter 1.3.2) +(defconst scala-syntax:exponentPart-re (concat "\\([eE][+-]?[" scala-syntax:digit-group "]+\\)")) +(defconst scala-syntax:floatType-re "[fFdD]") +(defconst scala-syntax:floatingPointLiteral-re + (concat "-?" ;; added from definition of literal + "\\([" scala-syntax:digit-group "]+\\.[" scala-syntax:digit-group "]*" + scala-syntax:exponentPart-re "?" scala-syntax:floatType-re "?" + "\\|" "\\.[" scala-syntax:digit-group "]+" + scala-syntax:exponentPart-re "?" scala-syntax:floatType-re "?" + "\\|" "[" scala-syntax:digit-group "]+" scala-syntax:exponentPart-re + "\\|" "[" scala-syntax:digit-group "]+" scala-syntax:floatType-re "\\)")) + +;; Boolean Literals (Chapter 1.3.3) +(defconst scala-syntax:booleanLiteral-re "true|false") + +;; Escape Sequences (Chapter 1.3.6) +(defconst scala-syntax:escapeSequence-re "\\\\['btnfr\"\\\\]") + +;; Character Literals (Chapter 1.3.4) +(defconst scala-syntax:characterLiteral-re + (concat "\\('\\)\\(" "[^\\\\]" ;; should be just printable char, but this is faster + "\\|" scala-syntax:escapeSequence-re + "\\|" scala-syntax:UnicodeEscape-re "\\)\\('\\)")) + +;; String Literals (Chapter 1.3.5) +(defconst scala-syntax:stringElement-re + (concat "\\(" "[^\n\"\\\\\]" + "\\|" scala-syntax:escapeSequence-re + "\\|" scala-syntax:UnicodeEscape-re "\\)")) +(defconst scala-syntax:oneLineStringLiteral-re (concat "\\(\"\\)" scala-syntax:stringElement-re "*\\(\"\\)")) +(defconst scala-syntax:multiLineStringLiteral-re + "\\(\"\\)\\(\"\"\\(\"?\"?[^\"]\\)*\"\"+\\)\\(\"\\)") +(defconst scala-syntax:stringLiteral-re + (concat "\\(" scala-syntax:multiLineStringLiteral-re + "\\|" scala-syntax:oneLineStringLiteral-re "\\)" )) + +;; 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)) +(defconst scala-syntax:plainid-re (concat "\\(" scala-syntax:capitalid-re + "\\|" scala-syntax:varid-re + "\\|" scala-syntax:op-re "\\)")) +;; stringlit is referred to, but not defined Scala Language Specification 2.9 +(defconst scala-syntax:stringlit-re (concat scala-syntax:stringElement-re "*?")) +(defconst scala-syntax:quotedid-re (concat "`" scala-syntax:stringlit-re "`")) +(defconst scala-syntax:id-re (concat "\\(" scala-syntax:plainid-re + "\\|" scala-syntax:quotedid-re "\\)")) + +;; Symbol literals (Chapter 1.3.7) +(defconst scala-syntax:symbolLiteral-re + ;; must end with non-' to not conflict with scala-syntax:characterLiteral-re + (concat "'" scala-syntax:plainid-re "\\([^']\\|$\\)")) + +;; Literals (Chapter 1.3) +(defconst scala-syntax:literal-re + (concat "\\(" scala-syntax:integerLiteral-re + "\\|" scala-syntax:floatingPointLiteral-re + "\\|" scala-syntax:booleanLiteral-re + "\\|" scala-syntax:characterLiteral-re + "\\|" scala-syntax:stringLiteral-re + "\\|" scala-syntax:symbolLiteral-re + "\\|" "null" "\\)")) + +;; Paths (Chapter 3.1) +(defconst scala-syntax:classQualifier-re (concat "\\[" scala-syntax:id-re "\\]")) +(defconst scala-syntax:stableId-re + (concat "\\(\\(" scala-syntax:id-re + "\\|" "this" + "\\|" "super" scala-syntax:classQualifier-re "\\)\\.\\)*" + scala-syntax:id-re)) +(defconst scala-syntax:path-re + (concat "\\(" scala-syntax:stableId-re + "\\|" "\\(" scala-syntax:id-re "\\." "\\)?" "this" "\\)")) + + +;;; +;;; Other regular expressions +;;; + +(defconst scala-syntax:empty-line-re + "^\\s *$") + +(defconst scala-syntax:keywords-re + (regexp-opt '("abstract" "case" "catch" "class" "def" + "do" "else" "extends" "false" "final" + "finally" "for" "forSome" "if" "implicit" + "import" "lazy" "match" "new" "null" + "object" "override" "package" "private" "protected" + "return" "sealed" "super" "this" "throw" + "trait" "try" "true" "type" "val" + "var" "while" "with" "yield") 'words)) + + +;; false, true, null, super, this are neither + +(defconst scala-syntax:after-reserved-symbol-underscore-re + ;; what can be after reserved symbol _ (if there is something else, it + ;; will be upper case letter per SLS) + (concat "$\\|[^" scala-syntax:upper-group scala-syntax:lower-group scala-syntax:digit-group "]")) + +(defconst scala-syntax:reserved-symbol-underscore-re + ;; reserved symbol _ + (concat "\\(^\\|[^" scala-syntax:upper-group scala-syntax:lower-group scala-syntax:digit-group "]\\)" + "\\(_\\)" + "\\($\\|[^" scala-syntax:upper-group scala-syntax:lower-group scala-syntax:digit-group "]\\)")) + +(defconst scala-syntax:reserved-symbols-re + ;; reserved symbols and XML starts ('<!' and '<?') + (concat "\\(^\\|[^" scala-syntax:opchar-group "]\\)" + "\\([:=#@\u21D2\u2190]\\|=>\\|<[:%!?\\-]\\|>:\\)" + "\\($\\|[^" scala-syntax:opchar-group "]\\)")) + +(defconst scala-syntax:reserved-re + (concat scala-syntax:keywords-re "\\|" scala-syntax:reserved-symbols-re "\\|" scala-syntax:reserved-symbol-underscore-re)) + +(defconst scala-syntax:mustNotTerminate-keywords-re + "Keywords which cannot end a expression and are infact a sign of run-on." + (regexp-opt '("catch", "else", "extends", "finally", + "forSome", "match", "with", "yield") 'words)) + +(defconst scala-syntax:mustNotTerminate-re + "All keywords and symbols that cannot terminate a expression +and are infact a sign of run-on, except for @, which may start +an expression with annotation." + (concat "\\(" scala-syntax:mustNotTerminate-keywords-re + "\\|" scala-syntax:reserved-symbols-re "\\)")) + +(defconst scala-syntax:mustTerminate-re + "Symbols that must terminate an expression, i.e the following expression +cannot be a run-on. This includes only , and ; and the empty line" + (concat "\\([,;]|" scala-syntax:empty-line-re "\\)")) + +(defconst scala-syntax:mustNotContinue-re + "Keywords that begin an expression, i.e they cannot be run-on to the +previous the line even if there is no semi in between." + ;; 'case' and 'while' are unclear. 'case' might belong to 'case class' + ;; while 'while' might belong to a 'do..while' + (regexp-opt '("abstract", "class", "def", "do", "final", + "for", "if", "implicit", "import", "lazy", + "new", "object", "override", "package", "private", + "protected", "return", "sealed", "throw", + "trait", "try", "type", "val", "var") 'words)) + +(defconst scala-syntax:double-arrow-re + "=>\\|\u21D2") + +(defconst scala-syntax:multiLineStringLiteral-start-re + "\\(\"\\)\"\"") + +(defconst scala-syntax:multiLineStringLiteral-end-re + "\"\"+\\(\"\\)") + +(defun scala-syntax:find-reserved-symbols () + (interactive) + (re-search-forward scala-syntax:reserved-symbols-re nil t)) + + +;;;; Character syntax table and related syntax-propertize functions +;;; The syntax table relies havily on the syntax-propertize-functions being +;;; run. Hence this syntax requires at least emacs 24, which introduced +;;; this new facility. + +(defvar scala-syntax:syntax-table nil + "Syntax table used in `scala-mode' buffers.") +(when (not scala-syntax:syntax-table) + (let ((syntab (make-syntax-table))) + ;; 1. start by reseting the syntax table: only (){}[] are + ;; parentheses, so all others marked as parentheses in the parent + ;; table must be marked as symbols, nothing is a punctuation + ;; unless otherwise stated + (map-char-table + #'(lambda (key value) + (when (or (= (syntax-class value) 4) ; open + (= (syntax-class value) 5) ; close + (= (syntax-class value) 1)) ; punctuation + (modify-syntax-entry key "_" syntab))) + (char-table-parent syntab)) + + ;; Below 'space', everything is either illegal or whitespace. + ;; Consider as whitespace, unless otherwise stated below. + (modify-syntax-entry '(0 . 32) " " syntab) + + ;; The scala parentheses + (modify-syntax-entry ?\( "()" syntab) + (modify-syntax-entry ?\[ "(]" syntab) + (modify-syntax-entry ?\{ "(}" syntab) + (modify-syntax-entry ?\) ")(" syntab) + (modify-syntax-entry ?\] ")[" syntab) + (modify-syntax-entry ?\} "){" syntab) + + ;; _ is upper-case letter, but will be modified to be punctuation + ;; when in reserved symbol position by syntax-propertize-function + (modify-syntax-entry ?\_ "w" syntab) + + ;; by default all opchars are punctuation, but they will be + ;; modified by syntax-propertize-function to be symbol + ;; constituents when a part of varid or capitalid + (dolist (char (mapcar 'identity "#%:<=>@!&*+-/?\\^|~\u21D2\u2190")) ;; TODO: Sm, So + (modify-syntax-entry char "." syntab)) + + ;; what can I say? It's the escape char. + (modify-syntax-entry ?\\ "." syntab) + + ;; scala strings cannot span lines, so we mark + ;; " as punctuation, but do the real stuff + ;; in syntax-propertize-function for properly + ;; formatted strings. + (modify-syntax-entry ?\" "." syntab) + + ;; backquote is given paired delimiter syntax so that + ;; quoted ids are parsed as one sexp. Fontification + ;; is done separately. + (modify-syntax-entry ?\` "$" syntab) + + ;; ' is considered an expression prefix, since it can + ;; both start a Symbol and is a char quote. It + ;; will be given string syntax by syntax-propertize-function + ;; for properly formatted char literals. + (modify-syntax-entry ?\' "'" syntab) + + ;; punctuation as specified by SLS + (modify-syntax-entry ?\. "." syntab) + (modify-syntax-entry ?\; "." syntab) + (modify-syntax-entry ?\, "." syntab) + + ;; comments + ;; the `n' means that comments can be nested + (modify-syntax-entry ?\/ ". 124b" syntab) + (modify-syntax-entry ?\* ". 23n" syntab) + (modify-syntax-entry ?\n "> b" syntab) + (modify-syntax-entry ?\r "> b" syntab) + + (setq scala-syntax:syntax-table syntab))) + +(defun scala-syntax:propertize-extend-region (start end) + "See syntax-propertize-extend-region-functions" + ;; nothing yet + nil) + +(defmacro scala-syntax:put-syntax-table-property (match-group value) + "Add 'syntax-table entry 'value' to the region marked by the +match-group 'match-group'" + `(put-text-property (match-beginning ,match-group) + (match-end ,match-group) + 'syntax-table + ,value)) + +(defun scala-syntax:propertize-characterLiterals (start end) + "Mark start and end of character literals as syntax class +7 (string quotes). Only valid character literals will be marked." + (save-excursion + (goto-char start) + (while (re-search-forward scala-syntax:characterLiteral-re end t) + (scala-syntax:put-syntax-table-property 1 '(7 . nil)) + (scala-syntax:put-syntax-table-property 3 '(7 . nil))))) + +(defun scala-syntax:propertize-stringLiterals (start end) + "Mark start and end of both one-line and multi-line string +literals. One-line strings use syntax class 7 (string quotes), +while multi-line strings are marked with 15 (generic string +delimiter). Multi-line string literals are marked even if they +are unbalanced. One-line string literals have to be balanced to +get marked. This means invalid one-line strings will not be fontified." + (let* ((string-state (nth 3 (syntax-ppss start))) + (unbalanced-p (eq string-state t))) + + (if (and string-state (not unbalanced-p)) + ;; a normal string is open, let's de-propertize + (remove-text-properties start end '(syntax-table nil)) + (save-excursion + (goto-char start) + ;; close the closing for the unbalanced multi-line literal + (when (and unbalanced-p + (re-search-forward scala-syntax:multiLineStringLiteral-end-re end t)) + (scala-syntax:put-syntax-table-property 1 '(15 . nil))) + ;; match any balanced one-line or multi-line literals + (catch 'break + (while (re-search-forward scala-syntax:stringLiteral-re end t) + (cond + ((match-beginning 2) + (scala-syntax:put-syntax-table-property 2 '(15 . nil)) + (scala-syntax:put-syntax-table-property 5 '(15 . nil))) + ((or (match-end 7) ; group 7 is non-nil, ie. online string is not empty + (= (match-end 8) (line-end-position)) ; empty string at line end + (not (= (char-after (match-end 8)) ?\"))) ; no " after empty string + (scala-syntax:put-syntax-table-property 6 '(7 . nil)) + (scala-syntax:put-syntax-table-property 8 '(7 . nil))) + (t ;; backtrack and continue to next while loop + (goto-char (match-beginning 0)) + (throw 'break nil))))) + ;; match any start of multi-line literals that are not yet balanced + (when (re-search-forward scala-syntax:multiLineStringLiteral-start-re end t) + (scala-syntax:put-syntax-table-property 1 '(15 . nil))))))) + +(defun scala-syntax:propertize-underscore-and-idrest (start end) + "Mark all underscores (_) as punctuation (syntax 1) or upper +case letter (syntax 2). Also mark opchars in idrest as symbol +constituents (syntax 3)" + (save-excursion + (goto-char start) + (while (re-search-forward "_" end t) + (let ((match-beg (match-beginning 0)) + (match-end (match-end 0))) + (put-text-property + match-beg match-end 'syntax-table + (if (= match-beg (line-beginning-position)) + (if (looking-at scala-syntax:after-reserved-symbol-underscore-re) + '(1 . nil) ; punctuation + '(2 . nil)) ; word syntax + (save-excursion + (goto-char (1- match-beg)) + (if (looking-at scala-syntax:reserved-symbol-underscore-re) + '(1 . nil) ; punctuation + ;; check for opchars that should be marked as symbol constituents (3) + (goto-char match-end) + (when (looking-at scala-syntax:op-re) + (scala-syntax:put-syntax-table-property 0 '(3 . nil))) + '(2 . nil))))))))) ;; word syntax (2) for the '_' + +(defun scala-syntax:propertize (start end) + "See syntax-propertize-function" + (scala-syntax:propertize-characterLiterals start end) + (scala-syntax:propertize-stringLiterals start end) + (scala-syntax:propertize-underscore-and-idrest start end)) + diff --git a/scala-mode.el b/scala-mode.el new file mode 100644 index 0000000..5038b7b --- /dev/null +++ b/scala-mode.el @@ -0,0 +1,97 @@ +;;; scala-mode.el - Major mode for editing scala +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +;;; Based on Scala Language Specification (SLS) Version 2.9 + +(provide 'scala-mode) + +(require 'scala-mode-constants) +(require 'scala-mode-syntax) +(require 'scala-mode-fontlock) +(require 'scala-mode-indent) +(require 'scala-mode-map) + +;; Tested only for emacs 23 +(unless (<= 23 emacs-major-version) + (error + (format "The Scala mode has been tested only on Emacs version 23.x (and not your Emacs version %s.%s)" + emacs-major-version emacs-minor-version))) + +;; Attach .scala files to the scala-mode +(add-to-list 'auto-mode-alist '("\\.scala\\'" . scala-mode)) +(modify-coding-system-alist 'file "\\.scala\\'" 'utf-8) + + +(defmacro scala-mode:make-local-variables (&rest quoted-names) + (cons 'progn (mapcar #'(lambda (quoted-name) `(make-local-variable ,quoted-name)) quoted-names))) + +;; (defun scala-mode () +;; "Major mode for editing scala code. + +;; When started, runs `scala-mode-hook'. + +;; \\{scala-mode-map}" +;; (interactive) +;; (kill-all-local-variables) +;; (set-syntax-table scala-mode-syntax-table) + +;; (scala-mode:make-local-variables +;; 'require-final-newline +;; 'comment-start +;; 'comment-end +;; 'comment-start-line +;; 'comment-column +;; 'comment-multi-line) + +;;;###autoload +(define-derived-mode scala-mode prog-mode "Scala" + "Major mode for editing scala code. + +When started, runs `scala-mode-hook'. + +\\{scala-mode-map}" + :syntax-table scala-syntax:syntax-table +; :group +; :abbrev + + (scala-mode:make-local-variables + 'syntax-propertize-function + 'font-lock-defaults + 'comment-start + 'comment-end + 'comment-start-skip + 'comment-column + 'comment-multi-line) + + (add-hook 'syntax-propertize-extend-region-functions + 'scala-syntax:propertize-extend-region) + (setq + syntax-propertize-function 'scala-syntax:propertize + parse-sexp-lookup-properties t + + ;; TODO: font-lock + font-lock-defaults '(scala-font-lock:keywords + nil) + + ;; TODO: paragraph-start, paragraphs-separate, paragraph-ignore-fill-prefix + ;; TODO: beginning-of-defun-function, end-of-defun-function + + ;; comments + comment-start "// " + comment-end "" + comment-start-skip "\\(//+\\|/\\*+\\)\\s *" + comment-column 0 + comment-multi-line t + ;; TODO: comment-indent-function + ) + (use-local-map scala-mode-map) + (turn-on-font-lock) +) + + + + + + +