Yanyg - SAN Software Engineer

Emacs C/C++代码版面设置

目录

代码版式(Code Style)一直是争吵的焦点。例如,缩进使用 SPC or TAB ,是 8个字符宽度还是4个字符宽度。

Emacs C/C++代码的版面调整通过 c-offsets-alist 控制。命名空间,函数,语句块, 子块,注释,多个参数换行等语法元素(syntactic element),可以分别进行控制。 通过 c-show-syntactic-information ,默认键位 C-c C-s 可以查看当前 代码的适用语法元素:

buffer_data = buffer + data.header_length +
        data.block_descriptor_length; // statement cont

buffer_data[2] &= ~0x05; // statement

if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC)
        return -EINVAL; // substatement

if (sdp->type != TYPE_DISK &&
    sdp->type != TYPE_ZBC && // arglist-cont-nonempty
    sdp->type != TYPE_MOD && // arglist-cont-nonempty
    sdp->type != TYPE_RBC)
        goto out;

if (index < 0) {
        sdev_printk(KERN_WARNING, sdp, "sd_probe: memory exhausted.\n"); // statement-block-intro
        goto out_put; // statement
}

1 生成代码版面配置

从头到尾定义代码版面是繁琐的。一般是先用默认的配置打开一个代码文件,手动 调整代码风格,执行 c-guess-no-installc-guess-view 生成模板,保存 并修改。例如,对于 linux/drivers/block/brd.c 执行上述操作的部分结果:

(c-add-style "STYLE NAME HERE"
             '("y/c-linux"
               (c-basic-offset . 8)     ; Guessed value
               (c-offsets-alist
                (block-close . 0)       ; Guessed value
                (brace-list-close . 0)  ; Guessed value
                (brace-list-entry . 0)  ; Guessed value
                (brace-list-intro . +)  ; Guessed value
                (class-close . 0)       ; Guessed value
                (defun-block-intro . +) ; Guessed value
                (defun-close . 0)       ; Guessed value
                (defun-open . 0)        ; Guessed value
                (inclass . +)           ; Guessed value
                (label . 0)             ; Guessed value
                (statement . 0)         ; Guessed value
                (statement-block-intro . +) ; Guessed value
                (statement-cont . +)    ; Guessed value
                (substatement . +)      ; Guessed value
                (topmost-intro . 0)     ; Guessed value
                (access-label . -)
                (annotation-top-cont . 0)
                (annotation-var-cont . +)
                (arglist-close . c-lineup-close-paren)
                (arglist-cont c-lineup-gcc-asm-reg 0)
                (arglist-cont-nonempty . c-lineup-arglist)
                (arglist-intro . c-lineup-arglist-intro-after-paren)
                (block-open . 0)
                (brace-entry-open . 0)
                (brace-list-open . 0)
                ... ...
                (string . -1000)
                (substatement-label . 0)
                (substatement-open . 0)
                (template-args-cont c-lineup-template-args +)
                (topmost-intro-cont first c-lineup-topmost-intro-cont c-lineup-gnu-DEFUN-intro-cont))))

保存为变量,并对特定路径/项目设置格式。

2 不同项目设置不同的代码版面

虽然尽量保持一致,然后不同项目总有些不同的配置。对于我而言至少有三种:

  • 私下写的一些代码,完全按照自己喜欢的样子;
  • 公司的代码,按照公司的编码规范;
  • 某些开源项目的代码,总是按照社区编码规范。

可以定义多组 c-style 变量,并设置hook处理:

(defconst y/c-style-basic
  '((c-tab-always-indent . nil)
    (c-basic-offset . 4)
    (c-offsets-alist
     (block-close . 0)       ; Guessed value
     (brace-list-close . 0)  ; Guessed value
     (brace-list-entry . 0)  ; Guessed value
     ...
     (template-args-cont c-lineup-template-args +)
     (topmost-intro-cont . c-lineup-topmost-intro-cont)))
  "y/c-basic")

(defconst y/c-style-linux
  '((c-tab-always-indent . nil) ; manualy added
    (c-basic-offset . 8)     ; Guessed value
    (c-offsets-alist
     (block-close . 0)       ; Guessed value
     (brace-list-close . 0)  ; Guessed value
     (brace-list-entry . 0)  ; Guessed value
     (brace-list-intro . +)  ; Guessed value
     (substatement-open . 0)
     ...
     (template-args-cont c-lineup-template-args +)
     (topmost-intro-cont first c-lineup-topmost-intro-cont c-lineup-gnu-DEFUN-intro-cont)))
  "y/c-linux")
(c-add-style "y/c-linux" y/c-style-linux)

(defconst y/c-style-alibaba
  '((c-tab-always-indent . nil) ; manualy added
    (c-basic-offset . 4)     ; Guessed value
    (c-offsets-alist
     (block-close . 0)       ; Guessed value
     (brace-list-close . 0)  ; Guessed value
     (template-args-cont c-lineup-template-args +)
     ...
     (topmost-intro-cont . c-lineup-topmost-intro-cont)))
  "y/c-alibaba")
(c-add-style "y/c-alibaba" y/c-style-alibaba)

(defun y/c-style-hook()
  "Config c/c++ style depends on file pathname"
  (when (buffer-file-name)
    (cond ((or (string-match "/pangu/" (buffer-file-name))
               (string-match "/apsara/" (buffer-file-name))
               (string-match "/stone/" (buffer-file-name)))
           (c-set-style "y/c-alibaba"))
          ((or (string-match "/linux.*/" (buffer-file-name)))
           (c-set-style "y/c-linux")
           ;; Linux use real tab. Auto buffer-local.
           (setq indent-tabs-mode t))
          (t ;; all default to y/c-basic
           (c-set-style "y/c-basic")))))
(add-hook 'c-mode-common-hook 'y/c-style-hook)

我的Emacs配置参见完整的内容。

3 Help info

Emacs Help:

c-offsets-alist is a variable defined in ‘cc-vars.el’.
Its value is nil

  Automatically becomes buffer-local when set.

Documentation:
Association list of syntactic element symbols and indentation offsets.
As described below, each cons cell in this list has the form:

    (SYNTACTIC-SYMBOL . OFFSET)

When a line is indented, CC Mode first determines the syntactic
context of it by generating a list of symbols called syntactic
elements.  The global variable ‘c-syntactic-context’ is bound to that
list.  Each element in the list is in turn a list where the first
element is a syntactic symbol which tells what kind of construct the
indentation point is located within.  More elements in the syntactic
element lists are optional.  If there is one more and it isn’t nil,
then it’s the anchor position for that construct.

After generating the syntactic context for the line, CC Mode
calculates the absolute indentation: First the base indentation is
found by using the anchor position for the first syntactic element
that provides one.  If none does, zero is used as base indentation.
Then CC Mode looks at each syntactic element in the context in turn.
It compares the car of the syntactic element against the
SYNTACTIC-SYMBOL’s in ‘c-offsets-alist’.  When it finds a match, it
adds OFFSET to the base indentation.  The sum of this calculation is
the absolute offset for line being indented.

If the syntactic element does not match any in the ‘c-offsets-alist’,
the element is ignored.

OFFSET can specify an offset in several different ways:

  If OFFSET is nil then it’s ignored.

  If OFFSET is an integer then it’s used as relative offset, i.e. it’s
  added to the base indentation.

  If OFFSET is one of the symbols ‘+’, ‘-’, ‘++’, ‘--’, ‘*’, or ‘/’
  then a positive or negative multiple of ‘c-basic-offset’ is added to
  the base indentation; 1, -1, 2, -2, 0.5, and -0.5, respectively.

  If OFFSET is a symbol with a value binding then that value, which
  must be an integer, is used as relative offset.

  If OFFSET is a vector then its first element, which must be an
  integer, is used as an absolute indentation column.  This overrides
  the previous base indentation and the relative offsets applied to
  it, and it becomes the new base indentation.

  If OFFSET is a function or a lambda expression then it’s called with
  a single argument containing the cons of the syntactic symbol and
  the anchor position (or nil if there is none).  The return value
  from the function is then reinterpreted as an offset specification.

  If OFFSET is a list then its elements are evaluated recursively as
  offset specifications.  If the first element is any of the symbols
  below then it isn’t evaluated but instead specifies how the
  remaining offsets in the list should be combined.  If it’s something
  else then the list is combined according the method ‘first’.  The
  valid combination methods are:

  ‘first’ -- Use the first offset (that doesn’t evaluate to nil).
  ‘min’   -- Use the minimum of all the offsets.  All must be either
             relative or absolute - they can’t be mixed.
  ‘max’   -- Use the maximum of all the offsets.  All must be either
             relative or absolute - they can’t be mixed.
  ‘add’   -- Add all the evaluated offsets together.  Exactly one of
             them may be absolute, in which case the result is
             absolute.  Any relative offsets that preceded the
             absolute one in the list will be ignored in that case.

‘c-offsets-alist’ is a style variable.  This means that the offsets on
this variable are normally taken from the style system in CC Mode
(see ‘c-default-style’ and ‘c-style-alist’).  However, any offsets
put explicitly on this list will override the style system when a CC
Mode buffer is initialized (there is a variable
‘c-old-style-variable-behavior’ that changes this, though).

Here is the current list of valid syntactic element symbols:

 string                 -- Inside multi-line string.
 c                      -- Inside a multi-line C style block comment.
 defun-open             -- Brace that opens a function definition.
 defun-close            -- Brace that closes a function definition.
 defun-block-intro      -- The first line in a top-level defun.
 class-open             -- Brace that opens a class definition.
 class-close            -- Brace that closes a class definition.
 inline-open            -- Brace that opens an in-class inline method.
 inline-close           -- Brace that closes an in-class inline method.
 func-decl-cont         -- The region between a function definition’s
                           argument list and the function opening brace
                           (excluding K&R argument declarations).  In C, you
                           cannot put anything but whitespace and comments
                           between them; in C++ and Java, throws declarations
                           and other things can appear in this context.
 knr-argdecl-intro      -- First line of a K&R C argument declaration.
 knr-argdecl            -- Subsequent lines in a K&R C argument declaration.
 topmost-intro          -- The first line in a topmost construct definition.
 topmost-intro-cont     -- Topmost definition continuation lines.
 annotation-top-cont    -- Topmost definition continuation line where only
                           annotations are on previous lines.
 annotation-var-cont    -- A continuation of a C (or like) statement where
                           only annotations are on previous lines.
 member-init-intro      -- First line in a member initialization list.
 member-init-cont       -- Subsequent member initialization list lines.
 inher-intro            -- First line of a multiple inheritance list.
 inher-cont             -- Subsequent multiple inheritance lines.
 block-open             -- Statement block open brace.
 block-close            -- Statement block close brace.
 brace-list-open        -- Open brace of an enum or static array list.
 brace-list-close       -- Close brace of an enum or static array list.
 brace-list-intro       -- First line in an enum or static array list.
 brace-list-entry       -- Subsequent lines in an enum or static array list.
 brace-entry-open       -- Subsequent lines in an enum or static array
                           list that start with an open brace.
 statement              -- A C (or like) statement.
 statement-cont         -- A continuation of a C (or like) statement.
 statement-block-intro  -- The first line in a new statement block.
 statement-case-intro   -- The first line in a case "block".
 statement-case-open    -- The first line in a case block starting with brace.
 substatement           -- The first line after an if/while/for/do/else.
 substatement-open      -- The brace that opens a substatement block.
 substatement-label     -- Labeled line after an if/while/for/do/else.
 case-label             -- A "case" or "default" label.
 access-label           -- C++ private/protected/public access label.
 label                  -- Any ordinary label.
 do-while-closure       -- The "while" that ends a do/while construct.
 else-clause            -- The "else" of an if/else construct.
 catch-clause           -- The "catch" or "finally" of a try/catch construct.
 comment-intro          -- A line containing only a comment introduction.
 arglist-intro          -- The first line in an argument list.
 arglist-cont           -- Subsequent argument list lines when no
                           arguments follow on the same line as the
                           arglist opening paren.
 arglist-cont-nonempty  -- Subsequent argument list lines when at
                           least one argument follows on the same
                           line as the arglist opening paren.
 arglist-close          -- The solo close paren of an argument list.
 stream-op              -- Lines continuing a stream operator construct.
 inclass                -- The construct is nested inside a class definition.
                           Used together with e.g. ‘topmost-intro’.
 cpp-macro              -- The start of a C preprocessor macro definition.
 cpp-macro-cont         -- Inside a multi-line C preprocessor macro definition.
 friend                 -- A C++ friend declaration.
 objc-method-intro      -- The first line of an Objective-C method definition.
 objc-method-args-cont  -- Lines continuing an Objective-C method definition.
 objc-method-call-cont  -- Lines continuing an Objective-C method call.
 extern-lang-open       -- Brace that opens an "extern" block.
 extern-lang-close      -- Brace that closes an "extern" block.
 inextern-lang          -- Analogous to the ‘inclass’ syntactic symbol,
                           but used inside "extern" blocks.
 namespace-open, namespace-close, innamespace
                        -- Similar to the three ‘extern-lang’ symbols, but for
                           C++ "namespace" blocks.
 module-open, module-close, inmodule
                        -- Similar to the three ‘extern-lang’ symbols, but for
                           CORBA IDL "module" blocks.
 composition-open, composition-close, incomposition
                        -- Similar to the three ‘extern-lang’ symbols, but for
                           CORBA CIDL "composition" blocks.
 template-args-cont     -- C++ template argument list continuations.
 inlambda               -- In the header or body of a lambda function.
 lambda-intro-cont      -- Continuation of the header of a lambda function.
 inexpr-statement       -- The statement is inside an expression.
 inexpr-class           -- The class is inside an expression.  Used e.g. for
                           Java anonymous classes.

You can customize this variable.

[back]