You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/specs/2026-05-15-complex-value-replace-design.md
+11-23Lines changed: 11 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -25,21 +25,17 @@ Input: a `yamlpath::Document`, a `yamlpath::Route`, and a `serde_yaml::Value` (M
25
25
26
26
1.**Locate the feature** — `document.query_pretty(&route)` to get the byte span with surrounding key context.
27
27
28
-
2.**Extract content** — `extract_with_leading_whitespace(feature)` to get the full `key: value` text. Split at the first `:` (colon-space) or `:\n` (colon-newline) into `key_part` (through the colon) and `value_part` (after the colon).
28
+
2.**Extract content** — `extract_with_leading_whitespace(feature)` to get the full `key: value` text. Split at the first `:` (naive `find(':')`, consistent with yamlpatch's own Replace implementation) into `key_part` (through the colon) and the rest.
29
29
30
-
3.**Detect inline comment** — Scan the first line of `value_part` for a trailing `# comment`. Heuristic: after the value content on the first line, find `\s+#`. Save the comment text if found.
30
+
3.**Compute indentation** — Derive base indent from the number of leading spaces on the feature's line. Value content indentation = base indent + 2 spaces.
31
31
32
-
4.**Compute indentation** — Derive base indent from the number of leading spaces on the feature's line. Value content indentation = base indent + 2 spaces.
32
+
4.**Serialize the new value** — `serde_yaml::to_string(&value)`, strip trailing newline, re-indent each line to the computed value indentation.
33
33
34
-
5.**Serialize the new value** — `serde_yaml::to_string(&value)`, strip trailing newline, re-indent each line to the computed value indentation.
34
+
5.**Assemble replacement text** — `key:\n indented_value`. Inline comments on the value line are not preserved, consistent with yamlpatch's own Replace behavior for scalar values.
35
35
36
-
6.**Assemble replacement text**:
37
-
- With comment: `key: # comment\n indented_value`
38
-
- Without comment: `key:\n indented_value`
36
+
6.**String surgery** — Replace the byte range from the feature's span (adjusted for leading whitespace) in the document source.
39
37
40
-
7.**String surgery** — Replace the byte range from the feature's span (adjusted for leading whitespace) in the document source.
41
-
42
-
8.**Re-parse** — `yamlpath::Document::new(patched_source)` to validate.
38
+
7.**Re-parse** — `yamlpath::Document::new(patched_source)` to validate.
43
39
44
40
### Root-level replace
45
41
@@ -64,17 +60,6 @@ Patches are applied sequentially. If a complex replace is encountered mid-batch,
64
60
65
61
`Document.upsert()` delegates to `replace()` when the path exists, and to `_create_at()` (which uses `Op.add`/`Op.merge_into`) when it doesn't. Since `Op.add` and `Op.merge_into` already handle complex values, the only broken path is the `replace` delegation — fixed by this change. No changes to `document.py`.
66
62
67
-
## Comment Relocation
68
-
69
-
When replacing a scalar that has an inline comment (e.g. `repos: [] # managed by tool`) with a multi-line block value:
70
-
71
-
- The comment is preserved on the key line: `repos: # managed by tool`
72
-
- The block value starts on the next line, indented
73
-
74
-
Detection heuristic: after stripping the YAML value from the first line of `value_part`, look for `\s+#` at the end.
75
-
76
-
**Limitation:** Comments inside quoted strings that happen to contain `#` could be misdetected. This is unlikely for the scalar values being replaced and is documented as a known limitation.
77
-
78
63
## Serialization Style
79
64
80
65
No changes to `Op.add` behavior. Complex values in `Replace` use `serde_yaml::to_string()` which produces block style by default (block mappings, block sequences). This matches the pre-commit config use case where block style is expected.
@@ -88,16 +73,19 @@ New tests in `tests/test_document.py`:
88
73
-`replace()` with nested dict-in-list-in-dict
89
74
-`upsert()` with a dict value (path exists → goes through replace)
90
75
-`upsert()` with a dict value (path doesn't exist → goes through add, already works)
91
-
- Comment preservation when replacing scalar with complex value
92
76
- Replacing a complex value with another complex value
93
77
- Root-level replace with complex value
94
78
- Indentation correctness at various nesting depths (0, 2, 4 spaces)
95
79
80
+
## Known Limitations
81
+
82
+
-**Quoted keys with colons** — `find_key_colon` uses a naive `str::find(':')`, which will misparse keys like `"host:port": value`. This is consistent with yamlpatch's own Replace implementation. When yamlpatch fixes this upstream, yamltrip should inherit the fix.
83
+
-**Inline comments** — Not preserved during complex replace, consistent with yamlpatch's scalar Replace behavior.
0 commit comments