Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Node range as [start, value-end, node-end] #259

Merged
merged 2 commits into from Apr 17, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 9 additions & 12 deletions docs/04_documents.md
Expand Up @@ -14,33 +14,30 @@ doc.contents
// YAMLMap {
// items:
// [ Pair {
// key: Scalar { value: 'YAML', range: [ 0, 4 ] },
// key: Scalar { value: 'YAML', range: [ 0, 4, 4 ] },
// value:
// YAMLSeq {
// items:
// [ Scalar {
// value: 'A human-readable data serialization language',
// range: [ 10, 55 ] },
// range: [ 10, 54, 55 ] },
// Scalar {
// value: 'https://en.wikipedia.org/wiki/YAML',
// range: [ 59, 94 ] } ],
// tag: 'tag:yaml.org,2002:seq',
// range: [ 8, 94 ] } },
// range: [ 59, 93, 94 ] } ],
// range: [ 8, 94, 94 ] } },
// Pair {
// key: Scalar { value: 'yaml', range: [ 94, 98 ] },
// key: Scalar { value: 'yaml', range: [ 94, 98, 98 ] },
// value:
// YAMLSeq {
// items:
// [ Scalar {
// value: 'A complete JavaScript implementation',
// range: [ 104, 141 ] },
// range: [ 104, 140, 141 ] },
// Scalar {
// value: 'https://www.npmjs.com/package/yaml',
// range: [ 145, 180 ] } ],
// tag: 'tag:yaml.org,2002:seq',
// range: [ 102, 180 ] } } ],
// tag: 'tag:yaml.org,2002:map',
// range: [ 0, 180 ] }
// range: [ 145, 180, 180 ] } ],
// range: [ 102, 180, 180 ] } } ],
// range: [ 0, 180, 180 ] }
```

#### `parseDocument(str, options = {}): Document`
Expand Down
6 changes: 3 additions & 3 deletions docs/05_content_nodes.md
Expand Up @@ -12,9 +12,9 @@ It is valid to have an anchor associated with a node even if it has no aliases.
class NodeBase {
comment?: string // a comment on or immediately after this
commentBefore?: string // a comment before this
range?: [number, number]
// the [start, end] range of characters of the source parsed
// into this node (undefined for pairs or if not parsed)
range?: [number, number, number]
// The [start, value-end, node-end] character offsets for the part
// of the source parsed into this node (undefined if not parsed).
spaceBefore?: boolean
// a blank line before this node and its commentBefore
tag?: string // a fully qualified tag, if required
Expand Down
2 changes: 1 addition & 1 deletion docs/07_parsing_yaml.md
Expand Up @@ -308,7 +308,7 @@ const item = doc.value.items[0].value
}

YAML.resolveAsScalar(item)
> { value: 'bar', type: 'QUOTE_DOUBLE', comment: 'comment', length: 14 }
> { value: 'bar', type: 'QUOTE_DOUBLE', comment: 'comment', range: [5, 9, 19] }
```

#### `CST.isCollection(token?: Token): boolean`
Expand Down
5 changes: 3 additions & 2 deletions src/compose/compose-doc.ts
Expand Up @@ -52,8 +52,9 @@ export function composeDoc(
? composeNode(ctx, value, props, onError)
: composeEmptyNode(ctx, props.end, start, null, props, onError)

const re = resolveEnd(end, doc.contents.range[1], false, onError)
const contentEnd = doc.contents.range[2]
const re = resolveEnd(end, contentEnd, false, onError)
if (re.comment) doc.comment = re.comment
doc.range = [offset, re.offset]
doc.range = [offset, contentEnd, re.offset]
return doc
}
5 changes: 3 additions & 2 deletions src/compose/compose-node.ts
Expand Up @@ -102,8 +102,9 @@ function composeAlias(
onError: ComposeErrorHandler
) {
const alias = new Alias(source.substring(1))
const re = resolveEnd(end, offset + source.length, options.strict, onError)
alias.range = [offset, re.offset]
const valueEnd = offset + source.length
const re = resolveEnd(end, valueEnd, options.strict, onError)
alias.range = [offset, valueEnd, re.offset]
if (re.comment) alias.comment = re.comment
return alias as Alias.Parsed
}
9 changes: 4 additions & 5 deletions src/compose/compose-scalar.ts
Expand Up @@ -14,8 +14,7 @@ export function composeScalar(
tagName: string | null,
onError: ComposeErrorHandler
) {
const { offset } = token
const { value, type, comment, length } =
const { value, type, comment, range } =
token.type === 'block-scalar'
? resolveBlockScalar(token, ctx.options.strict, onError)
: resolveFlowScalar(token, ctx.options.strict, onError)
Expand All @@ -28,15 +27,15 @@ export function composeScalar(
try {
const res = tag.resolve(
value,
msg => onError(offset, 'TAG_RESOLVE_FAILED', msg),
msg => onError(token.offset, 'TAG_RESOLVE_FAILED', msg),
ctx.options
)
scalar = isScalar(res) ? res : new Scalar(res)
} catch (error) {
onError(offset, 'TAG_RESOLVE_FAILED', error.message)
onError(token.offset, 'TAG_RESOLVE_FAILED', error.message)
scalar = new Scalar(value)
}
scalar.range = [offset, offset + length]
scalar.range = range
scalar.source = value
if (type) scalar.type = type
if (tagName) scalar.tag = tagName
Expand Down
4 changes: 2 additions & 2 deletions src/compose/composer.ts
Expand Up @@ -206,7 +206,7 @@ export class Composer {
const dc = this.doc.comment
this.doc.comment = dc ? `${dc}\n${end.comment}` : end.comment
}
this.doc.range[1] = end.offset
this.doc.range[2] = end.offset
break
}
default:
Expand Down Expand Up @@ -240,7 +240,7 @@ export class Composer {
'MISSING_CHAR',
'Missing directives-end indicator line'
)
doc.range = [0, endOffset]
doc.range = [0, endOffset, endOffset]
this.decorate(doc, false)
yield doc
}
Expand Down
6 changes: 3 additions & 3 deletions src/compose/resolve-block-map.ts
Expand Up @@ -65,7 +65,7 @@ export function resolveBlockMap(
const valueProps = resolveProps(sep || [], {
ctx,
indicator: 'map-value-ind',
offset: keyNode.range[1],
offset: keyNode.range[2],
onError,
startOnNewline: !key || key.type === 'block-scalar'
})
Expand Down Expand Up @@ -93,7 +93,7 @@ export function resolveBlockMap(
const valueNode = value
? composeNode(ctx, value, valueProps, onError)
: composeEmptyNode(ctx, offset, sep, null, valueProps, onError)
offset = valueNode.range[1]
offset = valueNode.range[2]
map.items.push(new Pair(keyNode, valueNode))
} else {
// key with no value
Expand All @@ -111,6 +111,6 @@ export function resolveBlockMap(
}
}

map.range = [bm.offset, offset]
map.range = [bm.offset, offset, offset]
return map as YAMLMap.Parsed
}
21 changes: 10 additions & 11 deletions src/compose/resolve-block-scalar.ts
@@ -1,3 +1,4 @@
import { Range } from '../nodes/Node.js'
import { Scalar } from '../nodes/Scalar.js'
import type { BlockScalar } from '../parse/cst.js'
import type { ComposeErrorHandler } from './composer.js'
Expand All @@ -10,10 +11,12 @@ export function resolveBlockScalar(
value: string
type: Scalar.BLOCK_FOLDED | Scalar.BLOCK_LITERAL | null
comment: string
length: number
range: Range
} {
const start = scalar.offset
const header = parseBlockScalarHeader(scalar, strict, onError)
if (!header) return { value: '', type: null, comment: '', length: 0 }
if (!header)
return { value: '', type: null, comment: '', range: [start, start, start] }
const type = header.mode === '>' ? Scalar.BLOCK_FOLDED : Scalar.BLOCK_LITERAL
const lines = scalar.source ? splitLines(scalar.source) : []

Expand All @@ -29,9 +32,9 @@ export function resolveBlockScalar(
if (!scalar.source || chompStart === 0) {
const value =
header.chomp === '+' ? lines.map(line => line[0]).join('\n') : ''
let length = header.length
if (scalar.source) length += scalar.source.length
return { value, type, comment: header.comment, length }
let end = start + header.length
if (scalar.source) end += scalar.source.length
return { value, type, comment: header.comment, range: [start, end, end] }
}

// find the indentation level to trim from start
Expand Down Expand Up @@ -113,12 +116,8 @@ export function resolveBlockScalar(
value += '\n'
}

return {
value,
type,
comment: header.comment,
length: header.length + scalar.source.length
}
const end = start + header.length + scalar.source.length
return { value, type, comment: header.comment, range: [start, end, end] }
}

function parseBlockScalarHeader(
Expand Down
4 changes: 2 additions & 2 deletions src/compose/resolve-block-seq.ts
Expand Up @@ -40,9 +40,9 @@ export function resolveBlockSeq(
const node = value
? composeNode(ctx, value, props, onError)
: composeEmptyNode(ctx, offset, start, null, props, onError)
offset = node.range[1]
offset = node.range[2]
seq.items.push(node)
}
seq.range = [bs.offset, offset]
seq.range = [bs.offset, offset, offset]
return seq as YAMLSeq.Parsed
}
19 changes: 11 additions & 8 deletions src/compose/resolve-flow-collection.ts
Expand Up @@ -113,7 +113,7 @@ export function resolveFlowCollection(
? composeNode(ctx, value, props, onError)
: composeEmptyNode(ctx, props.end, sep, null, props, onError)
;(coll as YAMLSeq).items.push(valueNode)
offset = valueNode.range[1]
offset = valueNode.range[2]
} else {
// item is a key+value pair

Expand All @@ -128,7 +128,7 @@ export function resolveFlowCollection(
ctx,
flow: fcName,
indicator: 'map-value-ind',
offset: keyNode.range[1],
offset: keyNode.range[2],
onError,
startOnNewline: false
})
Expand Down Expand Up @@ -188,29 +188,32 @@ export function resolveFlowCollection(
map.items.push(pair)
;(coll as YAMLSeq).items.push(map)
}
offset = valueNode ? valueNode.range[1] : valueProps.end
offset = valueNode ? valueNode.range[2] : valueProps.end
}
}

const expectedEnd = isMap ? '}' : ']'
const [ce, ...ee] = fc.end
if (!ce || ce.source !== expectedEnd) {
let cePos = offset
if (ce && ce.source === expectedEnd) cePos += ce.source.length
else {
onError(
offset + 1,
'MISSING_CHAR',
`Expected ${fcName} to end with ${expectedEnd}`
)
if (ce && ce.source.length !== 1) ee.unshift(ce)
}
if (ce) offset += ce.source.length
if (ee.length > 0) {
const end = resolveEnd(ee, offset, ctx.options.strict, onError)
const end = resolveEnd(ee, cePos, ctx.options.strict, onError)
if (end.comment) {
if (coll.comment) coll.comment += '\n' + end.comment
else coll.comment = end.comment
}
offset = end.offset
coll.range = [fc.offset, cePos, end.offset]
} else {
coll.range = [fc.offset, cePos, cePos]
}

coll.range = [fc.offset, offset]
return coll as YAMLMap.Parsed | YAMLSeq.Parsed
}
7 changes: 4 additions & 3 deletions src/compose/resolve-flow-scalar.ts
@@ -1,3 +1,4 @@
import { Range } from '../nodes/Node.js'
import { Scalar } from '../nodes/Scalar.js'
import type { FlowScalar } from '../parse/cst.js'
import type { ComposeErrorHandler } from './composer.js'
Expand All @@ -11,7 +12,7 @@ export function resolveFlowScalar(
value: string
type: Scalar.PLAIN | Scalar.QUOTE_DOUBLE | Scalar.QUOTE_SINGLE | null
comment: string
length: number
range: Range
} {
let _type: Scalar.PLAIN | Scalar.QUOTE_DOUBLE | Scalar.QUOTE_SINGLE
let value: string
Expand Down Expand Up @@ -44,7 +45,7 @@ export function resolveFlowScalar(
value: '',
type: null,
comment: '',
length: source.length
range: [offset, offset + source.length, offset + source.length]
}
}

Expand All @@ -53,7 +54,7 @@ export function resolveFlowScalar(
value,
type: _type,
comment: re.comment,
length: source.length + re.offset
range: [offset, offset + source.length, offset + source.length + re.offset]
}
}

Expand Down
13 changes: 9 additions & 4 deletions src/doc/Document.ts
Expand Up @@ -7,7 +7,8 @@ import {
isScalar,
Node,
NODE_TYPE,
ParsedNode
ParsedNode,
Range
} from '../nodes/Node.js'
import { Pair } from '../nodes/Pair.js'
import type { Scalar } from '../nodes/Scalar.js'
Expand Down Expand Up @@ -35,9 +36,7 @@ export type Replacer = any[] | ((key: any, value: any) => unknown)

export declare namespace Document {
interface Parsed<T extends ParsedNode = ParsedNode> extends Document<T> {
range: [number, number]
/** The schema used with the document. */
schema: Schema
range: Range
}
}

Expand Down Expand Up @@ -65,6 +64,12 @@ export class Document<T = unknown> {
>
>

/**
* The [start, value-end, node-end] character offsets for the part of the
* source parsed into this document (undefined if not parsed).
*/
declare range?: Range

// TS can't figure out that setSchema() will set this, or throw
/** The schema used with the document. Use `setSchema()` to change. */
declare schema: Schema
Expand Down
12 changes: 10 additions & 2 deletions src/nodes/Alias.ts
Expand Up @@ -2,15 +2,23 @@ import { anchorIsValid } from '../doc/anchors'
import type { Document } from '../doc/Document'
import type { StringifyContext } from '../stringify/stringify.js'
import { visit } from '../visit'
import { ALIAS, isAlias, isCollection, isPair, Node, NodeBase } from './Node.js'
import {
ALIAS,
isAlias,
isCollection,
isPair,
Node,
NodeBase,
Range
} from './Node.js'
import type { Scalar } from './Scalar'
import type { ToJSContext } from './toJS.js'
import type { YAMLMap } from './YAMLMap'
import type { YAMLSeq } from './YAMLSeq'

export declare namespace Alias {
interface Parsed extends Alias {
range: [number, number]
range: Range
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/nodes/Node.ts
Expand Up @@ -14,6 +14,8 @@ export type ParsedNode =
| YAMLMap.Parsed
| YAMLSeq.Parsed

export type Range = [number, number, number]

export const ALIAS = Symbol.for('yaml.alias')
export const DOC = Symbol.for('yaml.document')
export const MAP = Symbol.for('yaml.map')
Expand Down Expand Up @@ -75,10 +77,10 @@ export abstract class NodeBase {
declare commentBefore?: string | null

/**
* The [start, end] range of characters of the source parsed
* into this node (undefined for pairs or if not parsed)
* The [start, value-end, node-end] character offsets for the part of the
* source parsed into this node (undefined if not parsed).
*/
declare range?: [number, number] | null
declare range?: Range | null

/** A blank line before this node and its commentBefore */
declare spaceBefore?: boolean
Expand Down