Physical Address:
ChongQing,China.
WebSite:
团队代码格式化方案,大家一起写出漂亮的代码吧~
在一个大型项目中,我们往往都需要通过团队协作去达成我们的目标。由于个人风格不同,当众多开发者一起开发时,难免会遇到代码风格上的问题。代码风格的不同会导致我们Review时不能很好的捕捉具体的变动,尤其是Git提交后,格式的改动会让评审时浪费额外的精力。为了保证不同开发者的代码可读性与美观性,我们需要借助格式化工具来将每个人的代码进行格式化,且保持格式化之后的风格统一。格式化的方式有很多,比较推荐的是使用Visual Studio Code自带的格式化功能或者借助clang-format程序完成。
其中Visual Studio Code自带的格式化可能会随着版本不同而变更,如果使用Clang-format,在保证.clang-format文件保持不变的情况下,可以保证代码格式化后的效果一致性。所以最好的办法还是将clang-format固化下来。今天这篇博客的内容,就是教大家如何去做clang-format的配置,并实现git提交时自动格式化的功能。需要注意的是,使用clang-format前需要将clang-format的可执行文件路径设置到环境变量内。
如何安装clang-format程序我这里就不再多讲,可以自行去官网下载,也可以借助Visual Studio Code下载插件,在下载完并配置好环境变量后,我们在Windows Cmd下输入:clang-format –version,如果配置正确,我们应该能看到这样的输出:
在配置好之后,我们需要做的就是确定我们的格式化标准参考文件,这份文件通常以.clang-format命名,如何生成自己想要的.clang-format文件呢,这里我简单简绍两种方法:一是通过官方样例修改成自己喜欢的样子,边修改边查看效果,直到满意位置。二是通过已有的代码库,在大家都认可的风格下去自动生成,clang-format文件。
使用官方模板进行修改时需要一边修改一边格式化后看效果,为了这个过程方便一些,我们还是借助Visual Studio Code,安装好插件clang-format或者C/C++插件,设置好clang-format可执行文件的路径以及代码格式化的参照标准,如下所示:
C_CPP:Clang_format_path项用于设置clang-format可执行文件的路径
C_CPP:Clang_format_style项用于设置格式化的标志,设置file即表明将以.clang-format格式化标准文件中的规则项来格式化。
在做完上述设置后,我们还需要设置保存时自动格式化,设置如下:
在设置完成之后,我们使用clang-format -style=Google -dump-config > googleexample
命令生成官方的参考模板,在这里,可以-style命令用于设定基准的代码风格,可选的基准风格包括LLVM、Google、Chromium、Mozilla、WebKit等,请注意,由于clang-format程序的不同,生成的模板可能是有差异的。
在生成模板后,我们可以根据需要自行调整参数,在保存后即可观察到格式化之后的风格,我们一边调整一边观察效果,以达到自认为满意的状态。其中关于格式化标准文件中配置项的说明,我们可以参考clang官方:https://clang.llvm.org/docs/ClangFormatStyleOptions.html的Configurable Format Style Options指导说明。
当然了,这样调整的过程可能比较漫长也比较辛苦的。但好处时高度定制化,并且你可以熟悉其中每一项配置变化产生的不同效果。
假如你拥有一份代码,你很喜欢这份代码的编码风格,有没有办法可以快捷地生成.clang-format并应用到自己的项目,答案是肯定的。
这种方式需要借助其他工具,那就是unformat,这是一个开源项目,这个项目利用python可以快速生成clang-format文件。其项目的Git地址如下:https://github.com/johnmcfarlane/unformat。我们将这个项目clone到本地(Linux环境内)。
假设你clone到本地路径:/home/franzkafka95/Desktop/unformat-master,而你需要为其生成.clang-format文件的代码位于/home/franzkafka95/Desktop/codebase这个目录,,使用如下命令生成.clang-format文件:
python3 /home/franzkafka95/Desktop/unformat-master/unformat-master –root /home/franzkafka95/Desktop/codebase /home/franzkafka95/Desktop/codebase/**/*.h /home/franzkafka95/Desktop/codebase /**/*.cpp
执行后,就会开始自动更具代码生成.clang-format文件,我们可以随时通过Crtl+C打断这个过程。完成后会在代码路径下生成clang-format文件。我们拿到这份文件,直接放入我们需要格式化的代码工程的顶层目录,保存后观察一下效果,如有些地方不满意还可以做调整。
现在我们已经得到了.clang-format文件,将其作为git仓库内的一部分提交,项目其他成员同步下来后使用Visual Studio Code的自动格式化功能即可保证大家的代码风格。
考虑到项目开发成员并不一定都喜欢使用Visual Studio Code,那有没有办法可以做到在我们Commit时自动格式化呢,答案当然时肯定的啦。其实实现起来其实非常容易,我们可以借助.git/hook/pre-clang脚本完成。pre-commit脚本的内容如下:
#!/bin/bash#NOTE:Please do not edit this fileSTYLE=$(git config --get hooks.clangformat.style)if [ -n "${STYLE}" ] ; thenSTYLEARG="-style=${STYLE}"else#Note:Use current top directory clang-formatSTYLE=$(git rev-parse --show-toplevel)/.clang-formatif [ -n "${STYLE}" ] ; thenSTYLEARG="-style=file"fifiecho "format config file is $STYLE"format_file() {file="${1}"echo "Will format $file automatically..."if [ -f $file ]; thenclang-format -i ${STYLEARG} ${1}git add ${1}fi}case "${1}" in--about )echo "Runs clang-format on source files";;* )for file in `git diff-index --cached --name-only HEAD | grep -iE '\.(cpp|cc|h|hpp)$' ` ; doformat_file "${file}"done;;esac#!/bin/bash #NOTE:Please do not edit this file STYLE=$(git config --get hooks.clangformat.style) if [ -n "${STYLE}" ] ; then STYLEARG="-style=${STYLE}" else #Note:Use current top directory clang-format STYLE=$(git rev-parse --show-toplevel)/.clang-format if [ -n "${STYLE}" ] ; then STYLEARG="-style=file" fi fi echo "format config file is $STYLE" format_file() { file="${1}" echo "Will format $file automatically..." if [ -f $file ]; then clang-format -i ${STYLEARG} ${1} git add ${1} fi } case "${1}" in --about ) echo "Runs clang-format on source files" ;; * ) for file in `git diff-index --cached --name-only HEAD | grep -iE '\.(cpp|cc|h|hpp)$' ` ; do format_file "${file}" done ;; esac
还需要说明的是.git/hooks/内容是无法提交到git仓库的,那么我们如何将这一份pre-commit文件同步到所有开发者的工程内呢,在这里,我们需要借助第二个脚本,假设命名为DevelopInit.sh,并在我们的git仓库内创建一个名为DevelopmentSetup的目录,将写好的pre-commit文件放在该目录内,编写 DevelopInit.sh 脚本内容如下:
# 获取脚本路径cd "$(dirname "$BASH_SOURCE")"script_file=$(pwd)/$(basename "$BASH_SOURCE")script_path=$(dirname "$script_file")cd - >/dev/null#拷贝hooks到对应目录cp -fv $script_path/DevelopmentSetup/pre-commit $script_path/.git/hooks/# 获取脚本路径 cd "$(dirname "$BASH_SOURCE")" script_file=$(pwd)/$(basename "$BASH_SOURCE") script_path=$(dirname "$script_file") cd - >/dev/null #拷贝hooks到对应目录 cp -fv $script_path/DevelopmentSetup/pre-commit $script_path/.git/hooks/
将DevelopInit.sh脚本放置在与.git同一目录层级下,提交到git代码仓库后团队成员都进行同步并执行 DevelopInit.sh 脚本,执行之后大家的本地仓库都会在提交时自动使用clang-format进行格式化啦。
在这里我也将我自己用的.clang-format文件分享给大家,当然了我的代码风格是C++的,基于LLVM进行修改的,具体如下:
---Language: Cpp# BasedOnStyle: LLVMAccessModifierOffset: -4# 开括号(开圆括号、开尖括号、开方括号)后的对齐:AlignAfterOpenBracket: Align# 连续赋值时,对齐所有等号AlignConsecutiveAssignments: false# 连续声明时,对齐所有声明的变量名AlignConsecutiveDeclarations: falseAlignEscapedNewlines: Left# 水平对齐二元和三元表达式的操作数AlignOperands: false# 对齐连续的尾随的注释AlignTrailingComments: true# 允许函数声明的所有参数在放在下一行AllowAllParametersOfDeclarationOnNextLine: false# 允许短的块放在同一行AllowShortBlocksOnASingleLine: false# 允许短的case标签放在同一行AllowShortCaseLabelsOnASingleLine: false# 允许短的函数放在同一行AllowShortFunctionsOnASingleLine: Inline# 允许短的if语句保持在同一行AllowShortIfStatementsOnASingleLine: false# 允许短的循环保持在同一行AllowShortLoopsOnASingleLine: false# 总是在定义返回类型后换行(deprecated)AlwaysBreakAfterDefinitionReturnType: None# 总是在返回类型后换行:AlwaysBreakAfterReturnType: None# 总是在多行string字面量前换行AlwaysBreakBeforeMultilineStrings: true# 总是在template声明后换行AlwaysBreakTemplateDeclarations: false# false表示函数实参都在同一行BinPackArguments: true# false表示所有形参都在同一行BinPackParameters: false# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效BraceWrapping:AfterClass: falseAfterControlStatement: falseAfterEnum: falseAfterExternBlock: falseAfterFunction: trueAfterNamespace: falseAfterObjCDeclaration: falseAfterStruct: falseAfterUnion: falseBeforeCatch: falseBeforeElse: falseIndentBraces: falseSplitEmptyFunction: trueSplitEmptyNamespace: trueSplitEmptyRecord: trueBreakAfterJavaFieldAnnotations: true# 在二元运算符前换行BreakBeforeBinaryOperators: NonAssignment# 在大括号前换行BreakBeforeBraces: AllmanBreakBeforeInheritanceComma: false# 在三元运算符前换行BreakBeforeTernaryOperators: false# 在构造函数的初始化列表的逗号前换行BreakConstructorInitializers: BeforeCommaBreakConstructorInitializersBeforeComma: trueBreakStringLiterals: true# 每行字符的限制,0表示没有限制ColumnLimit: 0CommentPragmas: '^ IWYU pragma:'CompactNamespaces: trueConstructorInitializerAllOnOneLineOrOnePerLine: falseConstructorInitializerIndentWidth: 0ContinuationIndentWidth: 13Cpp11BracedListStyle: falseDerivePointerAlignment: trueDisableFormat: falseExperimentalAutoDetectBinPacking: trueFixNamespaceComments: trueForEachMacros:- foreach- Q_FOREACH- BOOST_FOREACHIncludeBlocks: PreserveIncludeCategories:- Priority: 2Regex: ^"(llvm|llvm-c|clang|clang-c)/- Priority: 3Regex: ^(<|"(gtest|gmock|isl|json)/)- Priority: 1Regex: .*IncludeIsMainRegex: (Test)?$IndentCaseLabels: falseIndentPPDirectives: NoneIndentWidth: 4IndentWrappedFunctionNames: falseJavaScriptQuotes: LeaveJavaScriptWrapImports: falseKeepEmptyLinesAtTheStartOfBlocks: falseLanguage: CppMacroBlockBegin: ''MacroBlockEnd: ''MaxEmptyLinesToKeep: 1NamespaceIndentation: NoneObjCBlockIndentWidth: 4ObjCSpaceAfterProperty: trueObjCSpaceBeforeProtocolList: truePenaltyBreakAssignment: 2PenaltyBreakBeforeFirstCallParameter: 15PenaltyBreakComment: 302PenaltyBreakFirstLessLess: 98PenaltyBreakString: 786PenaltyExcessCharacter: 1141897PenaltyReturnTypeOnItsOwnLine: 81PointerAlignment: LeftReflowComments: trueSortIncludes: falseSortUsingDeclarations: trueSpaceAfterCStyleCast: falseSpaceAfterTemplateKeyword: trueSpaceBeforeAssignmentOperators: trueSpaceBeforeParens: ControlStatementsSpaceInEmptyParentheses: falseSpacesBeforeTrailingComments: 1SpacesInAngles: falseSpacesInCStyleCastParentheses: falseSpacesInContainerLiterals: trueSpacesInParentheses: falseSpacesInSquareBrackets: falseStandard: Cpp11TabWidth: 9UseTab: Never...--- Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -4 # 开括号(开圆括号、开尖括号、开方括号)后的对齐: AlignAfterOpenBracket: Align # 连续赋值时,对齐所有等号 AlignConsecutiveAssignments: false # 连续声明时,对齐所有声明的变量名 AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left # 水平对齐二元和三元表达式的操作数 AlignOperands: false # 对齐连续的尾随的注释 AlignTrailingComments: true # 允许函数声明的所有参数在放在下一行 AllowAllParametersOfDeclarationOnNextLine: false # 允许短的块放在同一行 AllowShortBlocksOnASingleLine: false # 允许短的case标签放在同一行 AllowShortCaseLabelsOnASingleLine: false # 允许短的函数放在同一行 AllowShortFunctionsOnASingleLine: Inline # 允许短的if语句保持在同一行 AllowShortIfStatementsOnASingleLine: false # 允许短的循环保持在同一行 AllowShortLoopsOnASingleLine: false # 总是在定义返回类型后换行(deprecated) AlwaysBreakAfterDefinitionReturnType: None # 总是在返回类型后换行: AlwaysBreakAfterReturnType: None # 总是在多行string字面量前换行 AlwaysBreakBeforeMultilineStrings: true # 总是在template声明后换行 AlwaysBreakTemplateDeclarations: false # false表示函数实参都在同一行 BinPackArguments: true # false表示所有形参都在同一行 BinPackParameters: false # 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效 BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterExternBlock: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false SplitEmptyFunction: true SplitEmptyNamespace: true SplitEmptyRecord: true BreakAfterJavaFieldAnnotations: true # 在二元运算符前换行 BreakBeforeBinaryOperators: NonAssignment # 在大括号前换行 BreakBeforeBraces: Allman BreakBeforeInheritanceComma: false # 在三元运算符前换行 BreakBeforeTernaryOperators: false # 在构造函数的初始化列表的逗号前换行 BreakConstructorInitializers: BeforeComma BreakConstructorInitializersBeforeComma: true BreakStringLiterals: true # 每行字符的限制,0表示没有限制 ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: true ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 0 ContinuationIndentWidth: 13 Cpp11BracedListStyle: false DerivePointerAlignment: true DisableFormat: false ExperimentalAutoDetectBinPacking: true FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Priority: 2 Regex: ^"(llvm|llvm-c|clang|clang-c)/ - Priority: 3 Regex: ^(<|"(gtest|gmock|isl|json)/) - Priority: 1 Regex: .* IncludeIsMainRegex: (Test)?$ IndentCaseLabels: false IndentPPDirectives: None IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: false KeepEmptyLinesAtTheStartOfBlocks: false Language: Cpp MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 4 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 15 PenaltyBreakComment: 302 PenaltyBreakFirstLessLess: 98 PenaltyBreakString: 786 PenaltyExcessCharacter: 1141897 PenaltyReturnTypeOnItsOwnLine: 81 PointerAlignment: Left ReflowComments: true SortIncludes: false SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: true SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 9 UseTab: Never ...
给大家看看实际效果吧!
格式化前:
格式化后:
不得不说,格式化后还是好看许多。好了,以上就是这篇博客的全部内容了,希望大家会喜欢。如果各位有问题,可以通过评论告诉我,我会一一解答的。
请问我想实现以下这种,函数返回值是指针的时候 *挨着void ,变量是指针(或者引用)的时候*挨着变量名。
void* mMyNcion(int *a, int &b){}
我看官方支持的是 *号 要么都在左,要么都在右边。
期待回复,谢谢!