🐛 fix(tsl-syntax-reference): prefer ordinary quotes for plain text

This commit is contained in:
csh
2026-08-14 17:12:56 +08:00
parent 3d3036ebe1
commit 5d73964db8
4 changed files with 47 additions and 5 deletions
@@ -68,6 +68,14 @@
- 一旦某次调用里开始使用命名参数,后面的参数就不能再退回位置参数。
<!-- quickstart-rule: string-literal-default -->
- 普通单行文本默认使用 `"..."``'...'`;根据内容选择不冲突的引号,必要时再使用转义或连续同类引号。`%% ...%%` 仅用于多行文本、引号非常密集等原始字符串场景;不得因为内容是中文、非 ASCII 或较长就自动改用 `%%`
<!-- quickstart-rule: string-prefix-by-type -->
- `U``L` 前缀只由目标字符串类型或已确认的 API 编码要求决定,不能因为内容是中文就自动添加;普通中文内容优先直接写成 `"中文内容"`
<!-- quickstart-rule: index-origins -->
- `array(...)` 既可以写顺序数组,也可以写字符串键表;顺序数组和 `binary(...)` 二进制缓冲区下标从 `0` 开始,字符串下标从 `1` 开始。
@@ -30,7 +30,7 @@
- 字符串替换子串用 `s[start:end] := "..."`
- 字符串删除子串,本质上就是 `s[start:end] := ""`
- 字符串插入子串用 `s[index:0] := "..."`
- 普通字符串、`L""` 宽串、`U""` UTF8 前缀串,以及 `%%` 原始字符串都属于文档明确写法。
- 普通字符串、`L""` 宽串、`U""` UTF8 前缀串,以及 `%%` 原始字符串都属于文档明确写法;合法形式不等于默认选择,生成优先级见本页“字符串字面量选择与边界规则”
- `%%` 原始字符串开头后必须先跟一个空白分隔符;可以带标识符,也支持多行。
- `L%% ...%%``U%% ...%%` 这两种带前缀的原始字符串也属于文档明确写法。
- 普通字符串里的 `\uXXXX` 不要直接按“单字符宽串”理解;`length("\u0041") = 2`,而 `length(L"\u0041") = 1``length(U"\u0041") = 1`
@@ -48,7 +48,7 @@
- 只复制任务需要的值、字面量、下标或字符串片段,不要把多个示例拼成未写入文档的新语法。
- 普通示例默认按 `.tsl` 脚本语句区书写;需要函数或类型时,放在后置声明区。
- 字符串编码、宽串和原始字符串边界不清楚时,优先看本页“字符串边界规则”。
- 字符串字面量选择、编码、宽串和原始字符串边界不清楚时,优先看本页“字符串字面量选择与边界规则”。
最基础的值写法:
@@ -200,11 +200,23 @@ writeLn(keyed[1]);
说明:`length(items)` 统计已有项数,不等于“最大整数键 + 1”;下标 `1` 没有写入,因此读取为 `nil`
### 字符串边界规则
### 字符串字面量选择与边界规则
<!-- section-id: syntax-03-004 -->
<!-- tags: 原始字符串, 多行字符串, 引号怎么转义, 字符串里带引号, 长文本, 宽字符串, 宽串前缀, utf8 转换, 编码转换, 字符码拼接, 二进制缓冲区, 字节读写 -->
<!-- tags: 普通字符串, 字符串字面量, 中文字符串, 普通中文内容, 非 ASCII, 原始字符串, 多行字符串, 引号怎么转义, 字符串里带引号, 长文本, U 前缀, L 前缀, 宽字符串, 宽串前缀, utf8 转换, 编码转换, 字符码拼接, 二进制缓冲区, 字节读写 -->
<!-- quickstart-rule: string-literal-default -->
- 普通单行文本默认使用 `"..."``'...'`;根据内容选择不冲突的引号,必要时再使用转义或连续同类引号。`%% ...%%` 仅用于多行文本、引号非常密集等原始字符串场景;不得因为内容是中文、非 ASCII 或较长就自动改用 `%%`
<!-- quickstart-rule: string-prefix-by-type -->
- `U``L` 前缀只由目标字符串类型或已确认的 API 编码要求决定,不能因为内容是中文就自动添加;普通中文内容优先直接写成 `"中文内容"`
需要为真实 API 选择字符串类型或编码前缀时,先用 `tsl-api-reference` 取回该 API 的精确要求;语法页不根据文本内容猜测编码。
以下 `%%` 示例只说明原始字符串的合法外形和边界,不表示普通文本应默认采用原始字符串。
原始字符串 `%%` 的空白分隔、标识符和多行:
@@ -600,7 +612,9 @@ items := array(1, 2, 3);
- 把字符串区间当成数组切片式的半开区间;`s[1:3]` 返回 `ABC` 而不是 `AB`,结束位会被包含进结果,正向示例见本页“字符串子串读写”。
-`s[index:0] := ...` 误解成“替换到第 0 位”。
- 把二进制缓冲区也按字符串的 `1` 基下标去理解。
- 在普通字符串示例里默认切到原始字符串、宽串 / UTF8 前缀、字符码或编码转换,却没有按本页字符串边界规则确认
- 因为内容是中文、非 ASCII 或较长,就把普通单行文本自动改写成 `%%` 原始字符串
- 因为内容是中文,就自动添加 `U` / `L` 前缀;前缀只由目标字符串类型或已确认的 API 编码要求决定。
- 在普通字符串示例里默认切到原始字符串、宽串 / UTF8 前缀、字符码或编码转换,却没有按本页字符串字面量选择与边界规则确认。
- 把普通字符串里的 `\uXXXX` 直接当成宽串单字符。
-`U""` 当成宽串;需要宽串时按本页转换链处理。
-`#0` / `\0` 当成 C 风格字符串终止符。
@@ -97,6 +97,8 @@ QUICKSTART_RULE_OWNER_PAGES = {
"unit-default": "09_units_and_scope.md",
"named-arguments": "05_functions_and_calls.md",
"named-argument-order": "05_functions_and_calls.md",
"string-literal-default": "03_values_and_literals.md",
"string-prefix-by-type": "03_values_and_literals.md",
"index-origins": "03_values_and_literals.md",
}
+18
View File
@@ -336,6 +336,24 @@ class TslSyntaxReferenceTests(unittest.TestCase):
self.assertEqual("03_values_and_literals.md", result.matches[0].section.page.name)
self.assertEqual("核心规则", result.matches[0].section.heading_path[-1])
def test_string_literal_queries_rank_generation_policy_first(self):
for query in ("普通中文字符串", "原始字符串 %%", "字符串 U L 前缀"):
with self.subTest(query=query):
result = lookup.query_sections(query, "explain", limit=1)
self.assertEqual("syntax-03-004", result.matches[0].section.id)
def test_string_literal_generation_policy_is_in_quickstart_and_detail(self):
quickstart = run_lookup("--section", "syntax-01-002")
detail = run_lookup("--section", "syntax-03-004")
self.assertEqual(0, quickstart.returncode, msg=quickstart.stderr)
self.assertEqual(0, detail.returncode, msg=detail.stderr)
for output in (quickstart.stdout, detail.stdout):
self.assertIn("普通单行文本默认使用", output)
self.assertIn("不得因为内容是中文、非 ASCII 或较长", output)
self.assertIn("不能因为内容是中文就自动添加", output)
self.assertIn('普通中文内容优先直接写成 `"中文内容"`', output)
def test_every_curated_page_alias_still_ranks_its_page_first(self):
for page_name, aliases in lookup.PAGE_INTENT_ALIASES.items():
for alias in aliases: