Skip to content

Commit 0eb9724

Browse files
committed
Merge pull request #48 from mattdeboard/fix-use-indentation
Add a SMIE rule function for "def". Fixes #38 and #41
2 parents 2132422 + 8e20513 commit 0eb9724

File tree

3 files changed

+125
-17
lines changed

3 files changed

+125
-17
lines changed

elixir-mode.el

+3
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@
361361
;; records and modules at point of definition:
362362
'("^\\s *def\\(module\\|record\\|protocol\\|impl\\)\\s +\\([^( \t\n,]+\\)" 2 font-lock-type-face)
363363

364+
;; use/require/import:
365+
'("^\\s *\\(use\\|require\\|import\\)\\s \\([^\n]+\\)" 2 font-lock-type-face)
366+
364367
;; methods:
365368
`(,(concat "^\\s *\\<" (regexp-opt elixir-mode-define-names t) "\\>\\s +\\([^( \t\n]+\\)") 2 font-lock-function-name-face)
366369

elixir-smie.el

+34-10
Original file line numberDiff line numberDiff line change
@@ -198,24 +198,41 @@ Return non-nil if any line breaks were skipped."
198198
token)
199199
(save-excursion
200200
(block nil
201-
(while (and (not (= (point) (point-max))) (not (string= "" token)) (not (or (string= "\n" token) (string= ";" token))))
201+
(while
202+
(and
203+
;; Cursor is not at the end of the buffer...
204+
(not (= (point) (point-max)))
205+
;; ...and the current token is not an empty string...
206+
(not (string= "" token))
207+
;; ...nor a newline nor a semicolon.
208+
(not (or (string= "\n" token) (string= ";" token))))
202209
(setq token (elixir-smie-next-token-no-lookaround t nil))
210+
;; If we're at the top level and the token is "->",
211+
;; return t
203212
(cond ((and (= level 0) (string= "->" token))
204213
(return t))
214+
;; If token is "do" or "fn", increment level
205215
((find token '("do" "fn") :test 'string=)
206216
(incf level))
217+
;; If token is "end", decrement level
207218
((string= token "end")
208219
(decf level)))))))
209220
;; Scan behind:
210221
(let (token)
211222
(save-excursion
212223
(block nil
213-
(while (and (not (= (point) (point-min))) (not (string= "" token)) (not (string= "do" token)) (not (string= "fn" token)))
224+
(while
225+
(and
226+
;; Cursor is not at the beginning of buffer...
227+
(not (= (point) (point-min)))
228+
;; ...and token is neither empty string, nor "do"/"fn"
229+
(not (string= "" token))
230+
(not (string= "do" token))
231+
(not (string= "fn" token)))
214232
(setq token (elixir-smie-next-token-no-lookaround nil nil))
215233
(when (string= "->" token)
216234
(return t)))
217-
(when (or (string= token "do"))
218-
t)))))
235+
(when (string= token "do") t)))))
219236
"MATCH-STATEMENT-DELIMITER"
220237
current-token))))
221238

@@ -241,7 +258,7 @@ Return non-nil if any line breaks were skipped."
241258
("try" "do" statements "catch" match-statements "end")
242259
("try" "do" statements "end")
243260
("case" non-block-expr "do" match-statements "end")
244-
("function" "do" match-statements "end")
261+
("def" non-block-expr "do" statements "end")
245262
(non-block-expr "do" statements "end")
246263
(expr)
247264
)
@@ -258,7 +275,13 @@ Return non-nil if any line breaks were skipped."
258275
(match-statement))
259276
(match-statement
260277
(non-block-expr "->" statements)))
261-
'((assoc "DOT") (assoc "if") (assoc "do:") (assoc "else:") (assoc "COMMA") (assoc "OP") (assoc "->" ";")))))
278+
'((assoc "DOT")
279+
(assoc "if")
280+
(assoc "do:")
281+
(assoc "else:")
282+
(assoc "COMMA")
283+
(assoc "OP")
284+
(assoc "->" ";")))))
262285

263286
(defvar elixir-smie-indent-basic 2)
264287

@@ -281,7 +304,8 @@ Return non-nil if any line breaks were skipped."
281304
(`(:after . "OP")
282305
(unless (smie-rule-sibling-p)
283306
elixir-smie-indent-basic))
284-
(`(:before. "OP")
307+
(`(:before . "def") elixir-smie-indent-basic)
308+
(`(:before . "OP")
285309
;; FIXME: Issue #5: This should prevent comments on lines before
286310
;; continuation lines from causing indentation messed-upness, but
287311
;; 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."
292316
elixir-smie-indent-basic))
293317
(`(,_ . ,(or `"COMMA")) (smie-rule-separator kind))
294318
(`(:after . "=") elixir-smie-indent-basic)
319+
(`(:after . "end") 0)
295320
(`(:after . ,(or `"do"))
296321
elixir-smie-indent-basic)
297-
(`(:list-intro . ,(or `"do"))
298-
t)))
322+
(`(:list-intro . ,(or `"do")) t)))
299323

300324
(define-minor-mode elixir-smie-mode
301325
"SMIE-based indentation and syntax for Elixir"
302326
nil nil nil nil
303327
(set (make-local-variable 'comment-start) "# ")
304328
(set (make-local-variable 'comment-end) "")
305-
(smie-setup elixir-smie-grammar 'elixir-smie-rules ; 'verbose-elixir-smie-rules
329+
(smie-setup elixir-smie-grammar 'elixir-smie-rules
306330
:forward-token 'elixir-smie-forward-token
307331
:backward-token 'elixir-smie-backward-token))
308332

test/elixir-mode-indentation-tests.el

+88-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,42 @@
1111
(insert indented)
1212
(should (equal indented ,expected-output)))))))
1313

14+
;; Expected test failures indicates that the code tested by that test case is
15+
;; indeed broken. My intention is that while working on a specific problem,
16+
;; the failure expectation will be removed so that we know when the test case
17+
;; passes.
18+
(elixir-def-indentation-test indents-use-dot-module-newline ()
19+
"defmodule Foo do
20+
use GenServer.Behaviour
21+
22+
def foobar do
23+
if true, do: IO.puts \"yay\"
24+
end
25+
end"
26+
"defmodule Foo do
27+
use GenServer.Behaviour
28+
29+
def foobar do
30+
if true, do: IO.puts \"yay\"
31+
end
32+
end")
33+
34+
(elixir-def-indentation-test indents-use-dot-module ()
35+
"
36+
defmodule Foo do
37+
use GenServer.Behaviour
38+
def foobar do
39+
if true, do: IO.puts \"yay\"
40+
end
41+
end"
42+
"
43+
defmodule Foo do
44+
use GenServer.Behaviour
45+
def foobar do
46+
if true, do: IO.puts \"yay\"
47+
end
48+
end")
49+
1450
(elixir-def-indentation-test indents-do-blocks ()
1551
"
1652
defmodule Foo do
@@ -27,7 +63,7 @@ defmodule Foo do
2763
end
2864
end")
2965

30-
(elixir-def-indentation-test indents-do-blocks-after-linebreak ()
66+
(elixir-def-indentation-test indents-do-blocks-after-linebreak-two ()
3167
"
3268
defmodule FooBar do
3369
def foo do
@@ -53,7 +89,44 @@ defmodule FooBar do
5389
end
5490
end")
5591

56-
(elixir-def-indentation-test indents-after-empty-line ()
92+
(elixir-def-indentation-test indents-do-blocks-after-linebreak-three ()
93+
"
94+
defmodule FooBar do
95+
def foo do
96+
if true, do: IO.puts \"yay\"
97+
20
98+
end
99+
100+
def bar do
101+
if true, do: IO.puts \"yay\"
102+
20
103+
end
104+
105+
def baz do
106+
if true, do: IO.puts \"yay\"
107+
20
108+
end
109+
end"
110+
"
111+
defmodule FooBar do
112+
def foo do
113+
if true, do: IO.puts \"yay\"
114+
20
115+
end
116+
117+
def bar do
118+
if true, do: IO.puts \"yay\"
119+
20
120+
end
121+
122+
def baz do
123+
if true, do: IO.puts \"yay\"
124+
20
125+
end
126+
end")
127+
128+
(elixir-def-indentation-test indents-after-empty-line
129+
(:expected-result :failed) ; #18
57130
"
58131
a = 2
59132
@@ -107,7 +180,7 @@ has_something(x) &&
107180
")
108181

109182
(elixir-def-indentation-test indents-continuation-lines-with-comments/1
110-
(:expected-result :failed)
183+
()
111184
"
112185
has_something(x) && # foo
113186
has_something(y) ||
@@ -134,13 +207,21 @@ has_something(x) &&
134207
(elixir-def-indentation-test indents-last-commented-line
135208
(:expected-result :failed) ; #27
136209
"
137-
defmodule Bar do
138-
# ohai
210+
defmodule Foo
211+
def bar do
212+
2
213+
end
214+
215+
# last line
139216
end
140217
"
141218
"
142-
defmodule Bar do
143-
# ohai
219+
defmodule Foo
220+
def bar do
221+
2
222+
end
223+
224+
# last line
144225
end
145226
")
146227

0 commit comments

Comments
 (0)