Skip to content

Latest commit

 

History

History
412 lines (307 loc) · 12 KB

style-guide.adoc

File metadata and controls

412 lines (307 loc) · 12 KB

编码样式指南

1. 介绍

Red是一种同像语言,代码可以被视为数据。 该功能的结果是语言几乎是完全自由的,以便应对所有可能的方式来格式化数据,以及足够的灵活性满足 DSL 特定格式化需求。 以下内容只是格式化 Red 代码的 许多方法之一

本文档描述了 Red 源代码中使用的官方编码风格,因此,尊重此编码风格是提交给 Red/Red Github 资源库的每个提取请求的先决条件。

由于 Red/System 是 Red 的一种方言,它们共享相同的语法和编码风格规则。 特定的 Red/System 规则被标记为这样。

以下规则的目标是最大限度地提高可读性,包括在屏幕上保持最佳数量的代码行可见,同时最大限度地减少对注释的需求。

2. 代码行长度

对于单行代码,没有严格定义的最大列数,因为它可以根据使用的字体类型(大小,比例或固定宽度)或突出显示效果而有所不同。 应该可以在最多 1080p 监视器宽度的编辑器中读取全部代码(不包括注释)。 在我们用于 Red 代码库编码的显示器上,它大约是 100 列。 在下面的描述中,表达式尺寸太长的 表达式将会不符合上述标准的代码行大小。

3. 缩进

Red 代码库使用大小为 4 列的列表符缩进源代码。 这给出了太小的值(如 2 列)和太大值(如 8 列)之间的良好折衷。 使用制表符也意味着您可以在编辑器中将其调整为您的个人偏好,同时遵守规则(只需使用制表符注意正确的对齐方式)。

所有贡献的 Red 文件到 Red/Red 代码仓库应该在其标题中包含以下字段:

制表符: 4

每次打开一个块或圆括号后,你都要去新行,你应该在一个选项卡上缩进。

正确

 func [
     arg1
     arg2
     ...
 ][
     print arg1
     ...
 ]

不正确

 func [
 arg1				;-- 缺少缩进!
 arg2
 ...
 ][
		print arg1		;-- 过度缩进!
		...
 ]

4. 块布局

所有以下规则适用于块 [] 以及括号 ()

空块不包含任何空格:

a: []

相邻的块在一个的结尾和另一个的开始之间不需要空格:

[][]
[]()
[
   你好
][						;-- 不需要间距
   世界
]

然而,可以在嵌套块之间使用空格:

 array: [[] [] [] []]
 list:  [ [] [] [] [] ]

 either a = 1 [["你好"]][["世界"]]
 either a = 1 [ ["你好"] ][ ["世界"] ]

对于包含小块的表达式,它们通常在同一行上打开和关闭

b: either a = 1 [a + 1][3]

如果行太长,则该块应该用一个级别的缩进包裹在几行上:

正确

 b: either a = 1 [
     a + 1
 ][
     123 + length? mold a
 ]

不正确

 b: either a = 1
     [a + 1][123 + length? mold a]

这种风格是错误的,因为它破坏了将代码复制/粘贴到 Red 控制台的能力(同样 将在检测到块参数之前进行评估).

如果第一个块足够小并且可以适合同一行,则只有后续的块被包裹在几行中:

 print either a = 1 ["hello"][
     append mold a "这是一个很长的表达式"
 ]

 while [not tail? series][
     print series/1
     series: next series
 ]

5. 命名约定

变量名 应为单字 名词 。 选择简短的词语,尽可能地符合用意。 应该首先使用常用的单词(特别是如果它们已经在现有 Red 源代码的同一上下文语境中使用过)。 如果需要,请使用 synonyms dictionaryv,找到能使用的最好的单词。 应尽可能避免单字母或缩写词(除非缩写词常用)。

由多个单词组成的名称用短划线 - 字符分隔。 只有在找不到合适的单词时或者与已经使用的单词混淆才会使用双字的名称。 由两个以上的单词组成的变量名称只能在极少数情况下使用。 尽可能多地使用单个词让代码水平方向更加紧凑,大大提高可读性。 避免无用的冗长。

正确

 code: 123456
 name: "John"
 table: [2 6 8 4 3]
 lost-items: []

 unless tail? list [author: select list index]

不正确

 code_for_article: 123456
 Mytable: [2 6 8 4 3]
 lostItems: []

 unless tail? list-of-books [author-property: select list-of-books selected-index]

函数名称 应该努力成为单字 变量 ,以表达一个动作,虽然通常需要两个或三个字的名字。 应尽可能避免超过三个字。 变量命名约定也适用于函数名称。 一个名词或一个形容词,后跟一个问号也被接受。 通常,它表示返回值是 logic! 类型,但这不是严格的规则,因为它可以方便地形成用于检索属性的单字动作名称(例如 length?, index?)。 当用两个或多个单词形成函数名称时,始终将动词放在第一个位置。 如果为变量和函数仔细挑选了名称,则代码会变成近乎自带文档,通常这会减少对注释的需要。

正确

 make:   func [...
 reduce: func [...
 allow:  func [...
 crunch: func [...

不正确

 length:    func [...
 future:    func [...
 position:  func [...
 blue-fill: func [...		;-- 应填充蓝色

这些适用于操作系统或非 Red 第三方 API 名称的命名规则有一个例外。 为了使 API 特定的功能和结构字段名称易于识别,应使用其原始名称。 它在视觉上有助于区分这些导入的名称与常规 Red 或 Red/System 代码。 例如:

 tagMSG: alias struct! [
     hWnd   [handle!]
     msg    [integer!]
     wParam [integer!]
     lParam [integer!]
     time   [integer!]
     x      [integer!]
     y      [integer!]
  ]

 #import [
    "User32.dll" stdcall [
        CreateWindowEx: "CreateWindowExW" [
            dwExStyle    [integer!]
            lpClassName  [c-string!]
            lpWindowName [c-string!]
            dwStyle      [integer!]
            x            [integer!]
            y            [integer!]
            nWidth       [integer!]
            nHeight      [integer!]
            hWndParent   [handle!]
            hMenu        [handle!]
            hInstance    [handle!]
            lpParam      [int-ptr!]
            return:      [handle!]
        ]
    ]
]

6. 大小写

默认情况下,所有变量和函数名称都应为小写,除非有很好的理由使用大写字母,例如:

  • 名字是缩写,例如 GMT(格林威治标准时间)

  • 名称是操作系统或(非 Red)第三方 API 相关的

7. 宏 (Red/System)

应用相同的命名约定来获取 Red/System 宏名称。 宏通常使用大写字母作为名称的一种方式,以便轻松地与其余代码区分开(除非意图明确地使其看起来像常规代码,如伪自定义数据类型定义)。 当使用多个单词时,它们由下划线 _ 字符分隔,以增加与常规代码的差异。

(TBD:提取 Red 代码库中使用的所有单字名称作为示例)

8. 函数定义

一般规则是将定义块保留在一行之内。 代码块可以在同一行或多行上。 在 Red/System 的情况下,由于定义块往往更长,大多数函数定义块都被包裹在几行中,所以为了视觉一致性,通常会包含更小规模的块。

正确

 do-nothing: func [][]
 increment: func [n [integer!]][n + 1]

 increment: func [n [integer!]][
    n + 1
 ]

 increment: func [
     n [integer!]
 ][
     n + 1
 ]

不正确

 do-nothing: func [
 ][
 ]

 do-nothing: func [

 ][

 ]

 increment: func [
     n [integer!]
 ][n + 1]

当定义块太长时,它应该被包裹进好几行中。 封装定义块时,每个类型定义必须与其参数在同一行上。 可选属性块应该在其自己的行上。 每个语句新起一行。 如果后跟一个参数,则参数可以在同一行或具有缩进的新行(仅与同一定义块中的其他改进一致)。 对于 /local 语句,如果本地关键字后面没有类型注释,则可以将它们放在同一行上。

当将定义块封装在几行中时,建议将连续参数的数据类型定义对齐在同一列上,以便于阅读。 这种对齐最好使用制表符(如果你严格遵循这些编码风格规则),否则使用空格。

正确

 make-world: func [
     earth	 [word!]
     wind 	 [bitset!]
     fire	 [binary!]
     water	 [string!]
     /with
         thunder [url!]
     /only
     /into
         space   [block! none!]
     /local
	 plants animals men women computers robots
 ][
     ...
 ]

不正确

 make-world: func [
  	[throw] earth [word!]		;-- 属性块不在自己的行上
    	wind	[bitset!]
    	fire [binary!]			;-- 未对齐类型定义
    	water	[string!]
    	/with
            thunder [url!]
    	/only
    	/into space [block! none!]	;-- 与格式化 /with 不一致
    	/local
    	    plants animals		;-- 断行太早了
    	    men women computers robots
][
	...
]

对于文本串,如果定义块被包裹,主要(描述函数)应该在自己所在的行上。 参数和语句文本字符串应与其描述的项目在同一行。 文字块以大写字母开始,不需要结束点(当通过 help 功能打印在屏幕上时它会自动添加)。

正确

 increment: func ["Add 1 to the argument value" n][n + 1]

 make-world: func [
     "建立新世界"
     earth    [word!]      "1st element"
     wind     [bitset!]    "2nd element"
     fire     [binary!]    "3rd element"
     water    [string!]
     /with 		   "Additional element"
         thunder [url!]
     /only		   "Not implemented yet"
     /into		   "Provides a container"
         space [unset!]    "The container"
     /local
         plants animals men women computers robots
 ][
	...
 ]

不正确

 make-world: func ["建立新世界"	;-- 应该新起一行
     earth	[word!]		"1st element"
     wind	[bitset!]	  "2nd element"	;-- 过度缩进
     fire	[binary!]
     "3rd element"			;-- 应与 `fire` 在同一行
     water	[string!]
     /with 			"Additional element"
    		thunder [url!]
     /only "Not implemented yet"	;-- 应与其他文字块对齐
     /into
           "Provides a container"      ;-- 应该遵循语句
    	    space [unset!]	"The container"
     /local
         plants animals men women computers robots
 ][
    	...
 ]

9. 函数调用

参数在同一行上跟随函数调用。 如果行变得太长,则可以使用缩进将参数包裹在几行(每行一个参数)中。

正确

 foo arg1 arg2 arg3 arg4 arg5

 process-many
     argument1
     argument2
     argument3
     argument4
     argument5

不正确

 foo arg1 arg2 arg3
     arg4 arg5

 foo
     arg1 arg2 arg3
     arg4 arg5

 process-many
     argument1
         argument2
             argument3
                 argument4
                     argument5

对于具有许多嵌套部分的长表达式,发现每个表达式的边界有时会很困难。 使用括号将嵌套调用与其参数进行分组是可以接受的(但不是强制性的)。

 head insert (copy/part [1 2 3 4] 2) (length? mold (2 + index? find "Hello" #"o"))

 head insert
     copy/part [1 2 3 4] 2
     length? mold (2 + index? find "Hello" #"o")

10. 注释

在 Red 代码库中:

  • 注释使用 ;-- 前缀(更强的视觉提示)

  • 单行注释从第 57 列开始(做到平均值最好,否则 53 列也可以)

  • 多行注释是使用多个单行前缀而不是 comment {…​} 结构完成的。

一般规则是将注释放在相应代码开头相同的行上,而不是在新行上,以显著节省垂直空间。 然而,如果这个注释用于分割代码块,那么把它放在一个新的行上就行了。

11. 字符串语法

对于单行字符串使用 ""{} 格式用于保存多行字符串。 尊重此规则可确保:

  • 加载之前和之后的源代码更加一致

  • 更好地传达意义

规则的一个例外是当单行字符串包含 " 字符本身时。 在这种情况下,最好使用 {} 形式,而不是转义引号 ^" ,因为它更可读。

12. 新行用法

TBD