Emacs:类和结构体的不同缩进

11

我正在尝试在emacs中实现以下缩进:

class A
{
    // I ALWAYS use access labels in classes

    public: // access-label
        int member; // inclass
};

struct B
{
    // I NEVER use access labels in structs

    int member; // inclass
};

然而,通过以下配置文件...
(defun my-cpp-mode ()
  "My C++ mode"
  (c++-mode)
  (c-set-style "K&R")
  (setq c-basic-offset 4)
  (c-set-offset 'access-label '-)
  (c-set-offset 'inclass '++)
  ;; ...
  (setq mode-name "My C++")
)
(add-to-list 'auto-mode-alist '("\\.[ch]p?p?\\'" . my-cpp-mode))

... I achieve only:

class A
{
    public: // access-label
        int member; // inclass
};

struct B
{
        // this indentation is too long
        int member; // inclass
};

当然,这是因为:

  • 在缩进方面,“class”和“struct”显然没有区别(它们都是“inclass”),
  • “inclass”部分的缩进不取决于是否存在访问标签。

你有什么想法可以使“inclass”部分的缩进依赖于类/结构体或访问标签的存在吗?


我认为你只需要在public:和private:前面不加缩进级别。 - djechlin
谢谢,但如果你的意思是"{"和"public:"在同一列,那不是我想要的。 - Aurélien
1
我能理解你的愿望,如果进行足够的调整,应该是可行的。另一方面,这也有可能不容易实现,因为在C++中类和结构之间的非区分相当深入:你甚至可以为结构使用访问限定符。尽管这种用法很少见,但我会+1这个问题,并且会对是否出现任何解决方案感兴趣。 - MvG
3个回答

8

新回答

我遇到了你在问题中提到的具体需求。我需要根据我的新项目的编码风格设置缩进。经过一番研究,我使用 自定义行对齐函数 实现了这一点。

将你的 my-cpp-mode 修改为以下内容:

(defun my-c-lineup-inclass (langelem)
  (let ((inclass (assoc 'inclass c-syntactic-context)))
    (save-excursion
      (goto-char (c-langelem-pos inclass))
      (if (or (looking-at "struct")
              (looking-at "typedef struct"))
          '+
        '++))))

(defun my-cpp-mode ()
  "My C++ mode"
  (c++-mode)
  (c-set-style "K&R")
  (setq c-basic-offset 4)
  (c-set-offset 'access-label '-)
  (c-set-offset 'inclass 'my-c-lineup-inclass)
  ;; ...
  (setq mode-name "My C++")
)

如果这个答案可以接受,我会继续删除旧的答案。
旧答案
基于您想要实现的目标,我可以建议一种不同的方法吗?您似乎想要在不同的缩进级别上访问标签、类和类成员。请使用以下方法来实现。
(access-label . /)

来自Emacs文档:

如果OFFSET是符号 +'-'++'--'*/之一,则会将的正负倍数添加到基本缩进中;分别为1、-1、2、-2、0.5和-0.5。

这里是我定义的一个自定义样式的片段。

(c-add-style
 "xyz-style"
 '((indent-tabs-mode . nil)
   (fill-column . 75)
   (c-basic-offset . 4)
   (c-offsets-alist . (
                       (access-label . /)
                       (inextern-lang . 0)
                       (innamespace . 0)
                       (member-init-intro . ++)
                       ))))

c-basic-offset 设置为4时,(access-label . /) 会对访问标签添加一个负缩进,减少2个空格。以下是在您示例代码上使用我的缩进模式得到的实际结果。

class A
{
    // I ALWAYS use access labels in classes

  public: // access-label
    int member; // inclass
};

struct B
{
    // I NEVER use access labels in structs

    int member; // inclass
};

我建议使用此模式,因为成员变量/结构体成员的缩进级别保持一致。顺便说一句,Google C Style也采用了同样的方法。
据我所知,无法区分类成员和结构体成员(inclass 语法元素)。您可以使用 M-x c-syntactic-information-on-region 对区域进行语法分析。对示例进行的这种分析产生了以下输出。从输出中,无法区分是类还是结构体。
class A                                 // ((topmost-intro 1))
{                                       // ((class-open 1))
                                        // ((inclass 64) (topmost-intro 64) (comment-intro))I ALWAYS use access labels in classes
                                        // ((inclass 64) (topmost-intro 64))
  public:                               // ((inclass 64) (access-label 64))access-label
    int member;                         // ((inclass 64) (topmost-intro 64))inclass
};                                      // ((class-close 1))
                                        // ((topmost-intro 503))
struct B                                // ((topmost-intro 503))
{                                       // ((class-open 629))
                                        // ((inclass 694) (topmost-intro 694) (comment-intro))I NEVER use access labels in structs
                                        // ((inclass 694) (topmost-intro 694))
    int member;                         // ((inclass 694) (topmost-intro 694))inclass
};                                      // ((class-close 629))

是的,谢谢,这确实是我决定使用的妥协方案,所以我会+1你的答案,因为它可能会吸引很多Emacs用户。但正如你所说,它并没有回答实际问题。 - Aurélien
1
在你最后的编辑中,你表明这是不可能的,这现在肯定回答了这个问题。谢谢。 - Aurélien
这假设 struct 总是没有访问标签。我希望有一种解决方案,能够识别出是否存在访问标签... - user3458
你的新答案对我不起作用。goto-char 跳转到类的左大括号,而不是类/结构体定义。这是在 emacs 22.3.1 下发生的(旧版本,我知道,但我无能为力)。 - Trebor Rude

2

根据Praveen Kumar上面的答案,我实现了稍微不同版本的自定义阵容函数:

(defun my-c-lineup-inclass (langelem)
  (let ((inclass (assoc 'inclass c-syntactic-context)))
    (save-excursion
      (c-beginning-of-defun) ; This sees the correct string.
      (if (or (looking-at "struct")
              (looking-at "typedef struct"))
          '+
        '++))))

; In particular, the following offsets need to be specified:
(c-set-offset 'access-label '-)
(c-set-offset 'inclass 'my-c-lineup-inclass)
; ...

原始代码在花括号移到下一行时不起作用,即

struct foo
{
        int bar;
};

免责声明:我不懂任何Lisp语言。我只是玩了一下,这对我有用。例如,我不知道是否存在与此相关的性能问题。

仍将缩进到 "++"。


使用 (c-beginning-of-defun) 并不完全正确,因为它无法处理在结构体/类中定义的内联函数。 - Alex Ott

0

目前我项目的编码标准要求这种缩进方式。像Arkadiy一样,我更喜欢尊重访问标签的解决方案。这就是我想出来的:

(defun indent-after-access-label (langelem)
  "Return the appropriate indent for a class or a struct."
  (save-excursion
    (save-match-data
      ;; Optimization to avoid at least a few calls to re-search-backward.
      (if (assoc 'access-label c-syntactic-context)
          '++
        (if (re-search-backward "\\(?:p\\(?:ublic\\|r\\(?:otected\\|ivate\\)\\)\\)" c-langelem-pos langelem) t)
            '++
          '+)))))

如前所述,需要将indent-after-acess-label符号设置为inclass的缩进(通过c-set-offsetc-offset-alist等方式)。

由于re-search-backward的存在,这并不是理想的解决方案,但它确实可行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接