Skip to content

Commit

Permalink
Merge pull request #48 from RomanHargrave/fix-smie-hanging-indent
Browse files Browse the repository at this point in the history
Fix hanging assignment indentation
  • Loading branch information
matiaslina authored Apr 12, 2021
2 parents 02bf289 + 73c35c0 commit 3f33ca3
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 26 deletions.
95 changes: 69 additions & 26 deletions raku-indent.el
Original file line number Diff line number Diff line change
Expand Up @@ -11,56 +11,99 @@

(defconst raku-smie-grammar
(smie-prec2->grammar
(smie-precs->prec2 '((assoc ";") (assoc ",") (left ":")))))
(smie-precs->prec2
'((assoc ";") (assoc "=") (assoc ",") (left ":")))))

(defcustom raku-indent-offset 4
"Basic size of one indentation step."
:type 'integer
:group 'raku)

(defun raku-smie--not-interpolation-p ()
(save-excursion
(forward-char -1)
(or (zerop (skip-chars-backward "-[:alnum:]"))
(not (looking-back "#{\\$" (- (point) 3))))))
(save-excursion ;; Prepare for excursion
(forward-char -1) ;; Retreat one char

(or
;; Backtrack til we hit something that _isn't_ alnum
;; If we did not backtrack, we're not in interpolation
(zerop (skip-chars-backward "-[:alnum:]"))

;; If we did backtrack, see if #{\$ (EOL) occurs three
;; or more characters prior to point (??????????)
;; if this does NOT match, we are not in an interpolation
(not (looking-back "#{\\$" (- (point) 3))))))

(defun raku-smie--forward-token ()
(cond
((and (eq (char-before) ?\})
(raku-smie--not-interpolation-p)
;; FIXME: If the next char is not whitespace, what should we do?
(or (memq (char-after) '(?\s ?\t ?\n))
(looking-at comment-start-skip)))
(if (memq (char-after) '(?\s ?\t ?\n))
(forward-char 1) (forward-comment 1))
";")
((progn (forward-comment (point-max))
(looking-at "[;,:]"))
(forward-char 1) (match-string 0))
(t (smie-default-forward-token))))
;; Return `;` to fudge end-of-block indentation (I think), as ; is optional after a block
((and (eq (char-before) ?\}) ;; Character immediately prior to point is `}`
(raku-smie--not-interpolation-p) ;; And, not in an interpolation
;; FIXME: If the next char is not whitespace, what should we do?
(or (memq (char-after) '(?\s ?\t ?\n)) ;; And, point is followed by \s, \t, or \n
(looking-at comment-start-skip))) ;; or point is looking-at /#+ */

(if (memq (char-after) '(?\s ?\t ?\n)) ;; If the above is true, and point is followed by /[\s\t\n]/
(forward-char 1) (forward-comment 1)) ;; Then, advance by one character, and one whole comment
";")

((eq (char-after) ?\=) ;; Spit out '=' to kick off proper indentation for hanging assignment
(forward-char 1)
"=")

((progn (forward-comment (point-max)) ;; Read past ALL comments
(looking-at "[;,:]")) ;; Are we looking at ; , or :

(forward-char 1) ;; If so, advance one character
(match-string 0)) ;; And then return whatever looking-at found (?)

(t (smie-default-forward-token)))) ;; If none of the above matched, defer to SMIE default search

(defun raku-smie--backward-token ()
(let ((pos (point)))
(forward-comment (- (point)))
(forward-comment (- (point))) ;; Retreate past ALL comments up to point
(cond
;; FIXME: If the next char is not whitespace, what should we do?
((and (eq (char-before) ?\}) (raku-smie--not-interpolation-p)
(> pos (point))) ";")
((memq (char-before) '(?\; ?\, ?\:))
(forward-char -1) (string (char-after)))
(t (smie-default-backward-token)))))
;; Cond #1 - Same end-of-block hack, I think
((and (eq (char-before) ?\}) ;; Point is preceded immediately by `}`
(raku-smie--not-interpolation-p) ;; And, not in an interpolation
(> pos (point))) ;; And, point has moved backward

";") ;; If so, yield ';'

((eq (char-before) ?\=)
(forward-char -1)
"=")

;; Cond #2 - Get whatever precedes [,:;]
((memq (char-before) '(?\; ?\, ?\:)) ;; Point is preceded immediately by `;`, `,`, or `:`
(forward-char -1) ;; Retreat one char
(string (char-after))) ;; Return char after point (the char we just retreated past)

(t (smie-default-backward-token))))) ;; If none of the above matched, defer to SMIE default search

(defun raku-smie-rules (kind token)
(pcase (cons kind token)
;; Basic indent offset
(`(:elem . basic) raku-indent-offset)

;; Indent offset for function args
(`(:elem . arg) 0)

(`(:list-intro . ,(or `";" `"")) t) ;"" stands for BOB (bug#15467).

;; Make sure that hanging assignment gets indented
(`(:before . "=")
(if (smie-rule-hanging-p)
(smie-rule-parent raku-indent-offset)))

(`(:before . "{")
(when (smie-rule-hanging-p)
(smie-backward-sexp ";")
(when (smie-rule-hanging-p) ; is `{` the last thing on this line?
(smie-backward-sexp ";") ; y tho
(smie-indent-virtual)))

(`(:before . ,(or "{" "("))
(if (smie-rule-hanging-p) (smie-rule-parent 0)))))
(if (smie-rule-hanging-p)
(smie-rule-parent 0)))))

(provide 'raku-indent)

Expand Down
6 changes: 6 additions & 0 deletions test/test-smie.p6
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ qq:to/HERE/;
by emacs. (Except maybe to have left overhanging lines
adjusted.)
HERE

my %var =
A => 1,
:b<2>,
:3c,
D => 4;

0 comments on commit 3f33ca3

Please sign in to comment.