[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master d16cc52: * sm-c-mode.el: Auto-align backslashes. Improve i
From: |
Stefan Monnier |
Subject: |
[elpa] master d16cc52: * sm-c-mode.el: Auto-align backslashes. Improve indent rules |
Date: |
Tue, 24 Nov 2015 13:29:34 +0000 |
branch: master
commit d16cc527fbdab0e42642db827f12984be916dba2
Author: Stefan Monnier <address@hidden>
Commit: Stefan Monnier <address@hidden>
* sm-c-mode.el: Auto-align backslashes. Improve indent rules
* sm-c-mode/sm-c-mode.el: Auto-align backslashes. Improve indent rules.
(sm-c--cpp-indent-comment-inside): New function.
(sm-c--cpp-smie-indent-functions): Use it.
(sm-c--cpp-smie-indent): Handle indentation within comments-within-cpp.
(sm-c-smie-grammar): Manually tweak precedence of ":label".
(sm-c-smie-operator-regexp): Parentheses aren't operators.
(sm-c--boi): Add `inner' arg.
(sm-c--boe): Complete the code.
(sm-c-smie--*-token): Refine.
(sm-c-smie-rules): Various tweaks based on tests on src/eval.c and
src/xdisp.c.
(sm-c--bs-changed): New var.
(sm-c--bs-after-change, sm-c--bs-realign, sm-c--bs-realign-1):
New functions.
(sm-c-mode): Use them.
* sm-c-mode/sm-c-mode-test.c: Add a few more cases.
---
packages/sm-c-mode/sm-c-mode-test.c | 47 +++++-
packages/sm-c-mode/sm-c-mode.el | 307 +++++++++++++++++++++++++----------
2 files changed, 259 insertions(+), 95 deletions(-)
diff --git a/packages/sm-c-mode/sm-c-mode-test.c
b/packages/sm-c-mode/sm-c-mode-test.c
index 3a1f1b6..94cf476 100644
--- a/packages/sm-c-mode/sm-c-mode-test.c
+++ b/packages/sm-c-mode/sm-c-mode-test.c
@@ -1,12 +1,29 @@
/* -*- sm-c -*- */
-#define toto /* bla
- bla */ \
- if (a) { \
- f \
- }
+#define toto(arg) /* bla
+ bla */ \
+ if (a) { /* toto
+ * titi
+ */ \
+ fs((arg) + 2); \
+ }
+
+#define test(arg) \
+ (hello + arg)
+
+struct foo;
+
+#define titi(arg) { \
+ if (a) { \
+ f(arg + 1) \
+ } \
+ }
DEFUN ()
+ ()
+{
+ return Qnil;
+}
int main (void)
{
@@ -24,10 +41,22 @@ int main (void)
printf ("wow\n");
else
if (c)
- printf ("weee\n");
+ printf
+ ("weee\n");
else
printf ("wop\n");
-
+
+ if (a)
+ if (b) {
+ c;
+ }
+
+ *a = b;
+
+ if (pITORIG != pITCOPY)
+ *(pITORIG)
+ = *(pITCOPY);
+
switch (a)
{
case 1:
@@ -42,8 +71,8 @@ int main (void)
}
}
-static struct myownspecialstruct
- *testfunction
+static struct myownspecialstruct *
+testfunction
(args)
{
return NULL;
diff --git a/packages/sm-c-mode/sm-c-mode.el b/packages/sm-c-mode/sm-c-mode.el
index 3771169..7616616 100644
--- a/packages/sm-c-mode/sm-c-mode.el
+++ b/packages/sm-c-mode/sm-c-mode.el
@@ -163,8 +163,19 @@ Typically 2 for GNU style and `tab-width' for Linux style."
(defconst sm-c--cpp-smie-indent-functions
;; FIXME: Don't just align line after #define with the "d"!
- (remq #'smie-indent-comment-inside
- (default-value 'smie-indent-functions)))
+ (mapcar
+ (lambda (f)
+ (cond
+ ((eq f #'smie-indent-comment-inside) #'sm-c--cpp-indent-comment-inside)
+ ;; ((eq f #'smie-indent-exps) #'sm-c--cpp-indent-exps)
+ (t f)))
+ (default-value 'smie-indent-functions)))
+
+(defun sm-c--cpp-indent-comment-inside ()
+ (let ((ppss (syntax-ppss)))
+ (when (nth 4 ppss)
+ ;; Indicate where's the comment start.
+ `(noindent . ,(nth 8 ppss)))))
(defun sm-c--cpp-smie-indent ()
(let ((ppss (syntax-ppss)))
@@ -172,15 +183,36 @@ Typically 2 for GNU style and `tab-width' for Linux
style."
((sm-c--cpp-inside-p ppss)
(save-restriction
(narrow-to-region (nth 8 ppss) (point-max))
- (let ((smie-indent-functions sm-c--cpp-smie-indent-functions))
- (smie-indent-calculate))))
+ (let ((indent
+ (let ((smie-indent-functions sm-c--cpp-smie-indent-functions)
+ (syntax-ppss-cache nil)
+ (syntax-ppss-last nil)
+ (parse-sexp-lookup-properties nil))
+ (smie-indent-calculate))))
+ (if (not (eq 'noindent (car-safe indent)))
+ (if (integerp indent)
+ (max (funcall smie-rules-function :elem 'basic) indent)
+ indent)
+ ;; We can't just return `noindent' if we're inside a comment,
+ ;; because the indent.el code would then be similarly confused,
+ ;; thinking the `noindent' is because we're inside the cpp
+ ;; pseudo-comment, and would hence align the code with the content
+ ;; of the psuedo-comment rather than the nested real comment!
+ ;;
+ ;; FIXME: Copy&paste from indent--default-inside-comment.
+ ;; FIXME: This will always re-indent inside these comments, even
+ ;; during indent-region.
+ (save-excursion
+ (forward-line -1)
+ (skip-chars-forward " \t")
+ (when (< (1- (point)) (cdr indent) (line-end-position))
+ (goto-char (cdr indent))
+ (when (looking-at comment-start-skip)
+ (goto-char (match-end 0))))
+ (current-column))))))
+
((equal (syntax-after (point)) (string-to-syntax "< c")) 0)
- ((looking-at sm-c--cpp-regexp)
- (message "s-p-l=%S s-p-d=%S" syntax-ppss-last syntax-propertize--done)
- (when (get-buffer "*trace-output*")
- (with-current-buffer "*trace-output*"
- (message "%S" (buffer-string))))
- (debug)))))
+ )))
;;; Syntax table
@@ -239,36 +271,48 @@ Typically 2 for GNU style and `tab-width' for Linux
style."
(defconst sm-c-smie-grammar
;; `((:smie-closer-alist ("{" . "}")) ("{" (39) 0) ("}" 0 (40)) ("else" 27
26) ("," 38 38) ("do" (41) 22) ("while" (42) 23) ("for" (43) 24) (";" 11 11)
("if" (44) 25))
- (smie-prec2->grammar
- (smie-merge-prec2s
- (smie-bnf->prec2
- '((decls ("typedef" decl) ("extern" decl)
- (decls ";" decls))
- (decl)
- (id)
- (insts ("{" insts "}")
- (insts ";" insts)
- ("return" exp)
- ("goto" exp)
- (":label")
- ("case" subexp ": case")
- ("else" exp-if))
- (exp-if ("if" exp) ("do" exp) ("while" exp) ("switch" exp) ("for" exp)
- (exp))
- (exp ("(" exp ")") (exp "," exp) (subexp "?" exp ":" exp))
- (subexp (subexp "||" subexp))
- ;; Some of the precedence table deals with pre/postfixes, which
- ;; smie-precs->prec2 can't handle, so handle it here instead.
- (exp11 (exp12) (exp11 "/" exp11))
- (exp12 (exp13)) ;C++ only.
- (exp13 (exp14) ("++ prefix" exp13) ("-- prefix" exp13)
- ("!" exp13) ("~" exp13) ("&" exp13) ("* deref" exp13))
- (exp14 (id) (exp14 "++ postfix") (exp14 "-- postfix")
- (exp14 "->" id) (exp14 "." id)))
- '((assoc ";") (assoc ",") (nonassoc "?" ":"))
- sm-c-smie-precedence-table)
- (smie-precs->prec2 sm-c-smie-precedence-table)
- (smie-precs->prec2 '((nonassoc ";") (nonassoc ":"))))))
+ (let ((grm
+ (smie-prec2->grammar
+ (smie-merge-prec2s
+ (smie-bnf->prec2
+ '((decls ("typedef" decl) ("extern" decl)
+ (decls ";" decls))
+ (decl)
+ (id)
+ (insts ("{" insts "}")
+ (insts ";" insts)
+ ("return" exp)
+ ("goto" exp)
+ (":label")
+ ("case" subexp ": case")
+ ("else" exp-if))
+ (exp-if ("if" exp) ("do" exp) ("while" exp) ("switch" exp)
("for" exp)
+ (exp))
+ (exp ("(" exp ")") (exp "," exp) (subexp "?" exp ":" exp))
+ (subexp (subexp "||" subexp))
+ ;; Some of the precedence table deals with pre/postfixes, which
+ ;; smie-precs->prec2 can't handle, so handle it here instead.
+ (exp11 (exp12) (exp11 "/" exp11))
+ (exp12 (exp13)) ;C++ only.
+ (exp13 (exp14) ("++ prefix" exp13) ("-- prefix" exp13)
+ ("!" exp13) ("~" exp13) ("&" exp13) ("* deref" exp13))
+ (exp14 (id) (exp14 "++ postfix") (exp14 "-- postfix")
+ (exp14 "->" id) (exp14 "." id)))
+ '((assoc ";") (assoc ",") (nonassoc "?" ":"))
+ sm-c-smie-precedence-table)
+ (smie-precs->prec2 sm-c-smie-precedence-table)
+ (smie-precs->prec2 '((nonassoc ";") (nonassoc ":")))))))
+ ;; SMIE gives (":label" 261 262), but really this could just as well be
+ ;; (":label" nil nil) because labels don't have any argument to their left
+ ;; or right. They're like both openers and closers at the same time.
+ (mapcar (lambda (x)
+ (if (equal (car-safe x) ":label")
+ ;; Rather than (":label" (n1) (n2)) we use
+ ;; (":label" (n1) n2) because SMIE otherwise complains:
+ ;; cl--assertion-failed((numberp (funcall op-forw
toklevels)))
+ ;; in smie-next-sexp.
+ `(,(nth 0 x) (,(nth 1 x)) ,(nth 2 x)) x))
+ grm)))
;; (defun sm-c--:-discriminate ()
;; (save-excursion
@@ -282,7 +326,7 @@ Typically 2 for GNU style and `tab-width' for Linux style."
(defconst sm-c-smie-operator-regexp
(let ((ops '()))
(pcase-dolist (`(,token . ,_) sm-c-smie-grammar)
- (when (and (stringp token) (string-match "\\`[^ [:alnum:]]+" token))
+ (when (and (stringp token) (string-match "\\`[^ [:alnum:](){}]+" token))
(push (match-string 0 token) ops)))
(regexp-opt ops)))
@@ -385,7 +429,10 @@ Typically 2 for GNU style and `tab-width' for Linux style."
t))
(_ (goto-char start) nil)))))
-(defun sm-c--boi ()
+(defun sm-c--boi (&optional inner)
+ "Jump to the beginning-of-instruction.
+By default for things like nested ifs, it jumps to the outer if, but
+if INNER is non-nil, it stops at the innermost one."
(while
(let ((pos (point)))
(pcase (smie-backward-sexp)
@@ -401,6 +448,12 @@ Typically 2 for GNU style and `tab-width' for Linux style."
t
(goto-char pos) nil))
(`(,_ ,_ ,(or "(" "{" "[")) nil) ;Found it!
+ (`(,_ ,pos ,(and tok
+ (guard (when inner
+ (or (member tok sm-c-paren-block-keywords)
+ (equal tok "do"))))))
+ (goto-char pos) nil) ;Found it!
+ (`(t ,(pred (eq (point-min))) . ,_) nil)
(`(,_ ,pos . ,_) (goto-char pos) t)))))
;; (defun sm-c--if-tail-to-head ()
@@ -414,31 +467,32 @@ Typically 2 for GNU style and `tab-width' for Linux
style."
(defun sm-c--boe (tok)
(let ((start (point))
- (res (smie-backward-sexp tok))
- (min (point)))
- (while
- (and (member (nth 2 res) '("if" "while" "do" "for" "else"))
- (let ((skip (cdr (assoc (nth 2 res)
- '(("{" . 1)
- ("else" . 1)
- ("do" . 1)
- ("if" . 2)
- ("for" . 2)
- ("while" . 2))))))
- (let ((forward-sexp-function nil))
- (forward-sexp (1- skip)))
- (forward-comment (point-max))
- (if (< (point) start)
- (setq min (point))
- (goto-char min)
- nil))))))
+ (res (smie-backward-sexp tok)))
+ (when (member (nth 2 res) '("if" "while" "do" "for" "else"))
+ (when (member (nth 2 res) '("if" "for"))
+ (let ((forward-sexp-function nil))
+ (forward-sexp 1))
+ (forward-comment (point-max)))
+ (when (looking-at "{")
+ (let ((forward-sexp-function nil))
+ (forward-sexp 1))
+ (forward-comment (point-max)))
+ (if (> (point) start) (goto-char start)))))
(defun sm-c-smie--*-token ()
(save-excursion
(let ((pos (point)))
(pcase (car (smie-indent-backward-token))
- ((or ")" "]") "* mult") ;Multiplication.
- ((or "(" "[" "{") "* deref")
+ (")"
+ ;; Can be a multiplication (as in "(a+b)*c"), or a deref
+ ;; (as in "if (stop) *a = 0;")
+ (if (and (goto-char (nth 1 (syntax-ppss)))
+ (eq ?\( (char-after))
+ (member (smie-default-backward-token) '("if" "for")))
+ "* deref"
+ "* mult"))
+ ("]" "* mult") ;Multiplication.
+ ((or "(" "[" "{" "}") "* deref")
(`nil
(goto-char pos)
(pcase (smie-backward-sexp "* mult")
@@ -507,16 +561,23 @@ Typically 2 for GNU style and `tab-width' for Linux
style."
(cond
((smie-rule-prev-p "=") nil) ;Not a block of instructions!
((save-excursion
- (sm-c--boi) (sm-c--skip-labels (point-max))
- (let ((tok (save-excursion (sm-c-smie-forward-token))))
- (cond
- ((member tok '("enum" "struct" "typedef"))
- `(column . ,(+ (funcall smie-rules-function :elem 'basic)
- (smie-indent-virtual))))
- ((or (member tok sm-c-paren-block-keywords)
- (equal tok "do"))
- nil)
- (t `(column . ,(smie-indent-virtual)))))))
+ (let ((pos (point)))
+ (sm-c--boi 'inner) (sm-c--skip-labels (point-max))
+ (let ((tok (save-excursion (sm-c-smie-forward-token))))
+ (cond
+ ((member tok '("typedef")) ; "enum" "struct"
+ `(column . ,(+ (funcall smie-rules-function :elem 'basic)
+ (smie-indent-virtual))))
+ ((or (member tok sm-c-paren-block-keywords)
+ (equal tok "do"))
+ nil)
+ ((save-excursion
+ (goto-char pos)
+ (when (and (> (car (syntax-ppss)) 0)
+ (equal ")" (car (smie-indent-backward-token))))
+ (up-list -1)
+ `(column . ,(sm-c--smie-virtual)))))
+ (t `(column . ,(smie-indent-virtual))))))))
((smie-rule-hanging-p)
(cond
((smie-rule-prev-p "do" "else")
@@ -524,7 +585,7 @@ Typically 2 for GNU style and `tab-width' for Linux style."
((smie-rule-prev-p ")")
(smie-backward-sexp)
(smie-indent-backward-token))
- (t (sm-c--boi)))
+ (t (sm-c--boi 'inner)))
`(column . ,(sm-c--smie-virtual)))
(t
(let ((pos (point)))
@@ -542,23 +603,35 @@ Typically 2 for GNU style and `tab-width' for Linux
style."
(save-excursion
(let ((res (smie-backward-sexp)))
(pcase res
- (`nil `(column . ,(+ (funcall smie-rules-function :elem 'basic)
- (sm-c--smie-virtual))))
- (`(nil ,_ "(")
- (unless (save-excursion
- (member (sm-c-smie-backward-token)
- sm-c-paren-block-keywords))
- `(column . ,(sm-c--smie-virtual))))))))
+ (`nil
+ (if (looking-at "(")
+ ;; (unless (save-excursion
+ ;; (member (sm-c-smie-backward-token)
+ ;; sm-c-paren-block-keywords))
+ ;; `(column . ,(sm-c--smie-virtual)))
+ nil
+ `(column . ,(+ (funcall smie-rules-function :elem 'basic)
+ (sm-c--smie-virtual)))))))))
(`(:after . "else")
(save-excursion
(funcall smie-rules-function :elem 'basic)))
(`(:after . ")")
(save-excursion
- (forward-char 1) (backward-sexp 1)
- (let ((prev (sm-c-smie-backward-token)))
- (when (member prev sm-c-paren-block-keywords)
+ (let ((_ (progn (forward-char 1) (backward-sexp 1)))
+ (pos (point))
+ (prev (sm-c-smie-backward-token)))
+ (cond
+ ((member prev sm-c-paren-block-keywords)
`(column . ,(+ (funcall smie-rules-function :elem 'basic)
- (smie-indent-virtual)))))))
+ (smie-indent-virtual))))
+ ((and (looking-at "[[:alnum:]_]+(")
+ (save-excursion
+ (forward-line 0)
+ (and (bobp) (looking-at sm-c--cpp-regexp))))
+ ;; Will be bumped up presumably by the "max" in
+ ;; sm-c--cpp-smie-indent.
+ `(column . 0))
+ (t (goto-char pos) `(column . ,(sm-c--smie-virtual)))))))
(`(:after . "}")
(save-excursion
(forward-char 1) (backward-sexp 1)
@@ -594,6 +667,65 @@ Typically 2 for GNU style and `tab-width' for Linux style."
(goto-char pos)
(throw 'found `(column . ,(smie-indent-virtual))))))))))
+;;; Backslash alignment
+
+(defvar-local sm-c--bs-changed nil)
+
+(defun sm-c--bs-after-change (beg end _len)
+ (unless undo-in-progress
+ (if (null sm-c--bs-changed)
+ (setq sm-c--bs-changed (cons beg end))
+ (cl-callf (lambda (x) (min x beg)) (car sm-c--bs-changed))
+ (cl-callf (lambda (x) (max x end)) (cdr sm-c--bs-changed)))))
+
+(defun sm-c--bs-realign ()
+ (when sm-c--bs-changed
+ (sm-c--bs-realign-1 (car sm-c--bs-changed) (cdr sm-c--bs-changed))
+ (setq sm-c--bs-changed nil)))
+
+(defun sm-c--bs-realign-1 (from to)
+ (save-excursion
+ (goto-char from)
+ (end-of-line)
+ (unless (zerop (mod (skip-chars-backward "\\\\") 2))
+ (skip-chars-backward " \t")
+ (let ((col (current-column))
+ start end)
+ (while
+ (progn (setq start (point))
+ (end-of-line 0)
+ (and (< (point) start)
+ (not (zerop (mod (skip-chars-backward "\\\\") 2)))))
+ (skip-chars-backward " \t")
+ (setq col (max (current-column) col)))
+ (goto-char from)
+ (while
+ (progn (setq end (point))
+ (end-of-line 2)
+ (and (> (point) start)
+ (not (zerop (mod (skip-chars-backward "\\\\") 2)))))
+ (skip-chars-backward " \t")
+ (setq col (max (current-column) col)))
+ (goto-char to)
+ (beginning-of-line)
+ (unless (or (> (point) end) ;Don't realign if we changed outside!
+ (< end start)) ;A lone \
+
+ (setq col (1+ col)) ;Add a space before the backslashes.
+ (goto-char end)
+ (end-of-line)
+ (while (>= (point) start)
+ (cl-assert (eq (char-before) ?\\))
+ (forward-char -1)
+ (let ((curcol (current-column)))
+ (cond
+ ((> col curcol) (indent-to col))
+ ((< col curcol)
+ (move-to-column col t)
+ (delete-region (point)
+ (progn (skip-chars-forward " \t") (point))))))
+ (end-of-line 0)))))))
+
;;; Font-lock support
(defconst sm-c-font-lock-keywords
@@ -659,12 +791,15 @@ Typically 2 for GNU style and `tab-width' for Linux
style."
:forward-token #'sm-c-smie-forward-token)
;; FIXME: The stock SMIE forward-sexp-function is not good enough here, since
;; our grammar is much too poor. We should setup another function instead
- ;; (and ideally teach SMIE to use it).
+ ;; (or ideally teach SMIE to use it).
(kill-local-variable 'forward-sexp-function)
(add-hook 'smie-indent-functions #'sm-c--cpp-smie-indent nil t)
(add-function :after (local 'indent-line-function)
#'sm-c--cpp-indent-line)
- (setq-local smie--hanging-eolp-function #'sm-c-smie-hanging-eolp))
+ (setq-local smie--hanging-eolp-function #'sm-c-smie-hanging-eolp)
+ ;; Backslash auto-realign.
+ (add-hook 'after-change-functions #'sm-c--bs-after-change nil t)
+ (add-hook 'post-command-hook #'sm-c--bs-realign nil t))
(provide 'sm-c-mode)
;;; sm-c-mode.el ends here
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] master d16cc52: * sm-c-mode.el: Auto-align backslashes. Improve indent rules,
Stefan Monnier <=