diff --git a/raku-indent.el b/raku-indent.el index 517fcea..6c1aa73 100644 --- a/raku-indent.el +++ b/raku-indent.el @@ -11,7 +11,8 @@ (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." @@ -19,48 +20,90 @@ :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) diff --git a/test/test-smie.p6 b/test/test-smie.p6 index f035ebe..5fea4b7 100644 --- a/test/test-smie.p6 +++ b/test/test-smie.p6 @@ -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;