;;; scala-mode-paragraph.el - Major mode for editing scala, paragraph
;;; detection and fill
;;; Copyright (c) 2012 Heikki Vesalainen For information on the License,
;;; see the LICENSE file
;;; Based on Scala Language Specification (SLS) Version 2.9
;;; Provides paragraph navigation and fill for scaladocs and
;;; multi-line strings.
(defconst scala-paragraph:paragraph-line-start-re
(concat "\\(?:\\s-*" ; whitespace
"\\(?://+\\|\\*\\|/\\*+" ; comment start
"\\||\\)?" ; multi-line margin |
"\\s-*\\)")) ; whitespace
(defconst scala-paragraph:scaladoc-list-start-re
(concat "\\(?:-" ; unordered liststs
"\\|[1IiAa]\\." ; ordered lists
"\\)\\s-*"))
(defconst scala-paragraph:fill-first-line-re
(concat "\\s-*\\(//+\\|\\*\\||\\)?\\s-*"
"\\(?:" scala-paragraph:scaladoc-list-start-re "\\)?"))
(defconst scala-paragraph:paragraph-start-re
(concat scala-paragraph:paragraph-line-start-re
"\\(?:$" ; empty line
"\\|==*[^=]+==*[ ]*$" ; headings
"\\|"
scala-paragraph:scaladoc-list-start-re
"\\|{{{" ; code block start
"\\|}}}" ; code block end
"\\|@[a-zA-Z]+\\>" ; annotations
"\\)"
"\\|\\(?:\\s-*\\*/\\)" ; end of comment
))
(defconst scala-paragraph:paragraph-separate-re
(concat scala-paragraph:paragraph-line-start-re
"\\(?:$\\)"
"\\|\\(?:\\s *\\*/\\)" ; end of comment
))
(defun scala-paragraph:fill-function ()
(let (fill)
(save-restriction
(save-excursion
(widen)
(beginning-of-line)
(cond ((looking-at "\\s-*/?\\*+\\s-*")
(setq fill (replace-regexp-in-string
"/\\*+"
(lambda (str) (if (= (length str) 3) " *" " *"))
(match-string-no-properties 0)))
(goto-char (match-end 0))
(when (looking-at scala-paragraph:scaladoc-list-start-re)
(setq fill
(concat fill (make-string (- (match-end 0)
(match-beginning 0)) ?\s)))))
((or (re-search-forward "\"\"\"|" (line-end-position) t)
(and (eq (nth 3 (syntax-ppss)) t)
(re-search-forward "^\\s-*|" (line-end-position) t)))
(setq fill (concat (make-string (- (current-column) 1) ?\s) "|"))
(setq fill (concat fill (make-string (skip-syntax-forward " ") ?\s)))
(when (looking-at scala-paragraph:scaladoc-list-start-re)
(setq fill
(concat fill (make-string (- (match-end 0)
(match-beginning 0)) ?\s))))))))
fill))
(defun scala-paragraph:fill-paragraph (&rest args)
;; move to inside multi-line comment or multi-line string, if outside
(when (looking-at "\\s-*\\(?:/\\**\\|\"\"\"\\)\\s-*")
(goto-char (match-end 0)))
(let ((state (syntax-ppss))
(fill-paragraph-function
;; Avoid infinite recursion, set fill-paragraph-function to
;; nil if it is 'scala-paragraph:fill-paragraph
(unless (eq fill-paragraph-function 'scala-paragraph:fill-paragraph)
fill-paragraph-function)))
(cond ((integerp (nth 4 state))
;; mask multi-line comments and fill
(save-restriction
(narrow-to-region (nth 8 state)
(save-excursion (goto-char (nth 8 state))
(if (forward-comment 1)
(point)
(point-max))))
(apply #'fill-paragraph args))
t)
((eq (nth 4 state) t)
;; line comment, let normal fill-function handle this
nil)
((eq (nth 3 state) t)
;; mask multi-line strings and fill.
(save-restriction
(narrow-to-region (nth 8 state)
(save-excursion (goto-char (nth 8 state))
(or (ignore-errors
(forward-sexp)
(point))
(point-max))))
(apply #'fill-paragraph args))
t)
;; TODO: fill lists
;; the rest should not be filled (code, etc)
(t t))))
(provide 'scala-mode-paragraph)