diff --git a/elixir-mode.el b/elixir-mode.el index beb73c48..a53d2273 100644 --- a/elixir-mode.el +++ b/elixir-mode.el @@ -361,6 +361,9 @@ ;; records and modules at point of definition: '("^\\s *def\\(module\\|record\\|protocol\\|impl\\)\\s +\\([^( \t\n,]+\\)" 2 font-lock-type-face) + ;; use/require/import: + '("^\\s *\\(use\\|require\\|import\\)\\s \\([^\n]+\\)" 2 font-lock-type-face) + ;; methods: `(,(concat "^\\s *\\<" (regexp-opt elixir-mode-define-names t) "\\>\\s +\\([^( \t\n]+\\)") 2 font-lock-function-name-face) diff --git a/elixir-smie.el b/elixir-smie.el index 64d3e859..3d06b89f 100644 --- a/elixir-smie.el +++ b/elixir-smie.el @@ -198,24 +198,41 @@ Return non-nil if any line breaks were skipped." token) (save-excursion (block nil - (while (and (not (= (point) (point-max))) (not (string= "" token)) (not (or (string= "\n" token) (string= ";" token)))) + (while + (and + ;; Cursor is not at the end of the buffer... + (not (= (point) (point-max))) + ;; ...and the current token is not an empty string... + (not (string= "" token)) + ;; ...nor a newline nor a semicolon. + (not (or (string= "\n" token) (string= ";" token)))) (setq token (elixir-smie-next-token-no-lookaround t nil)) + ;; If we're at the top level and the token is "->", + ;; return t (cond ((and (= level 0) (string= "->" token)) (return t)) + ;; If token is "do" or "fn", increment level ((find token '("do" "fn") :test 'string=) (incf level)) + ;; If token is "end", decrement level ((string= token "end") (decf level))))))) ;; Scan behind: (let (token) (save-excursion (block nil - (while (and (not (= (point) (point-min))) (not (string= "" token)) (not (string= "do" token)) (not (string= "fn" token))) + (while + (and + ;; Cursor is not at the beginning of buffer... + (not (= (point) (point-min))) + ;; ...and token is neither empty string, nor "do"/"fn" + (not (string= "" token)) + (not (string= "do" token)) + (not (string= "fn" token))) (setq token (elixir-smie-next-token-no-lookaround nil nil)) (when (string= "->" token) (return t))) - (when (or (string= token "do")) - t))))) + (when (string= token "do") t))))) "MATCH-STATEMENT-DELIMITER" current-token)))) @@ -241,7 +258,7 @@ Return non-nil if any line breaks were skipped." ("try" "do" statements "catch" match-statements "end") ("try" "do" statements "end") ("case" non-block-expr "do" match-statements "end") - ("function" "do" match-statements "end") + ("def" non-block-expr "do" statements "end") (non-block-expr "do" statements "end") (expr) ) @@ -258,7 +275,13 @@ Return non-nil if any line breaks were skipped." (match-statement)) (match-statement (non-block-expr "->" statements))) - '((assoc "DOT") (assoc "if") (assoc "do:") (assoc "else:") (assoc "COMMA") (assoc "OP") (assoc "->" ";"))))) + '((assoc "DOT") + (assoc "if") + (assoc "do:") + (assoc "else:") + (assoc "COMMA") + (assoc "OP") + (assoc "->" ";"))))) (defvar elixir-smie-indent-basic 2) @@ -281,7 +304,8 @@ Return non-nil if any line breaks were skipped." (`(:after . "OP") (unless (smie-rule-sibling-p) elixir-smie-indent-basic)) - (`(:before. "OP") + (`(:before . "def") elixir-smie-indent-basic) + (`(:before . "OP") ;; FIXME: Issue #5: This should prevent comments on lines before ;; continuation lines from causing indentation messed-upness, but ;; for some reason SMIE doesn't look this far when there's a @@ -292,17 +316,17 @@ Return non-nil if any line breaks were skipped." elixir-smie-indent-basic)) (`(,_ . ,(or `"COMMA")) (smie-rule-separator kind)) (`(:after . "=") elixir-smie-indent-basic) + (`(:after . "end") 0) (`(:after . ,(or `"do")) elixir-smie-indent-basic) - (`(:list-intro . ,(or `"do")) - t))) + (`(:list-intro . ,(or `"do")) t))) (define-minor-mode elixir-smie-mode "SMIE-based indentation and syntax for Elixir" nil nil nil nil (set (make-local-variable 'comment-start) "# ") (set (make-local-variable 'comment-end) "") - (smie-setup elixir-smie-grammar 'elixir-smie-rules ; 'verbose-elixir-smie-rules + (smie-setup elixir-smie-grammar 'elixir-smie-rules :forward-token 'elixir-smie-forward-token :backward-token 'elixir-smie-backward-token)) diff --git a/test/elixir-mode-indentation-tests.el b/test/elixir-mode-indentation-tests.el index ef5c75d5..946a0410 100644 --- a/test/elixir-mode-indentation-tests.el +++ b/test/elixir-mode-indentation-tests.el @@ -11,6 +11,42 @@ (insert indented) (should (equal indented ,expected-output))))))) +;; Expected test failures indicates that the code tested by that test case is +;; indeed broken. My intention is that while working on a specific problem, +;; the failure expectation will be removed so that we know when the test case +;; passes. +(elixir-def-indentation-test indents-use-dot-module-newline () + "defmodule Foo do +use GenServer.Behaviour + +def foobar do +if true, do: IO.puts \"yay\" +end +end" + "defmodule Foo do + use GenServer.Behaviour + + def foobar do + if true, do: IO.puts \"yay\" + end +end") + +(elixir-def-indentation-test indents-use-dot-module () + " +defmodule Foo do +use GenServer.Behaviour +def foobar do +if true, do: IO.puts \"yay\" +end +end" + " +defmodule Foo do + use GenServer.Behaviour + def foobar do + if true, do: IO.puts \"yay\" + end +end") + (elixir-def-indentation-test indents-do-blocks () " defmodule Foo do @@ -27,7 +63,7 @@ defmodule Foo do end end") -(elixir-def-indentation-test indents-do-blocks-after-linebreak () +(elixir-def-indentation-test indents-do-blocks-after-linebreak-two () " defmodule FooBar do def foo do @@ -53,7 +89,44 @@ defmodule FooBar do end end") -(elixir-def-indentation-test indents-after-empty-line () +(elixir-def-indentation-test indents-do-blocks-after-linebreak-three () + " +defmodule FooBar do +def foo do +if true, do: IO.puts \"yay\" +20 +end + +def bar do +if true, do: IO.puts \"yay\" +20 +end + +def baz do +if true, do: IO.puts \"yay\" +20 +end +end" + " +defmodule FooBar do + def foo do + if true, do: IO.puts \"yay\" + 20 + end + + def bar do + if true, do: IO.puts \"yay\" + 20 + end + + def baz do + if true, do: IO.puts \"yay\" + 20 + end +end") + +(elixir-def-indentation-test indents-after-empty-line + (:expected-result :failed) ; #18 " a = 2 @@ -107,7 +180,7 @@ has_something(x) && ") (elixir-def-indentation-test indents-continuation-lines-with-comments/1 - (:expected-result :failed) + () " has_something(x) && # foo has_something(y) || @@ -134,13 +207,21 @@ has_something(x) && (elixir-def-indentation-test indents-last-commented-line (:expected-result :failed) ; #27 " -defmodule Bar do -# ohai +defmodule Foo + def bar do + 2 + end + + # last line end " " -defmodule Bar do - # ohai + defmodule Foo + def bar do + 2 + end + +# last line end ")