C ++模板和Emacs:自定义缩进

时间:2011-10-20 02:19:07

标签: c++ emacs

据我所知,在emacs中,没有办法自定义结束'>'的缩进级别C ++中模板列表的字符。目前我的emacs缩进方案执行此操作:

template <
    typename T1,
    typename T2,
    typename T3
    >
class X;

我想要的是这样的:

template <
    typename T1,
    typename T2,
    typename T3
>
class X;

将缩进变量template-args-cont设置为零将缩进'&gt;'正确的角色,但代价是取消模板参数列表的实际主体。

来自emacs大师的任何建议吗?

修改

我对以下黑客有所帮助:

(defun indent-templates (elem)
  (c-langelem-col elem t)
  (let ((current-line
         (buffer-substring-no-properties
          (point-at-bol) (point-at-eol))))
    (if (string-match-p "^\\s-*>" current-line)
        0
        '+)))

然后在我的自定义主题中将template-args-cont设置为indent-templates,ala:

(c-add-style "my-style"
             '("stroustrup"
                ;; ... Other stuff ...
                (template-args-cont . indent-templates))))

但它仍然很漂亮。它大部分时间都可以工作,但有时emacs会因为模板列表是一个arglist而感到困惑,然后就会出现欢闹。

3 个答案:

答案 0 :(得分:2)

我找到的最佳解决方案是编写自定义(并且相对简单)的缩进功能。

守则

(defun c++-template-args-cont (langelem)
"Control indentation of template parameters handling the special case of '>'.
Possible Values:
0   : The first non-ws character is '>'. Line it up under 'template'.
nil : Otherwise, return nil and run next lineup function."
  (save-excursion
    (beginning-of-line)
    (if (re-search-forward "^[\t ]*>" (line-end-position) t)
        0)))

(add-hook 'c++-mode-hook
          (lambda ()
            (c-set-offset 'template-args-cont
                          '(c++-template-args-cont c-lineup-template-args +))))

这可以处理我遇到的所有情况,即使模板嵌套了几层深层。

如何运作

对于缩进代码,如果提供了缩进函数列表,那么Emacs将按顺序尝试它们,如果当前正在执行的那个返回nil,它将调用下一个。{1}。我所做的是在列表的开头添加一个新的缩进函数,用于检测该行上的第一个非空格字符是否为'&gt;',如果是,则将缩进设置为位置0(将对其进行排列)打开模板)。这还包括您具有模板模板参数的情况,如下所示:

template <
  template <
    typename T,
    typename U,
    typename... Args
  > class... CS
>

因为它并不关心“&gt;”之后的内容。因此,缩进函数列表的工作原理如果'&gt;'不是第一个字符,函数返回nil并调用通常的缩进函数。

答案 1 :(得分:1)

评论

我认为部分您遇到的问题是,当您实例化模板时,emacs CC模式会使用相同的template-args-cont结构查看它。所以,考虑到这一点,我扩展了你原来的想法,并试图让它符合我的喜好;我使代码冗长,希望每个人都能理解我的意图。 :)当你实例化时,这不应该导致问题,它似乎也适用于模板模板参数!试试这个,直到有更多Elisp技能的人可以提供更好的解决方案!

如果您遇到任何“打斗”(即交替或损坏的缩进),请尝试重新加载cpp文件 Cx Cv 输入并再次缩进。有时使用模板模板参数,emacs会将内部参数显示为arglist-cont-nonempty,甚至可以使用template-args-const来回切换,但重新加载始终会恢复状态。

用法

要执行您想要的操作,请使用以下代码并添加到c-offsets-alist条目中来尝试此操作:

(template-args-cont . brian-c-lineup-template-args)

并设置变量

(setq brian-c-lineup-template-closebracket t)

我实际上更喜欢稍微不同的对齐方式:

(setq brian-c-lineup-template-closebracket 'under)

代码

(defvar brian-c-lineup-template-closebracket 'under 
  "Control the indentation of the closing template bracket, >.
Possible values and consequences:
'under : Align directly under (same column) the opening bracket.
t      : Align at the beginning of the line (or current indentation level.
nil    : Align at the same column of previous types (e.g. col of class T).")

(defun brian-c-lineup-template--closebracket-p ()
  "Return t if the line contains only a template close bracket, >."
  (save-excursion 
    (beginning-of-line)
    ;; Check if this line is empty except for the trailing bracket, >
    (looking-at (rx (zero-or-more blank)
            ">"
            (zero-or-more blank)))))

(defun brian-c-lineup-template--pos-to-col (pos)
  (save-excursion
    (goto-char pos)
    (current-column)))

(defun brian-c-lineup-template--calc-open-bracket-pos (langelem)
  "Calculate the position of a template declaration opening bracket via LANGELEM."
  (save-excursion 
    (c-with-syntax-table c++-template-syntax-table
      (goto-char (c-langelem-pos langelem))
      (1- (re-search-forward "<" (point-max) 'move)))))

(defun brian-c-lineup-template--calc-indent-offset (ob-pos)
  "Calculate the indentation offset for lining up types given the opening 
bracket position, OB-POS."
  (save-excursion
    (c-with-syntax-table c++-template-syntax-table
      ;; Move past the opening bracket, and check for types (basically not space)
      ;; if types are on the same line, use their starting column for indentation.
      (goto-char (1+ ob-pos))
      (cond ((re-search-forward (rx 
                 (or "class"
                     "typename"
                     (one-or-more (not blank))))
                (c-point 'eol)
                'move)
         (goto-char (match-beginning 0))
         (current-column))
        (t
         (back-to-indentation)
         (+ c-basic-offset (current-column)))))))

(defun brian-c-lineup-template-args (langelem)
  "Align template arguments and the closing bracket in a semi-custom manner."
  (let* ((ob-pos (brian-c-lineup-template--calc-open-bracket-pos langelem))
     (ob-col (brian-c-lineup-template--pos-to-col ob-pos))
     (offset (brian-c-lineup-template--calc-indent-offset ob-pos)))

    ;; Optional check for a line consisting of only a closebracket and
    ;; line it up either at the start of indentation, or underneath the
    ;; column of the opening bracket
    (cond ((and brian-c-lineup-template-closebracket
          (brian-c-lineup-template--closebracket-p))
         (cond ((eq brian-c-lineup-template-closebracket 'under)
            (vector ob-col))
           (t
            0)))
        (t
         (vector offset)))))

答案 2 :(得分:0)

这是一种不同的方法,然后更改标签,但使用像Yasnippet这样的代码段系统呢(参见示例here)。

唯一的问题是,如果您重新格式化文档“M-x索引区域”(或该部分),它可能会返回到其他选项卡规则。