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-install
和 c-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]