5353 , regexp )
5454 (pushnew `(,', regex-name . ,(upcase (symbol-name ', name ))) elixir-syntax-class-names))))
5555
56- (elixir-smie-define-regexp-opt op " &&" " ||" " !" )
56+ ; (elixir-smie-define-regexp-opt op "&&" "||" "!")
5757 (elixir-smie-define-regexp dot " \\ ." )
5858 (elixir-smie-define-regexp comma " ," )
5959 (elixir-smie-define-regexp -> " ->" )
6565 '(do else catch after rescue -> OP)
6666 " Keywords in which newlines cause confusion for the parser." )
6767
68- (defun elixir-skip-comment-backward ()
69- " Skip backwards over all whitespace and comments.
68+ (defvar elixir-smie--operator-regexp
69+ (regexp-opt '(" <<<" " >>>" " ^^^" " ~~~" " &&&" " |||" " ===" " !==" " ==" " !=" " <="
70+ " >=" " <" " >" " &&" " ||" " <>" " ++" " --" " //"
71+ " />" " =~" " |>" " ->" )))
7072
71- Return non-nil if any line breaks were skipped."
72- (let ((start-line-no (line-number-at-pos (point ))))
73- (forward-comment (- (point )))
74- (/= start-line-no (line-number-at-pos (point )))))
75-
76- (defun elixir-skip-comment-forward ()
77- " Skip forward over any whitespace and comments.
78-
79- Return non-nil if any line breaks were skipped."
80- (let ((start-line-no (line-number-at-pos (point ))))
81- (forward-comment (buffer-size ))
82- (/= start-line-no (line-number-at-pos (point )))))
83-
84- (defun elixir-smie-next-token-no-lookaround (forwardp )
85- (block elixir-smie-next-token-no-lookaround
86- ; ; First, skip comments; but if any comments / newlines were
87- ; ; skipped, the upper level needs to check if they were significant:
88- (when (if forwardp
89- (elixir-skip-comment-forward)
90- (elixir-skip-comment-backward))
91- (return-from elixir-smie-next-token-no-lookaround " \n " ))
92- (let* ((found-token-class (find-if
93- (lambda (class-def )
94- (let ((regex (symbol-value (car class-def))))
95- (if forwardp
96- (looking-at regex)
97- (looking-back regex nil t ))))
98- elixir-syntax-class-names))
99- (maybe-token
100- (let ((current-char (if forwardp
101- (following-char )
102- (preceding-char ))))
103- (cond ((member current-char
104- '(?\n ?\; ))
105- (if forwardp
106- (forward-comment (point-max ))
107- (forward-comment (- (point ))))
108- (string current-char))
109- (found-token-class
110- (goto-char (if forwardp
111- (match-end 0 )
112- (match-beginning 0 )))
113- (if (string= " PARENS" (cdr found-token-class))
114- (buffer-substring-no-properties (match-beginning 0 ) (match-end 0 ))
115- (cdr found-token-class)))
116- ((when (= ?\" (char-syntax (if forwardp
117- (following-char )
118- (preceding-char ))))
119- (if forwardp
120- (forward-sexp )
121- (backward-sexp ))
122- " STRING" ))))))
123- (or maybe-token
124- (downcase
125- (buffer-substring-no-properties
126- (point )
127- (if forwardp
128- (progn (skip-syntax-forward " 'w_" )
129- (point ))
130- (progn (skip-syntax-backward " 'w_" )
131- (point )))))))))
132-
133- (defun elixir-smie-next-token (forwardp )
134- (block elixir-smie-next-token
135- (let ((current-token (elixir-smie-next-token-no-lookaround forwardp)))
136- (when (string= " \n " current-token)
137- ; ; This is a newline; if the previous token isn't an OP2, this
138- ; ; means the line end marks the end of a statement & we get to
139- ; ; scan forward until there's a non-newline token; otherwise,
140- ; ; make this line ending something that probably ends the
141- ; ; statement (but see below).
142- (if (save-excursion
143- (block nil
144- (let ((token (elixir-smie-next-token-no-lookaround nil )))
145- (while (and (not (= (point ) (point-min )))
146- (string= " \n " token))
147- (setq token (elixir-smie-next-token-no-lookaround nil )))
148- (when (member (intern token) elixir-smie-block-intro-keywords)
149- (return t )))))
150- ; ; it's a continuation line, return the next token after the newline:
151- (return-from elixir-smie-next-token (elixir-smie-next-token forwardp))
152- (setq current-token " ;" )))
73+ (defun elixir-smie--at-dot-call ()
74+ (and (eq ?w (char-syntax (following-char )))
75+ (eq (char-before ) ?. )
76+ (not (eq (char-before (1- (point ))) ?. ))))
15377
154- ; ; When reading match statements (the ones with expr -> statements),
155- ; ; we need to drop non-; delimiters so the parser knows when a
156- ; ; match statement ends and another begins, so scan around point to
157- ; ; see if there are any -> within the current block's scope.
158-
159- ; ; If the current token is a ";", scan forward to see if the current
160- ; ; potential statement contains a "->". If so, scan back to find a
161- ; ; "do". If there is a -> there, emit a match-statement-delimiter
162- ; ; instead of the ";".
163- (if (and (string= " ;" current-token)
164- ; ; Scan ahead:
165- (let ((level 0 )
166- token)
167- (save-excursion
168- (block nil
169- (while
170- (and
171- ; ; Cursor is not at the end of the buffer...
172- (not (= (point ) (point-max )))
173- ; ; ...and the current token is not an empty string...
174- (not (string= " " token))
175- ; ; ...nor a newline nor a semicolon.
176- (not (or (string= " \n " token) (string= " ;" token))))
177- (setq token (elixir-smie-next-token-no-lookaround t ))
178- ; ; If we're at the top level and the token is "->",
179- ; ; return t
180- (cond ((and (= level 0 ) (string= " ->" token))
181- (return t ))
182- ; ; If token is "do" or "fn", increment level
183- ((find token '(" do" " fn" ) :test 'string= )
184- (incf level))
185- ; ; If token is "end", decrement level
186- ((string= token " end" )
187- (decf level)))))))
188- ; ; Scan behind:
189- (let (token)
190- (save-excursion
191- (block nil
192- (while
193- (and
194- ; ; Cursor is not at the beginning of buffer...
195- (not (= (point ) (point-min )))
196- ; ; ...and token is neither empty string, nor "do"/"fn"
197- (not (string= " " token))
198- (not (string= " do" token))
199- (not (string= " fn" token)))
200- (setq token (elixir-smie-next-token-no-lookaround nil ))
201- (when (string= " ->" token)
202- (return t )))
203- (when (string= token " do" ) t )))))
204- " MATCH-STATEMENT-DELIMITER"
205- current-token))))
78+ (defun elixir-smie--implicit-semi-p ()
79+ (not (or (memq (char-before ) '(?\{ ?\[ ?\, ))
80+ (looking-back elixir-smie--operator-regexp (- (point ) 3 ) t ))))
20681
20782(defun elixir-smie-forward-token ()
208- (elixir-smie-next-token t ))
83+ (cond
84+ ((and (looking-at " \n " ) (elixir-smie--implicit-semi-p))
85+ (if (eolp ) (forward-char 1 ) (forward-comment 1 ))
86+ " ;" )
87+ ((looking-at elixir-smie--operator-regexp)
88+ (goto-char (match-end 0 ))
89+ " OP" )
90+ (t (smie-default-forward-token ))))
20991
21092(defun elixir-smie-backward-token ()
211- (elixir-smie-next-token nil ))
93+ (let ((pos (point )))
94+ (forward-comment (- (point )))
95+ (cond
96+ ((and (> pos (line-end-position ))
97+ (elixir-smie--implicit-semi-p))
98+ " ;" )
99+ ((looking-back elixir-smie--operator-regexp (- (point ) 3 ) t )
100+ (goto-char (match-beginning 0 ))
101+ " OP" )
102+ (t (smie-default-backward-token )))))
212103
213104(defconst elixir-smie-grammar
214105 (smie-prec2->grammar
215- (smie-bnf->prec2
216- '((id)
217- (statements (statement)
218- (statement " ;" statements))
219- (statement (" def" non-block-expr " do" statements " end" )
220- (non-block-expr " fn" match-statement " end" )
221- (non-block-expr " do" statements " end" )
222- (" if" non-block-expr " do" statements " else" statements " end" )
223- (" if" non-block-expr " do" statements " end" )
224- (" if" non-block-expr " COMMA" " do:" non-block-expr)
225- (" if" non-block-expr " COMMA"
226- " do:" non-block-expr " COMMA"
227- " else:" non-block-expr)
228- (" try" " do" statements " after" statements " end" )
229- (" try" " do" statements " catch" match-statements " end" )
230- (" try" " do" statements " end" )
231- (" case" non-block-expr " do" match-statements " end" ))
232- (non-block-expr (non-block-expr " OP" non-block-expr)
233- (non-block-expr " COMMA" non-block-expr)
234- (" (" statements " )" )
235- (" {" statements " }" )
236- (" [" statements " ]" )
237- (" STRING" ))
238- (match-statements (match-statement " MATCH-STATEMENT-DELIMITER" match-statements)
239- (match-statement))
240- (match-statement (non-block-expr " ->" statements)))
241- '((assoc " if" " do:" " else:" )
242- (assoc " COMMA" )
243- (left " OP" )))))
106+ (smie-merge-prec2s
107+ (smie-bnf->prec2
108+ '((id)
109+ (statements (statement)
110+ (statement " ;" statements))
111+ (statement (" def" non-block-expr " do" statements " end" )
112+ (non-block-expr " fn" match-statement " end" )
113+ (non-block-expr " do" statements " end" )
114+ (" if" non-block-expr " do" statements " else" statements " end" )
115+ (" if" non-block-expr " do" statements " end" )
116+ (" if" non-block-expr " COMMA" " do:" non-block-expr)
117+ (" if" non-block-expr " COMMA"
118+ " do:" non-block-expr " COMMA"
119+ " else:" non-block-expr)
120+ (" try" " do" statements " after" statements " end" )
121+ (" try" " do" statements " catch" match-statements " end" )
122+ (" try" " do" statements " end" )
123+ (" case" non-block-expr " do" match-statements " end" ))
124+ (non-block-expr (non-block-expr " OP" non-block-expr)
125+ (non-block-expr " COMMA" non-block-expr)
126+ (" (" non-block-expr " )" )
127+ (" {" non-block-expr " }" )
128+ (" [" non-block-expr " ]" )
129+ (" STRING" ))
130+ (match-statements (match-statement " MATCH-STATEMENT-DELIMITER" match-statements)
131+ (match-statement))
132+ (match-statement (non-block-expr " ->" statements)))
133+ '((assoc " if" " do:" " else:" )
134+ (assoc " COMMA" )
135+ (left " OP" )))
136+
137+ (smie-precs->prec2
138+ '((left " ||" )
139+ (left " &&" )
140+ (nonassoc " =~" " ===" " !==" " ==" " !=" " <=" " >=" " <" " >" )
141+ (left " +" " -" " <<<" " >>>" " ^^^" " ~~~" " &&&" " |||" )
142+ (left " *" " /" ))))))
244143
245144(defvar elixir-smie-indent-basic 2 )
246145
@@ -265,26 +164,25 @@ Return non-nil if any line breaks were skipped."
265164 0
266165 elixir-smie-indent-basic))
267166 (`(:after . " OP" )
268- (unless (smie-rule-sibling-p )
269- elixir-smie-indent-basic))
167+ (cond
168+ ((smie-rule-sibling-p ) nil )
169+ ((smie-rule-hanging-p ) (smie-rule-parent elixir-smie-indent-basic))
170+ (t elixir-smie-indent-basic)))
270171 (`(:before . " def" ) elixir-smie-indent-basic)
271172 ; ; If the parent token of `->' is `fn' , then we want to align to the
272173 ; ; parent, and offset by `elixir-smie-indent-basic' . Otherwise, indent
273174 ; ; normally. This helps us work with/indent anonymous function blocks
274175 ; ; correctly.
275- (`(:after . " ->" )
276- (when (smie-rule-hanging-p )
277- (if (smie-rule-parent-p " fn" )
278- (smie-rule-parent elixir-smie-indent-basic)
279- elixir-smie-indent-basic)))
280- (`(:after . " do" )
281- elixir-smie-indent-basic)
282176 (`(:list-intro . ,(or `" do" `" ;" )) t )
177+ (`(:before . " ;" )
178+ (cond
179+ ((smie-rule-parent-p " after" " catch" " def" " defmodule" " defp" " do" " else"
180+ " fn" " if" " rescue" " try" " unless" )
181+ (smie-rule-parent elixir-smie-indent-basic))))
283182 (`(:after . " ;" )
284183 (if (smie-rule-parent-p " if" )
285184 (smie-rule-parent 0 )))))
286185
287-
288186(define-minor-mode elixir-smie-mode
289187 " SMIE-based indentation and syntax for Elixir"
290188 nil nil nil nil
0 commit comments