Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion origin.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func addOriginInMap(key, n *Node, file string) *Node {

// addOrigin injects a compact __origin__ sequence into the mapping node n.
//
// Format: [file, key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, s1_name, s1_count, s1_l0_delta, s1_c0, ...]
// Format: [file, key_name, key_line, key_col, nf, f1_name, f1_delta, f1_col, ..., ns, s1_name, s1_count, s1_l0_delta, s1_c0, ..., end_delta, end_col]
//
// - file: source file path
// - key_name: the YAML key whose value is this mapping
Expand All @@ -42,6 +42,10 @@ func addOriginInMap(key, n *Node, file string) *Node {
// - per field: name (string), line delta from key_line (int), column (int)
// - ns: number of sequence fields that have item locations
// - per sequence: name (string), item count (int), then count × (line delta, col)
// - end_delta, end_col: end of the whole mapping block — line delta from
// key_line and absolute column of the position just past its last content.
// Appended last so a consumer that stops after the sequences section
// simply ignores it (backward compatible).
func addOrigin(key, n *Node, file string) *Node {
if isOrigin(key) {
return n
Expand Down Expand Up @@ -108,6 +112,16 @@ func buildOriginSeq(key, n *Node, file string) []*Node {
nodes = append(nodes, fieldNodes...)
nodes = append(nodes, intNode(ns))
nodes = append(nodes, seqNodes...)

// Block end: line delta from key_line and absolute end column of the whole
// mapping. Lets a consumer reconstruct the full block span
// [key_line, key_line+end_delta] -- e.g. an entire endpoint operation block.
endDelta, endCol := 0, 0
if n.EndLine > 0 {
endDelta = n.EndLine - key.Line
endCol = n.EndColumn
}
nodes = append(nodes, intNode(endDelta), intNode(endCol))
return nodes
}

Expand Down
69 changes: 69 additions & 0 deletions origin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ __origin__:
- 0
- 1
- 0
- 3
- 17
root:
__origin__:
- file.yaml
Expand All @@ -82,6 +84,8 @@ root:
- 2
- 5
- 0
- 3
- 17
hello: world
object:
__origin__:
Expand All @@ -94,6 +98,8 @@ root:
- 1
- 9
- 0
- 1
- 17
foo: bar
`

Expand Down Expand Up @@ -132,6 +138,8 @@ __origin__:
- 0
- 1
- 0
- 5
- 19
root:
__origin__:
- file.yaml
Expand All @@ -143,6 +151,8 @@ root:
- 1
- 5
- 0
- 5
- 19
continents:
- __origin__:
- file.yaml
Expand All @@ -157,6 +167,8 @@ root:
- 1
- 11
- 0
- 1
- 19
name: europe
size: 10
- __origin__:
Expand All @@ -172,6 +184,8 @@ root:
- 1
- 11
- 0
- 1
- 19
name: america
size: 20
`
Expand Down Expand Up @@ -214,6 +228,8 @@ __origin__:
- 0
- 1
- 0
- 5
- 23
parent:
__origin__:
- spec.yaml
Expand All @@ -228,6 +244,8 @@ parent:
- 2
- 5
- 0
- 5
- 23
labels:
__origin__:
- spec.yaml
Expand All @@ -245,6 +263,8 @@ parent:
- 3
- 9
- 0
- 3
- 23
env: production
region: us-east
version: "2.0"
Expand Down Expand Up @@ -289,6 +309,8 @@ __origin__:
- 0
- 1
- 0
- 5
- 18
schema:
__origin__:
- spec.yaml
Expand All @@ -314,6 +336,8 @@ schema:
- integer
- 5
- 11
- 5
- 18
description: a test
type:
- string
Expand Down Expand Up @@ -471,6 +495,51 @@ alias: *schema
c.Assert(ns > 0, Equals, true, Commentf("alias __origin__ must record sequence item locations"))
}

// TestOrigin_BlockEnd verifies the trailing end_delta/end_col appended to each
// __origin__ sequence reconstruct the end of the whole block (the position just
// past its last content), which is how kin-openapi recovers an endpoint's span.
func (s *S) TestOrigin_BlockEnd(c *C) {
// 1: paths:
// 2: /pets:
// 3: get:
// 4: summary: list
// 5: x: "y"
// 6: /health:
input := `paths:
/pets:
get:
summary: list
x: "y"
/health:
get:
summary: ok
`
dec := yaml.NewDecoder(bytes.NewBufferString(input))
dec.Origin(true, "spec.yaml")
var out any
err := dec.Decode(&out)
c.Assert(err, IsNil)

get := out.(map[string]any)["paths"].(map[string]any)["/pets"].(map[string]any)["get"].(map[string]any)
seq, ok := get["__origin__"].([]any)
c.Assert(ok, Equals, true, Commentf("get block must carry __origin__"))

keyLine := toAnyInt(seq[2]) // header: file, key_name, key_line, key_col, ...
c.Assert(keyLine, Equals, 3, Commentf("get key is on line 3"))

// end_delta, end_col are the last two entries.
endDelta := toAnyInt(seq[len(seq)-2])
endCol := toAnyInt(seq[len(seq)-1])
endLine := keyLine + endDelta
// When a block is followed by a dedented sibling, the end lands on the
// block's last content line (x: "y" on line 5), inclusive. The key property
// for block extraction: it covers the whole get block and does not bleed
// into the /health sibling on line 6.
c.Assert(endLine, Equals, 5, Commentf("get block should end at its last content line (5), not bleed into the sibling; got %d", endLine))
// End column is just past the last content (`x: "y"` ends at col 12, so 13).
c.Assert(endCol, Equals, 13, Commentf("end column should be just past the last content; got %d", endCol))
}

func (s *S) TestOrigin_DuplicateKey(c *C) {
input := `
root:
Expand Down